diff --git a/apps/golinks-v2/README.md b/apps/golinks-v2/README.md index 56df86a..ad96177 100644 --- a/apps/golinks-v2/README.md +++ b/apps/golinks-v2/README.md @@ -1,6 +1,6 @@ # `@andreijiroh-dev/golinks-rewrite` -Made as part of [Hack Club Arcade / Summer of Making 2024](https://hackclub.com/arcade) ([see the scrapbook][scrapbook] or [the progress log]) +Made as part of [Hack Club Arcade / Summer of Making 2024](https://hackclub.com/arcade) ([see the scrapbook][scrapbook] for all the deets). A rewrite of [`golinks` Cloudflare Workers code](../golinks/), now with OpenAPI 3.1 powered by [chanfana](https://github.com/cloudflare/chanfana) and @@ -8,11 +8,13 @@ powered by [chanfana](https://github.com/cloudflare/chanfana) and ## Environments -* Staging: https://golinks-next-staging.ajhalili2006.workers.dev (use `gostg_4487705fd01888c0efeb78cf` as API key, might reset data) -* Production: https://golinks-next.ajhalili2006.workers.dev +- Staging: https://golinks-next-staging.ajhalili2006.workers.dev (use `gostg_4487705fd01888c0efeb78cf` as API key, might reset data) +- Production: https://golinks-next.ajhalili2006.workers.dev ## Development +> Hack Hour/Arcade reviewer? [See the docs for testing the staging environment](./docs/hackclub-arcade.md) if you don't want to mess around prod. + 1. Run `yarn` to install dependencies and generate Prisma client code with `yarn types:prisma-client`. 2. Sign in with Cloudflare for Wrangler CLI: `yarn wrangler login` 3. Run migrations to init things: `yarn migrations:apply --local` diff --git a/apps/golinks-v2/docs/hackclub-arcade.md b/apps/golinks-v2/docs/hackclub-arcade.md index a70c834..3f3ea43 100644 --- a/apps/golinks-v2/docs/hackclub-arcade.md +++ b/apps/golinks-v2/docs/hackclub-arcade.md @@ -15,7 +15,7 @@ so it is preferred to ![](https://files.slack.com/files-pri/T0266FRGM-F07CEURPSGP/image.png?pub_secret=2faa123191) 3. Now you can test the API. For routes that require authenication, use the following -API key for the `X-Golinks-Admin-Key` header. + API key for the `X-Golinks-Admin-Key` header. ``` gostg_hackclub-7yIz7ZgIy7eS9LI2KId1WxdgBisVXTKGBvuLBroIXVBqS9U9 diff --git a/apps/golinks-v2/package.json b/apps/golinks-v2/package.json index a4deb96..20cf8ed 100644 --- a/apps/golinks-v2/package.json +++ b/apps/golinks-v2/package.json @@ -1,40 +1,41 @@ { - "name": "cloudflare-workers-openapi", - "version": "0.0.1", - "private": true, - "scripts": { - "deps:upgrade": "yarn yarn-upgrade-all", - "deploy": "wrangler deploy --env staging", - "deploy:stg": "wrangler deploy --env staging", - "deploy:prod": "wrangler deploy --env production", - "dev": "wrangler dev --port 35120", - "start": "wrangler dev --port 35120", - "types:gen": "wrangler types && prisma generate", - "types:prisma-client": "prisma generate", - "migrations:new": "wrangler d1 migrations create golinks", - "migrations:diff": "prisma migrate diff --to-schema-datamodel ./prisma/schema.prisma", - "migrate:local": "wrangler d1 migrations apply golinks", - "migrate:stg": "wrangler d1 migrations apply golinks --remote --env staging", - "migrate:prod": "wrangler d1 migrations apply golinks --remote --env production", - "d1-exec:dev": "wrangler d1 execute golinks --local", - "d1-exec:stg": "wrangler d1 execute golinks --remote --env staging", - "d1-exec:prod": "wrangler d1 execute golinks --remote --env production", - "args": "echo $*" - }, - "dependencies": { - "@prisma/adapter-d1": "^5.17.0", - "@prisma/client": "^5.17.0", - "chanfana": "^2.0.2", - "hono": "^4.5.0", - "zod": "^3.23.8" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20240712.0", - "@types/node": "^20.14.11", - "@types/service-worker-mock": "^2.0.4", - "prisma": "^5.17.0", - "typescript": "^5.5.3", - "wrangler": "^3.65.0", - "yarn-upgrade-all": "^0.7.2" - } + "name": "cloudflare-workers-openapi", + "version": "0.0.1", + "private": true, + "scripts": { + "deps:upgrade": "yarn yarn-upgrade-all", + "deploy": "wrangler deploy --env staging", + "deploy:stg": "wrangler deploy --env staging", + "deploy:prod": "wrangler deploy --env production", + "dev": "wrangler dev --port 35120", + "start": "wrangler dev --port 35120", + "types:gen": "wrangler types && prisma generate", + "types:prisma-client": "prisma generate", + "migrations:new": "wrangler d1 migrations create golinks", + "migrations:diff": "prisma migrate diff --to-schema-datamodel ./prisma/schema.prisma", + "migrate:local": "wrangler d1 migrations apply golinks", + "migrate:stg": "wrangler d1 migrations apply golinks --remote --env staging", + "migrate:prod": "wrangler d1 migrations apply golinks --remote --env production", + "d1-exec:dev": "wrangler d1 execute golinks --local", + "d1-exec:stg": "wrangler d1 execute golinks --remote --env staging", + "d1-exec:prod": "wrangler d1 execute golinks --remote --env production", + "args": "echo $*" + }, + "dependencies": { + "@prisma/adapter-d1": "^5.17.0", + "@prisma/client": "^5.17.0", + "chanfana": "^2.0.2", + "hono": "^4.5.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20240712.0", + "@types/node": "^20.14.11", + "@types/service-worker-mock": "^2.0.4", + "prisma": "^5.17.0", + "ts-node": "^10.9.2", + "typescript": "^5.5.3", + "wrangler": "^3.65.0", + "yarn-upgrade-all": "^0.7.2" + } } diff --git a/apps/golinks-v2/src/api/auth.ts b/apps/golinks-v2/src/api/auth.ts new file mode 100644 index 0000000..b9eeb44 --- /dev/null +++ b/apps/golinks-v2/src/api/auth.ts @@ -0,0 +1,12 @@ +import { Context } from "hono"; + +export function checkToken(context: Context) { + const headerToken = context.req.header("X-Golinks-Admin-Token") + const apiKey = context.env.ADMIN_TOKEN + + if (headerToken === apiKey) { + return true + } else { + return false + } +} diff --git a/apps/golinks-v2/src/api/discord.ts b/apps/golinks-v2/src/api/discord.ts index 0d09db3..016b805 100644 --- a/apps/golinks-v2/src/api/discord.ts +++ b/apps/golinks-v2/src/api/discord.ts @@ -1,82 +1,82 @@ -import { OpenAPIRoute, Num, Bool, Str, contentJson } from 'chanfana'; -import { z } from 'zod'; -import { Context } from 'hono'; -import { DiscordInvites, EnvBindings } from 'types'; -import { addDiscordInvite, getDiscordInvites } from 'lib/db'; +import { OpenAPIRoute, Num, Bool, Str, contentJson } from "chanfana"; +import { z } from "zod"; +import { Context } from "hono"; +import { DiscordInvites, EnvBindings } from "types"; +import { addDiscordInvite, getDiscordInvites } from "lib/db"; export class DiscordInviteLinkCreate extends OpenAPIRoute { - schema = { - tags: ['discord-invites'], - summary: 'Add Discord invite code', - description: 'Add a Discord invite code into the DB for use in go/discord/ route.', - parameters: [ - { - in: 'header', - name: 'X-Golinks-Admin-Key', - schema: { - type: 'string', - }, - required: true, - description: 'Admin API key for golinks API', - }, - ], - request: { - body: { - content: { - "application/json": { - schema: z.object({ - slug: Str({ required: true }), - inviteCode: Str({ default: 'exmaple' }), - name: Str({ default: 'Example server' }), - description: Str({ default: 'This is a example Discord server.' }), - nsfw: z.boolean().default(false), - is_active: z.boolean().default(true), - }), - } - }, - }, - }, - security: [{ adminApiKey: [] }], - responses: { - '200': { - description: 'Returns Discord invite code information', - contentJson: { - schema: z.object({ - success: z.boolean().default(true), - result: DiscordInvites, - }), - }, - }, - }, - }; - async handle(c: Context) { - try { - const data = await this.getValidatedData(); - const linkToCreate = data.body; - console.log(`[golinks-api] received body for link creation ${JSON.stringify(linkToCreate)}`); - const { slug, inviteCode, name, description, is_active, nsfw } = linkToCreate; + schema = { + tags: ["discord-invites"], + summary: "Add Discord invite code", + description: "Add a Discord invite code into the DB for use in go/discord/ route.", + parameters: [ + { + in: "header", + name: "X-Golinks-Admin-Key", + schema: { + type: "string", + }, + required: true, + description: "Admin API key for golinks API", + }, + ], + request: { + body: { + content: { + "application/json": { + schema: z.object({ + slug: Str({ required: true }), + inviteCode: Str({ default: "exmaple" }), + name: Str({ default: "Example server" }), + description: Str({ default: "This is a example Discord server." }), + nsfw: z.boolean().default(false), + is_active: z.boolean().default(true), + }), + }, + }, + }, + }, + security: [{ adminApiKey: [] }], + responses: { + "200": { + description: "Returns Discord invite code information", + contentJson: { + schema: z.object({ + success: z.boolean().default(true), + result: DiscordInvites, + }), + }, + }, + }, + }; + async handle(c: Context) { + try { + const data = await this.getValidatedData(); + const linkToCreate = data.body; + console.log(`[golinks-api] received body for link creation ${JSON.stringify(linkToCreate)}`); + const { slug, inviteCode, name, description, is_active, nsfw } = linkToCreate; - const result = await addDiscordInvite(c.env.golinks, slug, inviteCode, name, description, is_active, nsfw) - return c.newResponse( - JSON.stringify({ - success: true, - result: result, - }), - 200, - { 'Content-Type': 'application/json' }, - ); - } catch (error) { - console.error(error) - return c.newResponse( - JSON.stringify({ - success: false, - error: `${error.name}:${error.code}` || "An unexpected error occurred while adding the invite code to database." - }), - 500, - { 'Content-Type': 'application/json' }, - ) - } - } + const result = await addDiscordInvite(c.env.golinks, slug, inviteCode, name, description, is_active, nsfw); + return c.newResponse( + JSON.stringify({ + success: true, + result: result, + }), + 200, + { "Content-Type": "application/json" }, + ); + } catch (error) { + console.error(error); + return c.newResponse( + JSON.stringify({ + success: false, + error: `${error.name}:${error.code}` || "An unexpected error occurred while adding the invite code to database.", + }), + 500, + { "Content-Type": "application/json" }, + ); + } + } } export class DiscordInviteLinkList extends OpenAPIRoute { @@ -95,47 +95,47 @@ export class DiscordInviteLinkList extends OpenAPIRoute { description: "Filter by is_active status", required: false, }), - nsfw: Bool({ - description: "Filter server list by NSFW status (NSFW servers are hidden by default).", - required: false - }) + nsfw: Bool({ + description: "Filter server list by NSFW status (NSFW servers are hidden by default).", + required: false, + }), }), }, - responses: { - "200": { - description: "Returns a list of Discord servers in the database.", - content: { - "application/json": { - schema: z.object({ - success: Bool(), - result: DiscordInvites.array() - }) - } - } - } - } + responses: { + "200": { + description: "Returns a list of Discord servers in the database.", + content: { + "application/json": { + schema: z.object({ + success: Bool(), + result: DiscordInvites.array(), + }), + }, + }, + }, + }, }; async handle(c: Context) { - try { - const data = await this.getValidatedData() - console.log(`[api] query params: JSON.stringify(${data.query})`) - const { page, isActive, nsfw } = data.query; - const links = await getDiscordInvites(c.env.golinks, page, isActive, nsfw) + try { + const data = await this.getValidatedData(); + console.log(`[api] query params: ${JSON.stringify(data.query)}`); + const { page, isActive, nsfw } = data.query; + const links = await getDiscordInvites(c.env.golinks, page !== undefined ? page : 0, isActive, nsfw); - return { - success: true, - result: links - } - } catch (err) { - return c.newResponse( - JSON.stringify({ - success: false, - error: `${err.name}:${err.code} (${err.message})` - }), - 500, - { "Content-Type": "application/json" } - ) - } + return { + success: true, + result: links, + }; + } catch (err) { + return c.newResponse( + JSON.stringify({ + success: false, + error: `${err.name}:${err.code} (${err.message})`, + }), + 500, + { "Content-Type": "application/json" }, + ); + } } } diff --git a/apps/golinks-v2/src/api/golinks.ts b/apps/golinks-v2/src/api/golinks.ts index fbf28ae..b88bef6 100644 --- a/apps/golinks-v2/src/api/golinks.ts +++ b/apps/golinks-v2/src/api/golinks.ts @@ -1,90 +1,92 @@ -import { OpenAPIRoute, Num, Bool, Str, contentJson } from "chanfana" -import { z } from "zod" -import { GoLinks } from "types" +import { OpenAPIRoute, Num, Bool, Str, contentJson } from "chanfana"; +import { z } from "zod"; +import { GoLinks } from "types"; import { addGoLink, generateSlug, getGoLinks } from "lib/db"; import { adminApiKey } from "lib/constants"; import { Context } from "hono"; +import { checkToken } from "./auth"; export class GoLinkCreate extends OpenAPIRoute { - schema = { - tags: ['golinks'], - summary: 'Create a golink', - parameters: [ - { - in: 'header', - name: 'X-Golinks-Admin-Key', - schema: { - type: 'string', - }, - required: true, - description: 'Admin API key for golinks API', - }, - ], - request: { - body: { - content: { - 'application/json': { - schema: z.object({ - slug: Str({ required: false }), - targetUrl: Str({ example: 'https://example.com' }), - is_active: z.boolean().default(true), - }), - }, - }, - }, - }, - security: [ - { - adminApiKey: [], - }, - ], - responses: { - '200': { - description: 'Returns the generated golink information', - content: { - 'application/json': { - schema: z.object({ - success: Bool(), - result: GoLinks, - }), - }, - }, - }, - '400': { - description: 'Existing entry found', - content: { - 'application/json': { - schema: z.object({ - success: Bool({ default: false }).default(false), - error: Str({ default: 'Existing entry found (error code: PrismaClientKnownRequestError:P2002)' }), - }), - }, - }, - }, - }, - }; + schema = { + tags: ["golinks"], + summary: "Create a golink", + parameters: [ + { + in: "header", + name: "X-Golinks-Admin-Key", + schema: { + type: "string", + }, + required: true, + description: "Admin API key for golinks API", + }, + ], + request: { + body: { + content: { + "application/json": { + schema: z.object({ + slug: Str({ required: false }), + targetUrl: Str({ example: "https://example.com" }), + is_active: z.boolean().default(true), + }), + }, + }, + }, + }, + security: [ + { + adminApiKey: [], + }, + ], + responses: { + "200": { + description: "Returns the generated golink information", + content: { + "application/json": { + schema: z.object({ + success: Bool(), + result: GoLinks, + }), + }, + }, + }, + "400": { + description: "Existing entry found", + content: { + "application/json": { + schema: z.object({ + success: Bool({ default: false }).default(false), + error: Str({ default: "Existing entry found (error code: PrismaClientKnownRequestError:P2002)" }), + }), + }, + }, + }, + }, + }; - async handle(c: Context) { - const data = await this.getValidatedData(); - const linkToCreate = data.body; - console.log(`[golinks-api] received body for link creation ${JSON.stringify(linkToCreate)}`); - const slug = linkToCreate.slug !== undefined ? linkToCreate.slug : generateSlug(12); + async handle(c: Context) { - const result = await addGoLink(c.env.golinks, slug, linkToCreate.targetUrl, linkToCreate.is_active); + const data = await this.getValidatedData(); + const linkToCreate = data.body; + console.log(`[golinks-api] received body for link creation ${JSON.stringify(linkToCreate)}`); + const slug = linkToCreate.slug !== undefined ? linkToCreate.slug : generateSlug(12); + + const result = await addGoLink(c.env.golinks, slug, linkToCreate.targetUrl, linkToCreate.is_active); - if (result) { - return c.newResponse( - JSON.stringify({ - success: true, - result: result, - }), - 200, - { 'Content-Type': 'application/json' }, - ); - } else { - return c.newResponse('Something went wrong', 400); - } - } + if (result) { + return c.newResponse( + JSON.stringify({ + success: true, + result: result, + }), + 200, + { "Content-Type": "application/json" }, + ); + } else { + return c.newResponse("Something went wrong", 400); + } + } } export class GoLinkList extends OpenAPIRoute { diff --git a/apps/golinks-v2/src/lib/auth.ts b/apps/golinks-v2/src/lib/auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/apps/golinks-v2/src/lib/constants.ts b/apps/golinks-v2/src/lib/constants.ts index a5c4268..e200b58 100644 --- a/apps/golinks-v2/src/lib/constants.ts +++ b/apps/golinks-v2/src/lib/constants.ts @@ -2,42 +2,42 @@ import { EnvBindings } from "types"; import { Env } from "../../worker-configuration"; export const adminApiKey = { - type: 'apiKey', - name: 'X-Golinks-Admin-Key', - in: 'header', + type: "apiKey", + name: "X-Golinks-Admin-Key", + in: "header", }; -export const homepage = "https://wiki.andreijiroh.xyz/golinks" +export const homepage = "https://wiki.andreijiroh.xyz/golinks"; export const contact = { - name: "Andrei Jiroh Halili", - url: homepage, - email: "ajhalili2006@andreijiroh.xyz" -} - -export function getWorkersDashboardUrl(env: EnvBindings['DEPLOY_ENV']) { - if (env == "production") { - return 'https://dash.cloudflare.com/cf0bd808c6a294fd8c4d8f6d2cdeca05/workers/services/view/golinks-next/production'; - } else { - return 'https://dash.cloudflare.com/cf0bd808c6a294fd8c4d8f6d2cdeca05/workers/services/view/golinks-next-staging/production'; - } + name: "Andrei Jiroh Halili", + url: homepage, + email: "ajhalili2006@andreijiroh.xyz", }; +export function getWorkersDashboardUrl(env: EnvBindings["DEPLOY_ENV"]) { + if (env == "production") { + return "https://dash.cloudflare.com/cf0bd808c6a294fd8c4d8f6d2cdeca05/workers/services/view/golinks-next/production"; + } else { + return "https://dash.cloudflare.com/cf0bd808c6a294fd8c4d8f6d2cdeca05/workers/services/view/golinks-next-staging/production"; + } +} + export const servers = [ - { - url: 'https://go.andreijiroh.xyz', - description: 'Production Deployment', - }, - { - url: 'https://golinks-next.ajhalili2006.workers.dev', - description: 'Production Deployment (workers.dev)', - }, - { - url: 'https://golinks-next-staging.ajhalili2006.workers.dev', - description: 'Staging Deployment', - }, - { - url: 'http://localhost:35120', - description: 'Local dev instance via miniflare', - }, + { + url: "https://go.andreijiroh.xyz", + description: "Production Deployment", + }, + { + url: "https://golinks-next.ajhalili2006.workers.dev", + description: "Production Deployment (workers.dev)", + }, + { + url: "https://golinks-next-staging.ajhalili2006.workers.dev", + description: "Staging Deployment", + }, + { + url: "http://localhost:35120", + description: "Local dev instance via miniflare", + }, ]; diff --git a/apps/golinks-v2/src/lib/db.ts b/apps/golinks-v2/src/lib/db.ts index 340212a..716acaf 100644 --- a/apps/golinks-v2/src/lib/db.ts +++ b/apps/golinks-v2/src/lib/db.ts @@ -1,11 +1,11 @@ -import { DiscordInviteLink, GoLink, Prisma, PrismaClient } from "@prisma/client" -import { PrismaD1 } from "@prisma/adapter-d1" +import { DiscordInviteLink, GoLink, Prisma, PrismaClient } from "@prisma/client"; +import { PrismaD1 } from "@prisma/adapter-d1"; import { Env, EnvBindings } from "../types"; -import { Str } from "chanfana" -const PAGE_SIZE = 10 +import { Str } from "chanfana"; +const PAGE_SIZE = 10; function getOffset(page: number): number { - return Math.max(0, page) * PAGE_SIZE + return Math.max(0, page) * PAGE_SIZE; } /** @@ -14,13 +14,13 @@ function getOffset(page: number): number { * @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; + 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; } /** @@ -30,24 +30,24 @@ export function generateSlug(length: number) { * @param isActive Filter by disabled links * @returns */ -export async function getGoLinks(db: EnvBindings['golinks'], page: number, isActive?: boolean) { - const adapter = new PrismaD1(db); - const prisma = new PrismaClient({ adapter }); // Assuming you have a Prisma client instance +export async function getGoLinks(db: EnvBindings["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 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: 'desc', // Sort by newer IDs - }, - skip, - take, - }); - return result; + const result = await prisma.goLink.findMany({ + where: { + is_active: isActive !== undefined ? isActive : undefined, // Filter by isActive if provided + }, + orderBy: { + id: "desc", // Sort by newer IDs + }, + skip, + take, + }); + return result; } /** @@ -58,26 +58,26 @@ export async function getGoLinks(db: EnvBindings['golinks'], page: number, * @param isNsfw * @returns */ -export async function getDiscordInvites(db: EnvBindings['golinks'], page: number, isActive?: boolean, isNsfw?: boolean) { - const adapter = new PrismaD1(db); - const prisma = new PrismaClient({ adapter }); // Assuming you have a Prisma client instance +export async function getDiscordInvites(db: EnvBindings["golinks"], page: number, isActive?: boolean, isNsfw?: 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 skip = getOffset(page); + const take = PAGE_SIZE; - const result = prisma.discordInviteLink.findMany({ - where: { - is_active: isActive !== undefined ? isActive : undefined, // Filter by isActive if provided - nsfw: isNsfw !== undefined ? isNsfw : false // Hide NSFW servers for safety by default. - }, - orderBy: { - id: 'desc', // Sort by newer IDs - }, - skip, - take, - }); - return result; -}; + const result = await prisma.discordInviteLink.findMany({ + where: { + is_active: isActive !== undefined ? isActive : undefined, // Filter by isActive if provided + nsfw: isNsfw !== undefined ? isNsfw : false, // Hide NSFW servers for safety by default. + }, + orderBy: { + id: "desc", // Sort by newer IDs + }, + skip, + take, + }); + return result; +} /** * @@ -85,67 +85,68 @@ export async function getDiscordInvites(db: EnvBindings['golinks'], page: n * @param slug URL slug to query into * @returns */ -export async function getLink(db: EnvBindings['golinks'], slug: string): Promise | null { - const adapter = new PrismaD1(db); - const prisma = new PrismaClient({ adapter }); +export async function getLink(db: EnvBindings["golinks"], slug: string): Promise | null { + const adapter = new PrismaD1(db); + const prisma = new PrismaClient({ adapter }); - const result = prisma.goLink.findUnique({ - where: { - slug: slug, - }, - }); - return result; + const result = prisma.goLink.findUnique({ + where: { + slug: slug, + }, + }); + return result; } -export async function getDiscordInvite( - db: EnvBindings["golinks"], - slug: string -): Promise | null { - const adapter = new PrismaD1(db) - const prisma = new PrismaClient({adapter}) +export async function getDiscordInvite(db: EnvBindings["golinks"], slug: string): Promise | null { + const adapter = new PrismaD1(db); + const prisma = new PrismaClient({ adapter }); - const result = await prisma.discordInviteLink.findUnique({ - where: { - slug - } - }) - return result + const result = await prisma.discordInviteLink.findUnique({ + where: { + slug, + }, + }); + return result; } export async function addGoLink( - db: EnvBindings["golinks"], slug: string, targetUrl: string, isActive?: boolean): Promise | null { - const adapter = new PrismaD1(db) - const prisma = new PrismaClient({ adapter }) + db: EnvBindings["golinks"], + slug: string, + targetUrl: string, + isActive?: boolean, +): Promise | null { + const adapter = new PrismaD1(db); + const prisma = new PrismaClient({ adapter }); const result = prisma.goLink.create({ - data: { - slug, - targetUrl, - is_active: isActive !== undefined ? isActive : true - }, - }); - return result + data: { + slug, + targetUrl, + is_active: isActive !== undefined ? isActive : true, + }, + }); + return result; } export async function addDiscordInvite( - db: EnvBindings['golinks'], - slug: string, - inviteCode: string, - name: string, - description: string, - is_active?: boolean, - nsfw?: boolean + db: EnvBindings["golinks"], + slug: string, + inviteCode: string, + name: string, + description: string, + is_active?: boolean, + nsfw?: boolean, ) { - const adapter = new PrismaD1(db); - const prisma = new PrismaClient({adapter}) - const result = prisma.discordInviteLink.create({ - data: { - slug, - inviteCode, - name, - description, - is_active: is_active !== undefined ? is_active : true, - nsfw: nsfw !== undefined ? nsfw : false - } - }) - return result -}; + const adapter = new PrismaD1(db); + const prisma = new PrismaClient({ adapter }); + const result = prisma.discordInviteLink.create({ + data: { + slug, + inviteCode, + name, + description, + is_active: is_active !== undefined ? is_active : true, + nsfw: nsfw !== undefined ? nsfw : false, + }, + }); + return result; +} diff --git a/apps/golinks-v2/src/types.ts b/apps/golinks-v2/src/types.ts index dd787ed..32f9aed 100644 --- a/apps/golinks-v2/src/types.ts +++ b/apps/golinks-v2/src/types.ts @@ -1,37 +1,43 @@ import { Bool, DateTime, Int, Str } from "chanfana"; import { z } from "zod"; -import { Env } from "../worker-configuration"; export const Task = z.object({ - name: Str({ example: "lorem" }), - slug: Str(), - description: Str({ required: false }), - completed: z.boolean().default(false), - due_date: DateTime(), + name: Str({ example: "lorem" }), + slug: Str(), + description: Str({ required: false }), + completed: z.boolean().default(false), + due_date: DateTime(), }); export const GoLinks = z.object({ - id: Int({ required: false }), - slug: Str({ required: true }), - targetUrl: Str({ example: "https://example.com" }), - is_active: z.boolean().default(true), - created_on: DateTime({ default: Date.now() }), - updated_on: DateTime({ default: Date.now() }) -}) + id: Int({ required: false }), + slug: Str({ required: true }), + targetUrl: Str({ example: "https://example.com" }), + is_active: z.boolean().default(true), + created_on: DateTime({ default: Date.now() }), + updated_on: DateTime({ default: Date.now() }), +}); export const DiscordInvites = z.object({ - id: Int({ required: false }), - slug: Str(), - inviteCode: Str(), - name: Str(), - description: Str(), - nsfw: z.boolean().default(false), - is_active: z.boolean().default(true), - created_on: DateTime({ default: Date.now() }), - updated_on: DateTime({ default: Date.now() }), + id: Int({ required: false }), + slug: Str(), + inviteCode: Str(), + name: Str(), + description: Str(), + nsfw: z.boolean().default(false), + is_active: z.boolean().default(true), + created_on: DateTime({ default: Date.now() }), + updated_on: DateTime({ default: Date.now() }), }); -export type EnvBindings = { - golinks: D1Database; - DEPLOY_ENV: "production" | "staging" | "development" +export interface Env { + DEPLOY_ENV: "production" | "staging" | "development"; + golinks: D1Database; + ADMIN_KEY: string; } + +export type EnvBindings = { + golinks: D1Database; + DEPLOY_ENV: "production" | "staging" | "development"; + ADMIN_KEY: string +}; diff --git a/apps/golinks-v2/tsconfig.json b/apps/golinks-v2/tsconfig.json index 196166e..f5eb665 100644 --- a/apps/golinks-v2/tsconfig.json +++ b/apps/golinks-v2/tsconfig.json @@ -1,32 +1,28 @@ { - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "baseUrl": "src", - "declaration": true, - "sourceMap": true, - "esModuleInterop": true, - "inlineSourceMap": false, - "lib": ["esnext"], - "listEmittedFiles": false, - "listFiles": false, - "moduleResolution": "node", - "noFallthroughCasesInSwitch": true, - "pretty": true, - "resolveJsonModule": true, - "rootDir": ".", - "skipLibCheck": true, - "strict": false, - "traceResolution": false, - "outDir": "", - "target": "esnext", - "module": "esnext", - "types": [ - "@types/node", - "@types/service-worker-mock", - "@cloudflare/workers-types" - ] - }, - "exclude": ["node_modules", "dist", "tests"], - "include": ["src", "scripts"] + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "baseUrl": "src", + "declaration": true, + "sourceMap": true, + "esModuleInterop": true, + "inlineSourceMap": false, + "lib": ["esnext"], + "listEmittedFiles": false, + "listFiles": false, + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "pretty": true, + "resolveJsonModule": true, + "rootDir": ".", + "skipLibCheck": true, + "strict": false, + "traceResolution": false, + "outDir": "", + "target": "esnext", + "module": "esnext", + "types": ["@types/node", "@types/service-worker-mock", "@cloudflare/workers-types"] + }, + "exclude": ["node_modules", "dist", "tests"], + "include": ["src", "scripts"] } diff --git a/apps/golinks-v2/wrangler.toml b/apps/golinks-v2/wrangler.toml index 9727749..53c9959 100644 --- a/apps/golinks-v2/wrangler.toml +++ b/apps/golinks-v2/wrangler.toml @@ -14,21 +14,23 @@ account_id = "cf0bd808c6a294fd8c4d8f6d2cdeca05" placement = { mode = "smart" } -vars = {DEPLOY_ENV = "development"} +vars = {DEPLOY_ENV = "development", ADMIN_KEY = "gostg_localdev"} d1_databases = [ { binding = "golinks", database_name = "golinks-db-preview", database_id = "0b415a64-cc61-4c2b-a7c8-2d223f18e996", migrations_dir = "migrations" } ] [env.staging] -vars = {DEPLOY_ENV = "staging"} +vars = {DEPLOY_ENV = "staging", ADMIN_KEY = "GOLINKS_ADMIN_API_KEY"} d1_databases = [ { binding = "golinks", database_name = "golinks-db-preview", database_id = "0b415a64-cc61-4c2b-a7c8-2d223f18e996", migrations_dir = "migrations" } ] + + [env.production] -vars = {DEPLOY_ENV = "production"} +vars = {DEPLOY_ENV = "production", ADMIN_KEY = "GOLINKS_ADMIN_API_KEY"} # Bind a D1 database. D1 is Cloudflare’s native serverless SQL database. # Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases