From 164e491ce94e7d2879613853424481cedab707d7 Mon Sep 17 00:00:00 2001 From: shaokeyibb Date: Wed, 17 Jul 2024 13:07:01 +0800 Subject: [PATCH] feat(workers): secret && meta --- cloudflare-workers/schema.sql | 6 +++ cloudflare-workers/src/administration.ts | 23 +++++++++ cloudflare-workers/src/db.ts | 14 ++++++ cloudflare-workers/src/index.ts | 50 ++++++++++++++++++-- cloudflare-workers/src/types.ts | 15 ++++-- cloudflare-workers/worker-configuration.d.ts | 3 +- 6 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 cloudflare-workers/src/administration.ts diff --git a/cloudflare-workers/schema.sql b/cloudflare-workers/schema.sql index 582dd1ee..07b93c94 100644 --- a/cloudflare-workers/schema.sql +++ b/cloudflare-workers/schema.sql @@ -2,6 +2,12 @@ DROP TABLE IF EXISTS `comments`; DROP TABLE IF EXISTS `commenters`; DROP TABLE IF EXISTS `offsets`; DROP TABLE IF EXISTS `pages`; +DROP TABLE IF EXISTS `metas`; + +CREATE TABLE IF NOT EXISTS `metas` ( + `key` TEXT PRIMARY KEY, + `value` TEXT NOT NULL +); CREATE TABLE IF NOT EXISTS `pages` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/cloudflare-workers/src/administration.ts b/cloudflare-workers/src/administration.ts new file mode 100644 index 00000000..e27d5f93 --- /dev/null +++ b/cloudflare-workers/src/administration.ts @@ -0,0 +1,23 @@ +import { getMeta, setMeta } from "./db"; + +const encoder = new TextEncoder(); + +export function validateSecret(env: Env, secret: string): boolean { + const secretBytes = encoder.encode(env.ADMINISTRATOR_SECRET); + const inputBytes = encoder.encode(secret); + + if (secretBytes.byteLength !== inputBytes.byteLength) { + return false; + } + + return crypto.subtle.timingSafeEqual(secretBytes, inputBytes); +} + +export async function setCommitHash(env: Env, hash: string) { + await setMeta(env, 'commit_hash', hash); +} + +export async function compareCommitHash(env: Env, hash: string): Promise { + const storedHash = await getMeta(env, 'commit_hash'); + return storedHash === hash; +} \ No newline at end of file diff --git a/cloudflare-workers/src/db.ts b/cloudflare-workers/src/db.ts index 925d04bf..dee5807d 100644 --- a/cloudflare-workers/src/db.ts +++ b/cloudflare-workers/src/db.ts @@ -64,3 +64,17 @@ export async function getComment(env: Env, req: GetComment): Promise { + const db = env.DB; + + const meta = await db.prepare('SELECT * FROM metas WHERE key = ?').bind(key).first>(); + + return meta?.value; +} diff --git a/cloudflare-workers/src/index.ts b/cloudflare-workers/src/index.ts index 44c5cbf7..93a1942a 100644 --- a/cloudflare-workers/src/index.ts +++ b/cloudflare-workers/src/index.ts @@ -12,8 +12,9 @@ */ import { AutoRouter, error } from 'itty-router'; -import { GetCommentBody, GetCommentRespBody, PostCommentBody, ResponseBody } from './types'; -import { getComment, postComment } from './db'; +import { GetCommentBody, GetCommentRespBody, GetCommitHashRespBody, PostCommentBody, PutCommitHashBody, ResponseBody } from './types'; +import { getComment, getMeta, postComment } from './db'; +import { validateSecret, setCommitHash, compareCommitHash } from './administration'; const router = AutoRouter(); @@ -28,7 +29,8 @@ router.post('/comment', async (req, env, ctx) => { body.comment == undefined || body.offset.start == undefined || body.offset.end == undefined || - body.commenter.name == undefined + body.commenter.name == undefined || + body.commit_hash == undefined ) { return error(400, 'Invalid request body'); } @@ -49,6 +51,10 @@ router.post('/comment', async (req, env, ctx) => { return error(400, 'Invalid comment'); } + if(!await compareCommitHash(env, body.commit_hash)) { + return error(409, 'Commit hash mismatch, usually due to outdated cache or running CI/CD, please retry after a few minutes'); + } + await postComment(env, { path: body.path, offset: body.offset, @@ -62,7 +68,7 @@ router.post('/comment', async (req, env, ctx) => { return { status: 200, - } as ResponseBody<{}>; + } satisfies ResponseBody; }); router.get('/comment', async (req, env, ctx) => { @@ -77,7 +83,41 @@ router.get('/comment', async (req, env, ctx) => { return { status: 200, data: rst, - } as ResponseBody; + } satisfies ResponseBody; +}); + +router.put('/meta/commithash', async (req, env, ctx) => { + const body = await req.json(); + + if (body == undefined || body.commit_hash == undefined) { + return error(400, 'Invalid request body'); + } + + if (body.commit_hash.length < 1) { + return error(400, 'Invalid commit hash'); + } + + const authorization = req.headers.get('Authorization'); + + if (!authorization) { + return error(401, 'Unauthorized'); + } + + const [scheme, secret] = authorization.split(' '); + + if (scheme !== 'Bearer' || !secret) { + return error(400, 'Malformed authorization header'); + } + + if (validateSecret(env, secret) !== true) { + return error(401, 'Unauthorized'); + } + + setCommitHash(env, body.commit_hash); + + return { + status: 200, + } satisfies ResponseBody; }); export default { ...router } satisfies ExportedHandler; diff --git a/cloudflare-workers/src/types.ts b/cloudflare-workers/src/types.ts index f5519939..6e90eb84 100644 --- a/cloudflare-workers/src/types.ts +++ b/cloudflare-workers/src/types.ts @@ -8,6 +8,7 @@ export type PostCommentBody = { name: string; }; comment: string; + commit_hash: string; }; export type PostComment = { @@ -15,7 +16,7 @@ export type PostComment = { user_agent: string; ip_address: string | null; }; -} & PostCommentBody; +} & Omit; export type GetCommentBody = { path: string; @@ -35,7 +36,15 @@ export type GetCommentRespBody = { created_time: string; }[]; -export type ResponseBody = { +export type PutCommitHashBody = { + commit_hash: string; +}; + +export type GetCommitHashRespBody = { + commit_hash: string | undefined; +} + +export type ResponseBody = { status: 200; - data: T; + data?: T; }; diff --git a/cloudflare-workers/worker-configuration.d.ts b/cloudflare-workers/worker-configuration.d.ts index 3cb8b3c8..bcdc16a4 100644 --- a/cloudflare-workers/worker-configuration.d.ts +++ b/cloudflare-workers/worker-configuration.d.ts @@ -1,6 +1,7 @@ -// Generated by Wrangler on Mon Jul 15 2024 16:30:44 GMT+0800 (China Standard Time) +// Generated by Wrangler on Wed Jul 17 2024 12:23:04 GMT+0800 (China Standard Time) // by running `wrangler types` interface Env { + ADMINISTRATOR_SECRET: string; DB: D1Database; }