From 156e2ac57c57400aed6c2bb489cb5dc93cc86dd9 Mon Sep 17 00:00:00 2001 From: Andrei Jiroh Halili Date: Tue, 16 Jul 2024 16:29:24 +0800 Subject: [PATCH] feat(golinks): implement redirects and DB access now Also update and add some scripts btw. Signed-off-by: Andrei Jiroh Halili --- apps/golinks-v2/package.json | 8 ++- apps/golinks-v2/src/api/golinks.ts | 49 ++++++++------- apps/golinks-v2/src/index.ts | 28 ++++++++- apps/golinks-v2/src/lib/db.ts | 72 +++++++++++++++++++++++ apps/golinks-v2/worker-configuration.d.ts | 2 +- apps/prisma/schema.prisma | 14 ++++- 6 files changed, 146 insertions(+), 27 deletions(-) create mode 100644 apps/golinks-v2/src/lib/db.ts diff --git a/apps/golinks-v2/package.json b/apps/golinks-v2/package.json index 1eeed90..b4cc9e9 100644 --- a/apps/golinks-v2/package.json +++ b/apps/golinks-v2/package.json @@ -6,12 +6,14 @@ "deploy": "wrangler deploy", "dev": "wrangler dev", "start": "wrangler dev", - "types:gen": "wrangler types", + "types:gen": "wrangler types && prisma generate", + "migrations:new": "wrangler d1 migrations create golinks-db", + "migrations:diff": "prisma migrate diff --from-schema-datamodel ./prisma/schema.prisma --to-local-d1", "migrations:apply": "wrangler d1 migrations apply golinks-db --local", "migrations:apply-dev": "wrangler d1 migrations apply golinks-db --local", "migrations:apply-prod": "wrangler d1 migrations apply golinks-db --remote", - "exec:dev": "wrangler d1 execute golinks-db --local", - "exec:prod": "wrangler d1 execute golinks-db --remote" + "d1-exec:dev": "wrangler d1 execute golinks-db --local", + "d1-exec:prod": "wrangler d1 execute golinks-db --remote" }, "dependencies": { "@prisma/adapter-d1": "^5.16.2", diff --git a/apps/golinks-v2/src/api/golinks.ts b/apps/golinks-v2/src/api/golinks.ts index 3dff331..6b388d7 100644 --- a/apps/golinks-v2/src/api/golinks.ts +++ b/apps/golinks-v2/src/api/golinks.ts @@ -1,41 +1,49 @@ -import { OpenAPIRoute, Num, Bool } from "chanfana" +import { OpenAPIRoute, Num, Bool, Str } from "chanfana" import { z } from "zod" import { GoLinks } from "types" -import { getGoLinks } from "lib/db"; +import { addGoLink, generateSlug, getGoLinks } from "lib/db"; export class GoLinkCreate extends OpenAPIRoute { schema = { - tags: ["golinks"], - summary: "Create a golink", + + tags: ['golinks'], + summary: 'Create a golink', request: { body: { content: { - "application/json": { - schema: GoLinks - } - } - } + 'application/json': { + schema: z.object({ + slug: Str({ required: false }), + targetUrl: Str({ example: 'https://example.com' }), + is_active: z.boolean().default(true), + }), + }, + }, + }, }, security: [ { - adminApiKey: [] - } + adminApiKey: [], + }, ], responses: { - "200": { - description: "Returns the generated golink" - } - } - } + '200': { + description: 'Returns the generated golink', + }, + }, + }; async handle(c) { const data = await this.getValidatedData(); - const linkToCreate = data.body + const linkToCreate = data.body; + const slug = data.body.slug !== null ? data.body.slug : generateSlug(12); + + const result = await addGoLink(c.env.golinks, slug, linkToCreate.targetUrl, linkToCreate.is_active); return { success: true, - data: linkToCreate - } + data: linkToCreate, + }; } } @@ -48,6 +56,7 @@ export class GoLinkList extends OpenAPIRoute { page: Num({ description: "Page number", default: 0, + required: false }), isActive: Bool({ description: "Filter by is_active status", @@ -78,7 +87,7 @@ export class GoLinkList extends OpenAPIRoute { const data = await this.getValidatedData() const { page, isActive } = data.query - const links = await getGoLinks(c.env.golinks, page, isActive) + const links = await getGoLinks(c.env.golinks, page !== undefined ? page : 0, isActive) return { success: true, diff --git a/apps/golinks-v2/src/index.ts b/apps/golinks-v2/src/index.ts index 768adc6..005030c 100644 --- a/apps/golinks-v2/src/index.ts +++ b/apps/golinks-v2/src/index.ts @@ -2,6 +2,7 @@ import { GoLinkCreate, GoLinkList } from "api/golinks"; import { fromHono } from "chanfana"; import { Hono } from "hono"; import { Env } from "./types" +import { getLink } from "lib/db"; // Start a Hono app const app = new Hono<{ Bindings: Env }>(); @@ -16,7 +17,7 @@ openapi.get("/api/links", GoLinkList) openapi.post("/api/links", GoLinkCreate) openapi.registry.registerComponent( - "securitySchemea", + "securitySchema", "adminApiKey", { type: "apiKey", @@ -25,5 +26,30 @@ openapi.registry.registerComponent( } ) +app.get("/", (c) => { + return c.redirect("https://wiki.andreijiroh.xyz/golinks") +}) + +app.get("/favicon.ico", (c) => { + return c.newResponse("404 Not Found", 404) +}) + +app.get('/:link', async (c) => { + console.log(c.req); + const {link} = c.req.param(); + const result = await getLink(c.env.golinks, link); + if (result == null) { + return c.newResponse( + JSON.stringify({ + sucesss: false, + error: "Not Found" + }), + 404, { + "Content-Type": "application/json" + }) + } + c.redirect(result.targetUrl); +}); + // Export the Hono app export default app; diff --git a/apps/golinks-v2/src/lib/db.ts b/apps/golinks-v2/src/lib/db.ts new file mode 100644 index 0000000..10410a7 --- /dev/null +++ b/apps/golinks-v2/src/lib/db.ts @@ -0,0 +1,72 @@ +import { PrismaClient } from "@prisma/client" +import { PrismaD1 } from "@prisma/adapter-d1" +import { Env } from "../types" +import { Str } from "chanfana" +const PAGE_SIZE = 10 + +function getOffset(page: number): number { + return Math.max(0, page) * PAGE_SIZE +} + +/** + * + * @param {number} length + * @returns {string} + */ +export function generateSlug(length: number) { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + +export async function getGoLinks(db: Env["golinks"], page: number, isActive?: boolean) { + const adapter = new PrismaD1(db) + const prisma = new PrismaClient({ adapter }); // Assuming you have a Prisma client instance + + const skip = getOffset(page); + const take = PAGE_SIZE; + + const result = await prisma.goLink.findMany({ + where: { + is_active: isActive !== undefined ? isActive : undefined, // Filter by isActive if provided + }, + orderBy: { + id: 'asc', // Sort by ID in ascending order (you can change this if needed) + }, + skip, + take, + }); + return result; +} + +export async function getLink(db: Env["golinks"], slug: string) { + const adapter = new PrismaD1(db) + const prisma = new PrismaClient({ adapter }); + + const result = prisma.goLink.findFirst({ + where: { + slug + } + }) + return result +} + +export async function addGoLink(db: Env["golinks"], slug: string, targetUrl: string, isActive?: boolean) { + const adapter = new PrismaD1(db) + const prisma = new PrismaClient({ adapter }) + + const result = prisma.goLink.create({ + data: { + slug, + targetUrl, + is_active: isActive !== undefined ? isActive : true, + created_on: Date.now().toString(), + updated_on: Date.now().toString(), + }, + }); + return result +} diff --git a/apps/golinks-v2/worker-configuration.d.ts b/apps/golinks-v2/worker-configuration.d.ts index afc2b89..f3f482e 100644 --- a/apps/golinks-v2/worker-configuration.d.ts +++ b/apps/golinks-v2/worker-configuration.d.ts @@ -1,4 +1,4 @@ -// Generated by Wrangler on Tue Jul 16 2024 01:49:21 GMT+0800 (Philippine Standard Time) +// Generated by Wrangler on Tue Jul 16 2024 13:58:56 GMT+0800 (Philippine Standard Time) // by running `wrangler types` interface Env { diff --git a/apps/prisma/schema.prisma b/apps/prisma/schema.prisma index 31dbd18..926478c 100644 --- a/apps/prisma/schema.prisma +++ b/apps/prisma/schema.prisma @@ -1,8 +1,18 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + model GoLink { id Int @id @default(autoincrement()) slug String @unique targetUrl String @default("https://example.com") - created_on DateTime - updated_on DateTime + created_on DateTime @default(now()) + updated_on DateTime @default(now()) is_active Boolean @default(true) } -- 2.51.2