From 2787d695ec08215bfb34f8169e5491f4957919c9 Mon Sep 17 00:00:00 2001 From: lashman Date: Sat, 21 Mar 2026 23:41:23 +0200 Subject: [PATCH] remove old gitea plugin, replaced by gitea-sync --- plugins/gitea/package.json | 17 ---- plugins/gitea/src/index.ts | 156 ------------------------------------ plugins/gitea/tsconfig.json | 8 -- 3 files changed, 181 deletions(-) delete mode 100644 plugins/gitea/package.json delete mode 100644 plugins/gitea/src/index.ts delete mode 100644 plugins/gitea/tsconfig.json diff --git a/plugins/gitea/package.json b/plugins/gitea/package.json deleted file mode 100644 index 0f55c71..0000000 --- a/plugins/gitea/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "@echoboard/plugin-gitea", - "version": "0.1.0", - "private": true, - "type": "module", - "main": "dist/index.js", - "scripts": { - "build": "tsc", - "dev": "tsc --watch" - }, - "dependencies": { - "@echoboard/api": "*" - }, - "devDependencies": { - "typescript": "^5.7.0" - } -} diff --git a/plugins/gitea/src/index.ts b/plugins/gitea/src/index.ts deleted file mode 100644 index 8b23025..0000000 --- a/plugins/gitea/src/index.ts +++ /dev/null @@ -1,156 +0,0 @@ -import type { FastifyInstance } from 'fastify'; -import crypto from 'node:crypto'; -import { Readable } from 'node:stream'; - -interface GiteaConfig { - url: string; - apiToken: string; - webhookSecret: string; - syncOnStartup: boolean; - syncCron: string; -} - -function getConfig(): GiteaConfig { - const url = process.env.PLUGIN_GITEA_URL; - const apiToken = process.env.PLUGIN_GITEA_API_TOKEN; - const webhookSecret = process.env.PLUGIN_GITEA_WEBHOOK_SECRET; - - if (!url || !apiToken || !webhookSecret) { - throw new Error('Missing required PLUGIN_GITEA_* env vars'); - } - - return { - url: url.replace(/\/$/, ''), - apiToken, - webhookSecret, - syncOnStartup: process.env.PLUGIN_GITEA_SYNC_ON_STARTUP !== 'false', - syncCron: process.env.PLUGIN_GITEA_SYNC_CRON || '0 */6 * * *', - }; -} - -async function fetchRepos(config: GiteaConfig) { - const repos: Array<{ id: number; name: string; full_name: string; html_url: string; description: string }> = []; - let page = 1; - - while (true) { - const res = await fetch( - `${config.url}/api/v1/repos/search?limit=50&page=${page}`, - { headers: { Authorization: `token ${config.apiToken}` } } - ); - if (!res.ok) break; - - const body = await res.json() as { data: typeof repos }; - if (!body.data?.length) break; - - repos.push(...body.data); - if (body.data.length < 50) break; - page++; - } - - return repos; -} - -function verifyWebhookSignature(payload: Buffer | string, signature: string, secret: string): boolean { - const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex'); - if (signature.length !== expected.length) return false; - return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); -} - -export const giteaPlugin = { - name: 'gitea', - version: '0.1.0', - - onRegister(app: FastifyInstance) { - const config = getConfig(); - - app.post('/api/v1/plugins/gitea/webhook', { - preParsing: async (req, _reply, payload) => { - const chunks: Buffer[] = []; - for await (const chunk of payload) { - chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); - } - const raw = Buffer.concat(chunks); - (req as any).rawBody = raw; - return Readable.from(raw); - }, - }, async (req, reply) => { - const sig = req.headers['x-gitea-signature'] as string; - const rawBody: Buffer = (req as any).rawBody; - - if (!sig || !verifyWebhookSignature(rawBody, sig, config.webhookSecret)) { - return reply.status(401).send({ error: 'invalid signature' }); - } - - const event = req.headers['x-gitea-event'] as string; - const body = req.body as Record; - - if (event === 'repository' && body.action === 'created') { - const repo = body.repository as { id: number; name: string; html_url: string; description: string }; - const slug = repo.name.toLowerCase().replace(/[^a-z0-9-]/g, '-'); - - const { prisma } = app as unknown as { prisma: { board: { upsert: Function } } }; - await prisma.board.upsert({ - where: { slug }, - create: { - slug, - name: repo.name, - description: repo.description || null, - externalUrl: repo.html_url, - }, - update: {}, - }); - } - - if (event === 'repository' && body.action === 'deleted') { - const repo = body.repository as { name: string }; - const slug = repo.name.toLowerCase().replace(/[^a-z0-9-]/g, '-'); - - const { prisma } = app as unknown as { prisma: { board: { updateMany: Function } } }; - await prisma.board.updateMany({ - where: { slug }, - data: { isArchived: true }, - }); - } - - return reply.status(200).send({ ok: true }); - }); - - app.get('/api/v1/plugins/gitea/sync-status', { preHandler: [app.requireAdmin] }, async (_req, reply) => { - return reply.send({ status: 'ok', lastSync: new Date().toISOString() }); - }); - - app.post('/api/v1/plugins/gitea/sync', { preHandler: [app.requireAdmin], config: { rateLimit: { max: 2, timeWindow: '1 minute' } } }, async (_req, reply) => { - const repos = await fetchRepos(config); - const { prisma } = app as unknown as { prisma: { board: { upsert: Function } } }; - - for (const repo of repos) { - const slug = repo.name.toLowerCase().replace(/[^a-z0-9-]/g, '-'); - await prisma.board.upsert({ - where: { slug }, - create: { - slug, - name: repo.name, - description: repo.description || null, - externalUrl: repo.html_url, - }, - update: { externalUrl: repo.html_url }, - }); - } - - return reply.send({ synced: repos.length }); - }); - }, - - async onStartup() { - const config = getConfig(); - if (!config.syncOnStartup) return; - // initial sync handled by the route handler logic - // in production this would call fetchRepos and sync - }, - - getAdminRoutes() { - return [ - { path: '/admin/gitea', label: 'Gitea Sync', component: 'gitea-sync' }, - ]; - }, -}; diff --git a/plugins/gitea/tsconfig.json b/plugins/gitea/tsconfig.json deleted file mode 100644 index 9c8a7d7..0000000 --- a/plugins/gitea/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*"] -}