postgres_cursor add
This commit is contained in:
parent
dc71938bd8
commit
0745298257
9 changed files with 131 additions and 82 deletions
BIN
bun.lockb
Normal file → Executable file
BIN
bun.lockb
Normal file → Executable file
Binary file not shown.
4
justfile
Normal file
4
justfile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
fake:
|
||||||
|
POSTGRES_URL="postgres://test:test@192.168.50.14:5432/posts" bun run ./tools/gen_fake.ts
|
||||||
|
fetch_post:
|
||||||
|
POSTGRES_URL="postgres://test:test@192.168.50.14:5432/posts" bun run ./tools/post_db.ts
|
|
@ -9,6 +9,7 @@
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@faker-js/faker": "^9.7.0",
|
||||||
"bun-types": "^1.2.9",
|
"bun-types": "^1.2.9",
|
||||||
"next": "15.3.0",
|
"next": "15.3.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { retrieve_post } from "@/db";
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|
||||||
|
@ -8,7 +7,7 @@ export default function Home() {
|
||||||
<h1 className="text-4xl mt-4 ml-3">匿名中工</h1>
|
<h1 className="text-4xl mt-4 ml-3">匿名中工</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-[90dvh] w-[85dvw] absolute left-1/2 top-1/2 mt-5 -translate-1/2">
|
<div className="h-[90dvh] w-[85dvw] absolute left-1/2 top-1/2 mt-5 -translate-1/2">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
|
|
||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Attachment } from "@/db";
|
import { Attachment, MultiMediaType } from "@/db";
|
||||||
|
|
||||||
export default function Post(post_content: string, attachments: Attachment[]) {
|
export default function Post(post_content: string, attachments: Attachment[]) {
|
||||||
let images = [];
|
let images = [];
|
||||||
let videos = [];
|
let videos = [];
|
||||||
attachments.forEach(attachment => {
|
attachments.forEach(attachment => {
|
||||||
if (attachment.type.type == "video") {
|
if (attachment.type == MultiMediaType.video) {
|
||||||
attachment.urls.forEach(url => {
|
attachment.urls.forEach(url => {
|
||||||
videos.push(
|
videos.push(
|
||||||
<Suspense fallback={<p>加載中</p>}>
|
<Suspense fallback={<p>加載中</p>}>
|
||||||
<video controls preload="none" aria-label="Video player">
|
<video controls preload="none" aria-label="Video player">
|
||||||
<source src={url} type={attachment.type.toString()} />
|
<source src={url} type={attachment.type.toString()} />
|
||||||
Your browser does not support the video tag.
|
Your browser does not support the video tag.
|
||||||
</video>
|
</video>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}else if (attachment.type.type="image"){
|
} else if (attachment.type == MultiMediaType.image) {
|
||||||
attachment.urls.forEach(url => {
|
attachment.urls.forEach(url => {
|
||||||
images.push(<Image src={url} alt="Uploaded" width={300} height={200}></Image>)
|
images.push(<Image src={url} alt="Uploaded" width={300} height={200}></Image>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return (<div className="w-full h-fit">
|
return (<div className="w-full h-fit">
|
||||||
<div>
|
<div>
|
||||||
{post_content}
|
{post_content}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
112
src/db.ts
112
src/db.ts
|
@ -1,57 +1,73 @@
|
||||||
import { main } from "bun";
|
import { env, ReservedSQL, sql } from 'bun'
|
||||||
import { sql } from "bun";
|
import { MIMEType } from 'util';
|
||||||
import { MIMEType } from "util"
|
|
||||||
|
export enum MultiMediaType {
|
||||||
|
video, image
|
||||||
|
}
|
||||||
|
|
||||||
export interface Attachment {
|
export interface Attachment {
|
||||||
type: MIMEType;
|
urls: string[],
|
||||||
urls: string[];
|
type: MultiMediaType
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface Post {
|
export interface Post {
|
||||||
content: string,
|
post: string,
|
||||||
|
post_time: string,
|
||||||
hash: string,
|
hash: string,
|
||||||
|
attachments: Attachment[],
|
||||||
|
}
|
||||||
|
interface SQLPostCast {
|
||||||
|
hash: string, post: string, post_time: string, images: number[]
|
||||||
|
};
|
||||||
|
const SQLPostCast2Post = (obj: SQLPostCast) => {
|
||||||
|
let x: Post =
|
||||||
|
{
|
||||||
|
post: obj.post,
|
||||||
|
hash: obj.hash,
|
||||||
|
post_time: obj.post_time,
|
||||||
|
attachments: []
|
||||||
|
};
|
||||||
|
if (obj.images && obj.images.length > 0) {
|
||||||
|
x.attachments.push(
|
||||||
|
{
|
||||||
|
type: MultiMediaType.image, urls: obj.images.map(img => {
|
||||||
|
return `/img/${obj.hash}_${img}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setup_func() {
|
export interface Cursor {
|
||||||
const rows = await sql`
|
hash: string,
|
||||||
CREATE OR REPLACE FUNCTION fetch_post(
|
post_time: string,
|
||||||
OUT p_text VARCHAR(1000),
|
|
||||||
OUT p_hash CHAR(32),
|
|
||||||
OUT p_date TIMESTAMP,
|
|
||||||
OUT p_pics INT[]
|
|
||||||
)
|
|
||||||
RETURNS SETOF RECORD AS
|
|
||||||
$$
|
|
||||||
DECLARE
|
|
||||||
post_cursor CURSOR FOR
|
|
||||||
SELECT content, hash, date
|
|
||||||
FROM posts;
|
|
||||||
post_record RECORD;
|
|
||||||
BEGIN
|
|
||||||
-- Open cursor
|
|
||||||
OPEN post_cursor;
|
|
||||||
|
|
||||||
-- Fetch rows and return
|
|
||||||
LOOP
|
|
||||||
FETCH NEXT FROM post_cursor INTO post_record;
|
|
||||||
EXIT WHEN NOT FOUND;
|
|
||||||
|
|
||||||
p_text = post_record.content;
|
|
||||||
p_date = post_record.date;
|
|
||||||
p_hash = post_record.hash;
|
|
||||||
RETURN NEXT;
|
|
||||||
END LOOP;
|
|
||||||
|
|
||||||
-- Close cursor
|
|
||||||
CLOSE post_cursor;
|
|
||||||
END;
|
|
||||||
$$
|
|
||||||
LANGUAGE PLPGSQL;`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* retrieve the latest post with posts table */
|
|
||||||
export async function retrieve_post(offset: Number) {
|
export class PostFetcher {
|
||||||
const res = await sql`SELECT * FROM fetch_post();`
|
conn: Promise<ReservedSQL>;
|
||||||
console.log(res)
|
constructor() {
|
||||||
|
this.conn = sql.reserve();
|
||||||
|
}
|
||||||
|
async init() {
|
||||||
|
await this.conn.then(async e => {
|
||||||
|
await e`DECLARE post_ptr CURSOR WITH HOLD FOR SELECT niming.posts.*, niming.images.file_sequence
|
||||||
|
AS images FROM niming.posts
|
||||||
|
LEFT JOIN niming.images ON niming.images.hash=niming.posts.hash ORDER BY niming.posts.post_time DESC, niming.posts.hash ASC`
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async postgres_fetch() {
|
||||||
|
let x: SQLPostCast[] = []
|
||||||
|
if (this.conn) {
|
||||||
|
x = await (await this.conn)`FETCH 10 IN post_ptr`;
|
||||||
|
}
|
||||||
|
return x.map(e => {
|
||||||
|
return SQLPostCast2Post(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setup_func()
|
|
||||||
retrieve_post(0)
|
|
||||||
|
|
15
tools/gen_fake.ts
Normal file
15
tools/gen_fake.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { CryptoHasher, sql } from "bun";
|
||||||
|
import { retrieve_post, setup_func } from "@/db";
|
||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
|
||||||
|
let text = faker.string.alpha(20);
|
||||||
|
let hash = new CryptoHasher("sha256")
|
||||||
|
hash.update(text)
|
||||||
|
hash.update(Date.now().toString())
|
||||||
|
let dg = hash.digest("hex")
|
||||||
|
console.log(dg)
|
||||||
|
const _ = await sql`INSERT INTO niming.posts (hash, post,post_time) VALUES (${dg},${text}, now())`
|
||||||
|
}
|
||||||
|
|
13
tools/post_db.ts
Normal file
13
tools/post_db.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { PostFetcher, Post } from "@/db"
|
||||||
|
import { sql } from "bun";
|
||||||
|
|
||||||
|
let x: Post[] = [];
|
||||||
|
const fetcher = new PostFetcher();
|
||||||
|
await fetcher.init()
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
x = await fetcher.postgres_fetch()
|
||||||
|
|
||||||
|
console.log(x)
|
||||||
|
|
||||||
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "Bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
|
@ -20,7 +20,8 @@
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
},
|
||||||
|
"types":["bun-types"]
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
|
Loading…
Add table
Reference in a new issue