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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^9.7.0",
|
||||
"bun-types": "^1.2.9",
|
||||
"next": "15.3.0",
|
||||
"react": "^19.0.0",
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { retrieve_post } from "@/db";
|
||||
|
||||
export default function Home() {
|
||||
|
||||
|
@ -8,7 +7,7 @@ export default function Home() {
|
|||
<h1 className="text-4xl mt-4 ml-3">匿名中工</h1>
|
||||
</div>
|
||||
<div className="h-[90dvh] w-[85dvw] absolute left-1/2 top-1/2 mt-5 -translate-1/2">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
|
||||
import { Suspense } from "react";
|
||||
import Image from "next/image";
|
||||
import { Attachment } from "@/db";
|
||||
import { Attachment, MultiMediaType } from "@/db";
|
||||
|
||||
export default function Post(post_content: string, attachments: Attachment[]) {
|
||||
let images = [];
|
||||
let videos = [];
|
||||
attachments.forEach(attachment => {
|
||||
if (attachment.type.type == "video") {
|
||||
attachment.urls.forEach(url => {
|
||||
videos.push(
|
||||
<Suspense fallback={<p>加載中</p>}>
|
||||
<video controls preload="none" aria-label="Video player">
|
||||
<source src={url} type={attachment.type.toString()} />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</Suspense>
|
||||
)
|
||||
})
|
||||
}else if (attachment.type.type="image"){
|
||||
attachment.urls.forEach(url => {
|
||||
images.push(<Image src={url} alt="Uploaded" width={300} height={200}></Image>)
|
||||
})
|
||||
}
|
||||
})
|
||||
return (<div className="w-full h-fit">
|
||||
<div>
|
||||
{post_content}
|
||||
</div>
|
||||
<div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
let images = [];
|
||||
let videos = [];
|
||||
attachments.forEach(attachment => {
|
||||
if (attachment.type == MultiMediaType.video) {
|
||||
attachment.urls.forEach(url => {
|
||||
videos.push(
|
||||
<Suspense fallback={<p>加載中</p>}>
|
||||
<video controls preload="none" aria-label="Video player">
|
||||
<source src={url} type={attachment.type.toString()} />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</Suspense>
|
||||
)
|
||||
})
|
||||
} else if (attachment.type == MultiMediaType.image) {
|
||||
attachment.urls.forEach(url => {
|
||||
images.push(<Image src={url} alt="Uploaded" width={300} height={200}></Image>)
|
||||
})
|
||||
}
|
||||
})
|
||||
return (<div className="w-full h-fit">
|
||||
<div>
|
||||
{post_content}
|
||||
</div>
|
||||
<div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
|
112
src/db.ts
112
src/db.ts
|
@ -1,57 +1,73 @@
|
|||
import { main } from "bun";
|
||||
import { sql } from "bun";
|
||||
import { MIMEType } from "util"
|
||||
import { env, ReservedSQL, sql } from 'bun'
|
||||
import { MIMEType } from 'util';
|
||||
|
||||
export enum MultiMediaType {
|
||||
video, image
|
||||
}
|
||||
|
||||
export interface Attachment {
|
||||
type: MIMEType;
|
||||
urls: string[];
|
||||
}
|
||||
urls: string[],
|
||||
type: MultiMediaType
|
||||
};
|
||||
|
||||
export interface Post {
|
||||
content: string,
|
||||
post: string,
|
||||
post_time: 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() {
|
||||
const rows = await sql`
|
||||
CREATE OR REPLACE FUNCTION fetch_post(
|
||||
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;`;
|
||||
export interface Cursor {
|
||||
hash: string,
|
||||
post_time: string,
|
||||
}
|
||||
|
||||
/* retrieve the latest post with posts table */
|
||||
export async function retrieve_post(offset: Number) {
|
||||
const res = await sql`SELECT * FROM fetch_post();`
|
||||
console.log(res)
|
||||
|
||||
export class PostFetcher {
|
||||
conn: Promise<ReservedSQL>;
|
||||
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,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleResolution": "Bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
|
@ -20,7 +20,8 @@
|
|||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"types":["bun-types"]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
|
|
Loading…
Add table
Reference in a new issue