Personal no-bs blogging platform.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157import * as schema from './schema/schema.ts';import { and, eq, inArray } from 'drizzle-orm';
import { db } from "./db.ts";import type { Post } from '@/lib/types.ts';import { BlogNotFoundError, ForbiddenError, PostNotFoundError, UnauthorizedError } from '@/lib/errors.ts';
/** * Create a post. * @param {Post} post Post object. Without ID. * @returns {Promise<Post>} The post. */export async function create_post(post: Post): Promise<Post> { if (post.id) throw new Error("Something's wrong with your post. Do not pass its ID."); const [result] = await db.insert(schema.posts).values(post).returning(); return result as Post;}
/** * Updates a post. Errors with `PostNotFoundError` if the ID is invalid. * @param {Post} post The Post object. * @returns {Promise<Post>} Returns the post. */export async function update_post(post: Post): Promise<Post> { if (!post.id) throw new PostNotFoundError(); const [result] = await db.update(schema.posts).set(post).where(eq(schema.posts.id, post.id)).returning(); if (!result) throw new PostNotFoundError(); return result as Post;}
/** * Updates the content of a post. Verifies the user owns it. Might error with `PostNotFoundError`. No need to pass the `updated_at` field. * @param {number} user_id User's ID. * @param {string} post_uuid Post's UUID. * @param {Partial<Post>} data The post itself. * @returns {Promise<Post>} Post. */export async function update_post_check(user_id: number, post_uuid: string, data: Partial<Post>): Promise<Post> { data.updated_at = new Date(); const [existing] = await db .select({ post_uuid: schema.posts.uuid, owner_id: schema.blogs.user_id }) .from(schema.posts) .innerJoin(schema.blogs, eq(schema.posts.blog_id, schema.blogs.id)) .where(eq(schema.posts.uuid, post_uuid));
if (!existing) throw new PostNotFoundError(); if (existing.owner_id !== user_id) throw new ForbiddenError();
const [result] = await db.update(schema.posts) .set({ ...data, updated_at: new Date() }) .from(schema.blogs) .where( and( eq(schema.posts.uuid, post_uuid), eq(schema.posts.blog_id, schema.blogs.id), eq(schema.blogs.user_id, user_id) ) ) .returning();
if (!result) throw new PostNotFoundError(); return result;}
/** * Sets the `published_at` column to current date. Errors with `PostNotFoundError` if the ID is invalid. * @param post_id * @returns {Promise<Post>} */export async function publish_post(post_id: number): Promise<Post> { const [result] = await db.update(schema.posts).set({ published_at: new Date() }).where(eq(schema.posts.id, post_id)).returning(); if (!result) throw new PostNotFoundError(); return result;}
/** * Given an array of IDs, retrieves all the posts. Might return no posts. * @param post_ids Array with Post IDs * @returns {Post[]} Array of Post(s). */export async function get_posts(post_ids: number[]): Promise<Post[]> { const results = await db.select().from(schema.posts).where(inArray(schema.posts.id, post_ids)); return results;}
/** * Retrieves a post by ID. Can receive either its ID or UUID. Errors with `PostNotFoundError` if the ID is invalid. * @param {number | string} post ID or UUID. Either work. * @returns {Promise<Post>} Banana. */export async function get_post(post: number | string): Promise<Post> { let post_attr = (typeof post === "string") ? schema.posts.uuid : schema.posts.id; const [result] = await db.select().from(schema.posts).where(eq(post_attr, post)); if (!result) throw new PostNotFoundError(); return result;}
/** * Verifies a user owns a post. Errors with `PostNotFoundError` if not. * @param {number} user_id User's ID. * @param {number | string} post Post's ID or UUID. * @returns {Promise<Post>} Post. */export async function get_post_check(user_id: number, post: number | string): Promise<Post> { let post_attr = (typeof post === "string") ? schema.posts.uuid : schema.posts.id; const [result] = await db .select({ post: schema.posts }) .from(schema.posts) .innerJoin(schema.blogs, eq(schema.posts.blog_id, schema.blogs.id)) .where( and( eq(post_attr, post), eq(schema.blogs.user_id, user_id) ) ); if (!result) throw new PostNotFoundError(); return result.post;}
export async function delete_post_check(user_id: number, post: string | number): Promise<true> { let post_attr = (typeof post === "string") ? schema.posts.uuid : schema.posts.id;
const blog = await db.query.blogs.findFirst({ where: { user_id }, with: { posts: { where: { ...(typeof post === "string" && { uuid: post }), ...(typeof post === "number" && { id: post }) } } }, })
const blog_id = blog?.posts[0]?.blog_id; if (!blog_id) throw new PostNotFoundError();
const [result] = await db .delete(schema.posts) .where( and( eq(post_attr, post), eq(schema.posts.blog_id, blog_id) ) ) .returning();
if (!result) throw new PostNotFoundError(); return true;}