Compare commits

..

No commits in common. "1d358a9ea9fcc8168a07ea07f144038da6f387c7" and "dc71938bd888613877d38b9e6499789af533e317" have entirely different histories.

12 changed files with 81 additions and 174 deletions

BIN
bun.lockb Executable file → Normal file

Binary file not shown.

View file

@ -1,6 +0,0 @@
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
create_schema:
POSTGRES_URL="postgres://test:test@192.168.50.14:5432/posts" bun run ./tools/create_schema.ts

View file

@ -9,7 +9,6 @@
"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",

View file

@ -1,15 +0,0 @@
import { insert_post, NewPost } from "@/db";
import { Blob } from "buffer";
import type { NextRequest } from "next/server";
export async function POST(req: NextRequest) {
let form = await req.formData();
let text = form.get("content")?.toString()
if (!text) {
return Response.error()
}
let request: NewPost = { content: text, image: [] };
let [id, hash] = await insert_post(request);
return Response.json({ id: id, hash: hash })
}

View file

@ -1,6 +0,0 @@
import { NextRequest } from "next/server";
export function Post(req: NextRequest) {
}

View file

@ -1,3 +1,4 @@
import { retrieve_post } from "@/db";
export default function Home() { export default function Home() {
@ -7,7 +8,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>
); );

View file

@ -1,35 +1,35 @@
import { Suspense } from "react"; import { Suspense } from "react";
import Image from "next/image"; import Image from "next/image";
import { Attachment, MultiMediaType } from "@/db"; import { Attachment } 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 == MultiMediaType.video) { if (attachment.type.type == "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 == MultiMediaType.image) { }else if (attachment.type.type="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>)
} }

130
src/db.ts
View file

@ -1,93 +1,57 @@
import { env, ReservedSQL, CryptoHasher, sql } from 'bun' import { main } from "bun";
import { MIMEType } from 'util'; import { sql } from "bun";
import { MIMEType } from "util"
export enum MultiMediaType {
video, image
}
export interface Attachment { export interface Attachment {
urls: string[], type: MIMEType;
type: MultiMediaType urls: string[];
}; }
export interface Post { export interface Post {
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 interface NewPost {
image: Blob[]
content: string, content: string,
hash: string,
} }
export async function insert_post(post: NewPost): Promise<[number, string]> { export async function setup_func() {
let post_hash = new CryptoHasher("sha256"); const rows = await sql`
post_hash.update(post.content); CREATE OR REPLACE FUNCTION fetch_post(
post_hash.update(Date.now().toString()) OUT p_text VARCHAR(1000),
let populated = post_hash.digest("base64"); OUT p_hash CHAR(32),
let [{ id, hash }] = await sql`INSERT INTO niming.posts (post, hash) VALUES (${post.content}, ${populated}) RETURNING id, hash` OUT p_date TIMESTAMP,
return [id, hash] 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 async function init_db() { /* retrieve the latest post with posts table */
await sql.begin(async sql => { export async function retrieve_post(offset: Number) {
await sql`CREATE SCHEMA niming` const res = await sql`SELECT * FROM fetch_post();`
await sql`CREATE TABLE niming.posts (id SERIAL PRIMARY KEY, hash char(44) UNIQUE, post VARCHAR(500), post_time TIMESTAMPTZ DEFAULT now())` console.log(res)
await sql`CREATE TABLE niming.images (id INTEGER PRIMARY KEY REFERENCES niming.posts (id), fileid integer[] )`
})
} }
setup_func()
export class PostFetcher { retrieve_post(0)
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.fileid
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)
})
}
}

View file

@ -1,3 +0,0 @@
import { init_db } from "@/db";
init_db()

View file

@ -1,13 +0,0 @@
import { insert_post, NewPost } from '@/db';
import { faker } from '@faker-js/faker';
for (let i = 0; i < 100; i++) {
let text = faker.string.alpha(20);
let x: NewPost = { image: [], content: text };
let [p_id, p_hash] = await insert_post(x)
console.log(p_id, p_hash)
}

View file

@ -1,13 +0,0 @@
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)
}

View file

@ -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,8 +20,7 @@
], ],
"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"]