diff --git a/swc-plugins/app/api/update/wasm-plugins/route.ts b/swc-plugins/app/api/update/wasm-plugins/route.ts new file mode 100644 index 0000000..a5c9524 --- /dev/null +++ b/swc-plugins/app/api/update/wasm-plugins/route.ts @@ -0,0 +1,13 @@ +import { UpdateWasmPluginsInputSchema } from "@/lib/api/updater/router"; +import { createCaller } from "@/lib/server"; +import { NextRequest, NextResponse } from "next/server"; + +export const POST = async (req: NextRequest) => { + const body = UpdateWasmPluginsInputSchema.parse(await req.json()); + + const api = await createCaller(); + + await api.updater.updateWasmPlugins(body); + + return NextResponse.json({ ok: true }); +}; diff --git a/swc-plugins/lib/api/router.ts b/swc-plugins/lib/api/router.ts index b74ab2d..2acce3a 100644 --- a/swc-plugins/lib/api/router.ts +++ b/swc-plugins/lib/api/router.ts @@ -3,6 +3,7 @@ import { router } from "@/lib/base"; import { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; import { compatRangeRouter } from "./compatRange/router"; import { runtimeRouter } from "./runtimes/router"; +import { updaterRouter } from "./updater/router"; import { userRouter } from "./users/router"; export const apiRouter = router({ @@ -10,6 +11,8 @@ export const apiRouter = router({ runtime: runtimeRouter, compatRange: compatRangeRouter, + + updater: updaterRouter, }); export type ApiRouter = typeof apiRouter; diff --git a/swc-plugins/lib/api/runtimes/router.ts b/swc-plugins/lib/api/runtimes/router.ts index 4eeffb2..5eadf09 100644 --- a/swc-plugins/lib/api/runtimes/router.ts +++ b/swc-plugins/lib/api/runtimes/router.ts @@ -47,6 +47,9 @@ export const runtimeRouter = router({ version: true, compatRangeId: true, }, + orderBy: { + version: "desc", + }, }); return versions; diff --git a/swc-plugins/lib/api/server.ts b/swc-plugins/lib/api/server.ts index e6882cc..e3f602b 100644 --- a/swc-plugins/lib/api/server.ts +++ b/swc-plugins/lib/api/server.ts @@ -154,14 +154,18 @@ export function defineAbilitiesFor({ user }: { user: User | null }): Abilities { const factory = createCallerFactory(apiRouter); -export const createCaller = async () => { +export const createCaller = async (ctx?: Context) => { + if (ctx) { + return factory(ctx); + } + const user: User | null = await getCurrentUser(); const abilities = defineAbilitiesFor({ user, }); - const ctx: Context = { + const newCtx: Context = { getAccessToken() { const h = headers(); const auth = h.get("authorization"); @@ -172,5 +176,5 @@ export const createCaller = async () => { responseHeaders: null, isAdmin: false, }; - return factory(ctx); + return factory(newCtx); }; diff --git a/swc-plugins/lib/api/updater/router.ts b/swc-plugins/lib/api/updater/router.ts new file mode 100644 index 0000000..7bbdc1a --- /dev/null +++ b/swc-plugins/lib/api/updater/router.ts @@ -0,0 +1,86 @@ +import { publicProcedure, router } from "@/lib/base"; +import { db } from "@/lib/prisma"; +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; + +function validateToken(token: string) { + if (token === process.env.CRAWL_SECRET) { + return; + } + + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Invalid token", + }); +} + +const NpmPackageVersionSchema = z.object({ + version: z.string(), + swcCoreVersion: z.string(), +}); + +const NpmPackageSchema = z.object({ + name: z.string(), + versions: z.array(NpmPackageVersionSchema), +}); + +export const UpdateWasmPluginsInputSchema = z.object({ + token: z.string(), + pkgs: z.array(NpmPackageSchema), +}); + +export const updaterRouter = router({ + updateWasmPlugins: publicProcedure + .input(UpdateWasmPluginsInputSchema) + .output(z.void()) + .mutation(async ({ input, ctx }) => { + validateToken(input.token); + + const api = await (await import("@/lib/api/server")).createCaller(ctx); + + for (const pkg of input.pkgs) { + const plugin = await db.swcPlugin.upsert({ + where: { + name: pkg.name, + }, + create: { + name: pkg.name, + }, + update: {}, + }); + + for (const version of pkg.versions) { + const swcCoreVersion = version.swcCoreVersion; + const compatRange = await api.compatRange.byCoreVersion({ + version: swcCoreVersion, + }); + + if (!compatRange) { + throw new TRPCError({ + code: "NOT_FOUND", + message: `Compat range not found for SWC core version ${swcCoreVersion}`, + }); + } + + await db.swcPluginVersion.upsert({ + where: { + pluginId_version: { + pluginId: plugin.id, + version: version.version, + }, + }, + create: { + pluginId: plugin.id, + version: version.version, + compatRangeId: compatRange.id, + swcCoreVersion, + }, + update: { + compatRangeId: compatRange.id, + swcCoreVersion, + }, + }); + } + } + }), +});