@donkeylabs/cli 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -1
- package/src/commands/generate.ts +122 -1
- package/src/commands/init.ts +31 -0
- package/templates/starter/src/index.ts +21 -4
- package/templates/sveltekit-app/package.json +3 -3
- package/templates/sveltekit-app/src/server/index.ts +14 -20
- package/templates/sveltekit-app/src/server/routes/demo.ts +237 -0
- package/templates/sveltekit-app/src/server/routes/cache/handlers/delete.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/handlers/get.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/handlers/keys.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/handlers/set.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/index.ts +0 -46
- package/templates/sveltekit-app/src/server/routes/counter/handlers/decrement.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/handlers/get.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/handlers/increment.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/handlers/reset.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/index.ts +0 -39
- package/templates/sveltekit-app/src/server/routes/cron/handlers/list.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/cron/index.ts +0 -24
- package/templates/sveltekit-app/src/server/routes/events/handlers/emit.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/events/index.ts +0 -19
- package/templates/sveltekit-app/src/server/routes/index.ts +0 -8
- package/templates/sveltekit-app/src/server/routes/jobs/handlers/enqueue.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/jobs/handlers/stats.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/jobs/index.ts +0 -28
- package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/check.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/reset.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/ratelimit/index.ts +0 -29
- package/templates/sveltekit-app/src/server/routes/sse/handlers/broadcast.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/sse/handlers/clients.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/sse/index.ts +0 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@donkeylabs/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for @donkeylabs/server - project scaffolding and code generation",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"typecheck": "bun --bun tsc --noEmit"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"kysely": "^0.27.0",
|
|
28
|
+
"kysely-bun-sqlite": "^0.3.0",
|
|
29
|
+
"kysely-codegen": "^0.17.0",
|
|
27
30
|
"picocolors": "^1.1.1",
|
|
28
31
|
"prompts": "^2.4.2"
|
|
29
32
|
},
|
package/src/commands/generate.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { readdir, writeFile, readFile, mkdir } from "node:fs/promises";
|
|
1
|
+
import { readdir, writeFile, readFile, mkdir, unlink } from "node:fs/promises";
|
|
2
2
|
import { join, relative, dirname, basename } from "node:path";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import pc from "picocolors";
|
|
6
|
+
import { Kysely, Migrator, FileMigrationProvider } from "kysely";
|
|
7
|
+
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
|
8
|
+
import { Database } from "bun:sqlite";
|
|
9
|
+
import { generate, KyselyBunSqliteDialect } from "kysely-codegen";
|
|
6
10
|
|
|
7
11
|
interface DonkeylabsConfig {
|
|
8
12
|
plugins: string[];
|
|
@@ -325,6 +329,8 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
325
329
|
return [];
|
|
326
330
|
}
|
|
327
331
|
|
|
332
|
+
const TIMEOUT_MS = 10000; // 10 second timeout
|
|
333
|
+
|
|
328
334
|
return new Promise((resolve) => {
|
|
329
335
|
const child = spawn("bun", [fullPath], {
|
|
330
336
|
env: { ...process.env, DONKEYLABS_GENERATE: "1" },
|
|
@@ -334,6 +340,16 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
334
340
|
|
|
335
341
|
let stdout = "";
|
|
336
342
|
let stderr = "";
|
|
343
|
+
let timedOut = false;
|
|
344
|
+
|
|
345
|
+
// Timeout handler
|
|
346
|
+
const timeout = setTimeout(() => {
|
|
347
|
+
timedOut = true;
|
|
348
|
+
child.kill("SIGTERM");
|
|
349
|
+
console.warn(pc.yellow(`Route extraction timed out after ${TIMEOUT_MS / 1000}s`));
|
|
350
|
+
console.warn(pc.dim("Make sure your entry file handles DONKEYLABS_GENERATE=1 and calls process.exit(0)"));
|
|
351
|
+
resolve([]);
|
|
352
|
+
}, TIMEOUT_MS);
|
|
337
353
|
|
|
338
354
|
child.stdout?.on("data", (data) => {
|
|
339
355
|
stdout += data.toString();
|
|
@@ -344,6 +360,9 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
344
360
|
});
|
|
345
361
|
|
|
346
362
|
child.on("close", (code) => {
|
|
363
|
+
clearTimeout(timeout);
|
|
364
|
+
if (timedOut) return; // Already resolved
|
|
365
|
+
|
|
347
366
|
if (code !== 0) {
|
|
348
367
|
console.warn(pc.yellow(`Failed to extract routes from server (exit code ${code})`));
|
|
349
368
|
if (stderr) console.warn(pc.dim(stderr));
|
|
@@ -374,12 +393,105 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
374
393
|
});
|
|
375
394
|
|
|
376
395
|
child.on("error", (err) => {
|
|
396
|
+
clearTimeout(timeout);
|
|
377
397
|
console.warn(pc.yellow(`Failed to run entry file: ${err.message}`));
|
|
378
398
|
resolve([]);
|
|
379
399
|
});
|
|
380
400
|
});
|
|
381
401
|
}
|
|
382
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Generate schema.ts from plugin migrations using kysely-codegen
|
|
405
|
+
*/
|
|
406
|
+
async function generatePluginSchemas(
|
|
407
|
+
plugins: { name: string; path: string; exportName: string }[]
|
|
408
|
+
): Promise<string[]> {
|
|
409
|
+
const generated: string[] = [];
|
|
410
|
+
|
|
411
|
+
for (const plugin of plugins) {
|
|
412
|
+
const pluginDir = dirname(join(process.cwd(), plugin.path));
|
|
413
|
+
const migrationsDir = join(pluginDir, "migrations");
|
|
414
|
+
|
|
415
|
+
// Skip plugins without migrations folder
|
|
416
|
+
if (!existsSync(migrationsDir)) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check if there are any migration files
|
|
421
|
+
const migrationFiles = await readdir(migrationsDir);
|
|
422
|
+
const hasMigrations = migrationFiles.some(
|
|
423
|
+
(f) => f.endsWith(".ts") && !f.endsWith(".d.ts")
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
if (!hasMigrations) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
console.log(pc.dim(` Generating schema for ${plugin.name}...`));
|
|
431
|
+
|
|
432
|
+
const dbPath = join(process.cwd(), `.temp_schema_${plugin.name}.db`);
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
// Create temp SQLite database
|
|
436
|
+
const db = new Kysely<any>({
|
|
437
|
+
dialect: new BunSqliteDialect({
|
|
438
|
+
database: new Database(dbPath),
|
|
439
|
+
}),
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Run migrations using Kysely's FileMigrationProvider
|
|
443
|
+
const migrator = new Migrator({
|
|
444
|
+
db,
|
|
445
|
+
provider: new FileMigrationProvider({
|
|
446
|
+
fs: await import("node:fs/promises"),
|
|
447
|
+
path: await import("node:path"),
|
|
448
|
+
migrationFolder: migrationsDir,
|
|
449
|
+
}),
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const { error } = await migrator.migrateToLatest();
|
|
453
|
+
|
|
454
|
+
if (error) {
|
|
455
|
+
console.warn(
|
|
456
|
+
pc.yellow(` Warning: Migration failed for ${plugin.name}: ${error}`)
|
|
457
|
+
);
|
|
458
|
+
await db.destroy();
|
|
459
|
+
try {
|
|
460
|
+
await unlink(dbPath);
|
|
461
|
+
} catch {}
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Generate schema.ts using kysely-codegen
|
|
466
|
+
const schemaPath = join(pluginDir, "schema.ts");
|
|
467
|
+
|
|
468
|
+
await generate({
|
|
469
|
+
db,
|
|
470
|
+
outFile: schemaPath,
|
|
471
|
+
dialect: new KyselyBunSqliteDialect(),
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
generated.push(plugin.name);
|
|
475
|
+
|
|
476
|
+
// Cleanup
|
|
477
|
+
await db.destroy();
|
|
478
|
+
try {
|
|
479
|
+
await unlink(dbPath);
|
|
480
|
+
} catch {}
|
|
481
|
+
} catch (err: any) {
|
|
482
|
+
console.warn(
|
|
483
|
+
pc.yellow(` Warning: Schema generation failed for ${plugin.name}: ${err.message}`)
|
|
484
|
+
);
|
|
485
|
+
// Cleanup on error
|
|
486
|
+
try {
|
|
487
|
+
await unlink(dbPath);
|
|
488
|
+
} catch {}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return generated;
|
|
493
|
+
}
|
|
494
|
+
|
|
383
495
|
export async function generateCommand(_args: string[]): Promise<void> {
|
|
384
496
|
const config = await loadConfig();
|
|
385
497
|
const outDir = config.outDir || ".@donkeylabs/server";
|
|
@@ -390,6 +502,15 @@ export async function generateCommand(_args: string[]): Promise<void> {
|
|
|
390
502
|
const plugins = await findPlugins(config.plugins);
|
|
391
503
|
const fileRoutes = await findRoutes(config.routes || "./src/routes/**/schema.ts");
|
|
392
504
|
|
|
505
|
+
// Generate schema.ts from migrations for plugins that have them
|
|
506
|
+
const schemaPlugins = await generatePluginSchemas(plugins);
|
|
507
|
+
if (schemaPlugins.length > 0) {
|
|
508
|
+
console.log(
|
|
509
|
+
pc.green("Generated schemas:"),
|
|
510
|
+
schemaPlugins.map((p) => pc.dim(p)).join(", ")
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
393
514
|
// Extract routes by running the server with DONKEYLABS_GENERATE=1
|
|
394
515
|
const entryPath = config.entry || "./src/index.ts";
|
|
395
516
|
const serverRoutes = await extractRoutesFromServer(entryPath);
|
package/src/commands/init.ts
CHANGED
|
@@ -166,6 +166,9 @@ export async function initCommand(args: string[]) {
|
|
|
166
166
|
console.log(pc.dim(" Run 'bun install' manually to install dependencies.\n"));
|
|
167
167
|
} else {
|
|
168
168
|
console.log(pc.green("\n✓ Dependencies installed\n"));
|
|
169
|
+
|
|
170
|
+
// Copy CLAUDE.md and docs/ from @donkeylabs/server to project root
|
|
171
|
+
await copyDocsFromServer(targetDir);
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
// Ask about MCP setup
|
|
@@ -319,3 +322,31 @@ async function copyDirectory(src: string, dest: string): Promise<void> {
|
|
|
319
322
|
}
|
|
320
323
|
}
|
|
321
324
|
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Copy CLAUDE.md and docs/ from @donkeylabs/server to project root
|
|
328
|
+
* for AI-assisted development
|
|
329
|
+
*/
|
|
330
|
+
async function copyDocsFromServer(targetDir: string): Promise<void> {
|
|
331
|
+
const serverPkgPath = join(targetDir, "node_modules", "@donkeylabs", "server");
|
|
332
|
+
|
|
333
|
+
if (!existsSync(serverPkgPath)) {
|
|
334
|
+
return; // Server package not installed
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Copy CLAUDE.md
|
|
338
|
+
const claudeMdSrc = join(serverPkgPath, "CLAUDE.md");
|
|
339
|
+
if (existsSync(claudeMdSrc)) {
|
|
340
|
+
const claudeMdDest = join(targetDir, "CLAUDE.md");
|
|
341
|
+
await copyFile(claudeMdSrc, claudeMdDest);
|
|
342
|
+
console.log(pc.green(" Created:"), "CLAUDE.md (AI instructions)");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Copy docs/ directory
|
|
346
|
+
const docsSrc = join(serverPkgPath, "docs");
|
|
347
|
+
if (existsSync(docsSrc)) {
|
|
348
|
+
const docsDest = join(targetDir, "docs");
|
|
349
|
+
await copyDirectory(docsSrc, docsDest);
|
|
350
|
+
console.log(pc.green(" Created:"), "docs/ (detailed documentation)");
|
|
351
|
+
}
|
|
352
|
+
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { db } from "./db";
|
|
2
1
|
import { AppServer, createRouter } from "@donkeylabs/server";
|
|
2
|
+
import { Kysely } from "kysely";
|
|
3
|
+
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
|
4
|
+
import { Database } from "bun:sqlite";
|
|
3
5
|
import { healthRouter } from "./routes/health";
|
|
4
6
|
import { statsPlugin } from "./plugins/stats";
|
|
5
7
|
|
|
8
|
+
// Simple in-memory database
|
|
9
|
+
const db = new Kysely<{}>({
|
|
10
|
+
dialect: new BunSqliteDialect({ database: new Database(":memory:") }),
|
|
11
|
+
});
|
|
12
|
+
|
|
6
13
|
const server = new AppServer({
|
|
7
14
|
port: Number(process.env.PORT) || 3000,
|
|
8
15
|
db,
|
|
@@ -26,12 +33,22 @@ export function createApi(baseUrl: string, options?: ApiClientOptions) {
|
|
|
26
33
|
// Register plugins
|
|
27
34
|
server.registerPlugin(statsPlugin);
|
|
28
35
|
|
|
29
|
-
const api = createRouter("api")
|
|
36
|
+
const api = createRouter("api");
|
|
30
37
|
// Register routes
|
|
31
38
|
api.router(healthRouter);
|
|
32
39
|
|
|
40
|
+
server.use(api);
|
|
33
41
|
|
|
42
|
+
// Handle DONKEYLABS_GENERATE mode for CLI type generation
|
|
43
|
+
if (process.env.DONKEYLABS_GENERATE === "1") {
|
|
44
|
+
const routes = api.getRoutes().map((route) => ({
|
|
45
|
+
name: route.name,
|
|
46
|
+
handler: route.handler || "typed",
|
|
47
|
+
inputType: route.input ? "(generated)" : undefined,
|
|
48
|
+
outputType: route.output ? "(generated)" : undefined,
|
|
49
|
+
}));
|
|
50
|
+
console.log(JSON.stringify({ routes }));
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
34
53
|
|
|
35
|
-
|
|
36
|
-
server.use(api);
|
|
37
54
|
await server.start();
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"vite": "^7.2.6"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@donkeylabs/cli": "^0.4.
|
|
28
|
-
"@donkeylabs/adapter-sveltekit": "^0.4.
|
|
29
|
-
"@donkeylabs/server": "^0.4.
|
|
27
|
+
"@donkeylabs/cli": "^0.4.4",
|
|
28
|
+
"@donkeylabs/adapter-sveltekit": "^0.4.4",
|
|
29
|
+
"@donkeylabs/server": "^0.4.4",
|
|
30
30
|
"bits-ui": "^2.15.4",
|
|
31
31
|
"clsx": "^2.1.1",
|
|
32
32
|
"kysely": "^0.27.6",
|
|
@@ -4,17 +4,7 @@ import { Kysely } from "kysely";
|
|
|
4
4
|
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
|
5
5
|
import { Database } from "bun:sqlite";
|
|
6
6
|
import { demoPlugin } from "./plugins/demo";
|
|
7
|
-
|
|
8
|
-
// Import routes
|
|
9
|
-
import {
|
|
10
|
-
counterRoutes,
|
|
11
|
-
cacheRoutes,
|
|
12
|
-
jobsRoutes,
|
|
13
|
-
cronRoutes,
|
|
14
|
-
ratelimitRoutes,
|
|
15
|
-
eventsRoutes,
|
|
16
|
-
sseRoutes,
|
|
17
|
-
} from "./routes";
|
|
7
|
+
import demoRoutes from "./routes/demo";
|
|
18
8
|
|
|
19
9
|
// Simple in-memory database
|
|
20
10
|
const db = new Kysely<{}>({
|
|
@@ -33,13 +23,17 @@ export const server = new AppServer({
|
|
|
33
23
|
// Register plugin
|
|
34
24
|
server.registerPlugin(demoPlugin);
|
|
35
25
|
|
|
36
|
-
// Register
|
|
37
|
-
server.use(
|
|
38
|
-
server.use(cacheRoutes);
|
|
39
|
-
server.use(jobsRoutes);
|
|
40
|
-
server.use(cronRoutes);
|
|
41
|
-
server.use(ratelimitRoutes);
|
|
42
|
-
server.use(eventsRoutes);
|
|
43
|
-
server.use(sseRoutes);
|
|
44
|
-
|
|
26
|
+
// Register routes
|
|
27
|
+
server.use(demoRoutes);
|
|
45
28
|
|
|
29
|
+
// Handle DONKEYLABS_GENERATE mode for CLI type generation
|
|
30
|
+
if (process.env.DONKEYLABS_GENERATE === "1") {
|
|
31
|
+
const routes = demoRoutes.getRoutes().map((route) => ({
|
|
32
|
+
name: route.name,
|
|
33
|
+
handler: route.handler || "typed",
|
|
34
|
+
inputType: route.input ? "(generated)" : undefined,
|
|
35
|
+
outputType: route.output ? "(generated)" : undefined,
|
|
36
|
+
}));
|
|
37
|
+
console.log(JSON.stringify({ routes }));
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Router - Showcases @donkeylabs/server core features
|
|
3
|
+
*
|
|
4
|
+
* Routes delegate to the demo plugin service which handles
|
|
5
|
+
* the core service integrations with proper types.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
const demo = createRouter("api");
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// COUNTER - Uses plugin service for state
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
demo.route("counter.get").typed(
|
|
18
|
+
defineRoute({
|
|
19
|
+
output: z.object({ count: z.number() }),
|
|
20
|
+
handle: async (_, ctx) => {
|
|
21
|
+
return { count: ctx.plugins.demo.getCounter() };
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
demo.route("counter.increment").typed(
|
|
27
|
+
defineRoute({
|
|
28
|
+
output: z.object({ count: z.number() }),
|
|
29
|
+
handle: async (_, ctx) => {
|
|
30
|
+
return { count: ctx.plugins.demo.increment() };
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
demo.route("counter.decrement").typed(
|
|
36
|
+
defineRoute({
|
|
37
|
+
output: z.object({ count: z.number() }),
|
|
38
|
+
handle: async (_, ctx) => {
|
|
39
|
+
return { count: ctx.plugins.demo.decrement() };
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
demo.route("counter.reset").typed(
|
|
45
|
+
defineRoute({
|
|
46
|
+
output: z.object({ count: z.number() }),
|
|
47
|
+
handle: async (_, ctx) => {
|
|
48
|
+
return { count: ctx.plugins.demo.reset() };
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// =============================================================================
|
|
54
|
+
// CACHE - In-memory caching (via plugin service)
|
|
55
|
+
// =============================================================================
|
|
56
|
+
|
|
57
|
+
demo.route("cache.set").typed(
|
|
58
|
+
defineRoute({
|
|
59
|
+
input: z.object({
|
|
60
|
+
key: z.string(),
|
|
61
|
+
value: z.any(),
|
|
62
|
+
ttl: z.number().optional(),
|
|
63
|
+
}),
|
|
64
|
+
output: z.object({ success: z.boolean() }),
|
|
65
|
+
handle: async (input, ctx) => {
|
|
66
|
+
return ctx.plugins.demo.cacheSet(input.key, input.value, input.ttl);
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
demo.route("cache.get").typed(
|
|
72
|
+
defineRoute({
|
|
73
|
+
input: z.object({ key: z.string() }),
|
|
74
|
+
output: z.object({ value: z.any().optional(), exists: z.boolean() }),
|
|
75
|
+
handle: async (input, ctx) => {
|
|
76
|
+
return ctx.plugins.demo.cacheGet(input.key);
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
demo.route("cache.delete").typed(
|
|
82
|
+
defineRoute({
|
|
83
|
+
input: z.object({ key: z.string() }),
|
|
84
|
+
output: z.object({ success: z.boolean() }),
|
|
85
|
+
handle: async (input, ctx) => {
|
|
86
|
+
return ctx.plugins.demo.cacheDelete(input.key);
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
demo.route("cache.keys").typed(
|
|
92
|
+
defineRoute({
|
|
93
|
+
output: z.object({ keys: z.array(z.string()), size: z.number() }),
|
|
94
|
+
handle: async (_, ctx) => {
|
|
95
|
+
return ctx.plugins.demo.cacheKeys();
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// SSE - Server-Sent Events (via plugin service)
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
demo.route("sse.broadcast").typed(
|
|
105
|
+
defineRoute({
|
|
106
|
+
input: z.object({
|
|
107
|
+
channel: z.string().default("events"),
|
|
108
|
+
event: z.string().default("manual"),
|
|
109
|
+
data: z.any(),
|
|
110
|
+
}),
|
|
111
|
+
output: z.object({ success: z.boolean() }),
|
|
112
|
+
handle: async (input, ctx) => {
|
|
113
|
+
const channel = input.channel ?? "events";
|
|
114
|
+
const event = input.event ?? "manual";
|
|
115
|
+
return ctx.plugins.demo.broadcast(channel, event, input.data);
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
demo.route("sse.clients").typed(
|
|
121
|
+
defineRoute({
|
|
122
|
+
output: z.object({ total: z.number(), byChannel: z.number() }),
|
|
123
|
+
handle: async (_, ctx) => {
|
|
124
|
+
return ctx.plugins.demo.getSSEClients();
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// =============================================================================
|
|
130
|
+
// JOBS - Background job queue (via plugin service)
|
|
131
|
+
// =============================================================================
|
|
132
|
+
|
|
133
|
+
demo.route("jobs.enqueue").typed(
|
|
134
|
+
defineRoute({
|
|
135
|
+
input: z.object({
|
|
136
|
+
name: z.string().default("demo-job"),
|
|
137
|
+
data: z.record(z.any()).optional(),
|
|
138
|
+
delay: z.number().optional(),
|
|
139
|
+
}),
|
|
140
|
+
output: z.object({ jobId: z.string() }),
|
|
141
|
+
handle: async (input, ctx) => {
|
|
142
|
+
const name = input.name ?? "demo-job";
|
|
143
|
+
return ctx.plugins.demo.enqueueJob(name, input.data || {}, input.delay);
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
demo.route("jobs.stats").typed(
|
|
149
|
+
defineRoute({
|
|
150
|
+
output: z.object({
|
|
151
|
+
pending: z.number(),
|
|
152
|
+
running: z.number(),
|
|
153
|
+
completed: z.number(),
|
|
154
|
+
}),
|
|
155
|
+
handle: async (_, ctx) => {
|
|
156
|
+
return ctx.plugins.demo.getJobStats();
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// EVENTS - Pub/sub system (via plugin service)
|
|
163
|
+
// =============================================================================
|
|
164
|
+
|
|
165
|
+
demo.route("events.emit").typed(
|
|
166
|
+
defineRoute({
|
|
167
|
+
input: z.object({
|
|
168
|
+
event: z.string(),
|
|
169
|
+
data: z.record(z.any()).optional(),
|
|
170
|
+
}),
|
|
171
|
+
output: z.object({ success: z.boolean() }),
|
|
172
|
+
handle: async (input, ctx) => {
|
|
173
|
+
return ctx.plugins.demo.emitEvent(input.event, input.data || {});
|
|
174
|
+
},
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// =============================================================================
|
|
179
|
+
// RATE LIMITING (via plugin service)
|
|
180
|
+
// =============================================================================
|
|
181
|
+
|
|
182
|
+
demo.route("ratelimit.check").typed(
|
|
183
|
+
defineRoute({
|
|
184
|
+
input: z.object({
|
|
185
|
+
key: z.string(),
|
|
186
|
+
limit: z.number().default(10),
|
|
187
|
+
window: z.number().default(60),
|
|
188
|
+
}),
|
|
189
|
+
output: z.object({
|
|
190
|
+
allowed: z.boolean(),
|
|
191
|
+
remaining: z.number(),
|
|
192
|
+
limit: z.number(),
|
|
193
|
+
resetAt: z.date(),
|
|
194
|
+
}),
|
|
195
|
+
handle: async (input, ctx) => {
|
|
196
|
+
const limit = input.limit ?? 10;
|
|
197
|
+
const window = input.window ?? 60;
|
|
198
|
+
return ctx.plugins.demo.checkRateLimit(input.key, limit, window * 1000);
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
demo.route("ratelimit.reset").typed(
|
|
204
|
+
defineRoute({
|
|
205
|
+
input: z.object({ key: z.string() }),
|
|
206
|
+
output: z.object({ success: z.boolean() }),
|
|
207
|
+
handle: async (input, ctx) => {
|
|
208
|
+
return ctx.plugins.demo.resetRateLimit(input.key);
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// =============================================================================
|
|
214
|
+
// CRON - Scheduled tasks info (via plugin service)
|
|
215
|
+
// =============================================================================
|
|
216
|
+
|
|
217
|
+
demo.route("cron.list").typed(
|
|
218
|
+
defineRoute({
|
|
219
|
+
output: z.object({
|
|
220
|
+
tasks: z.array(
|
|
221
|
+
z.object({
|
|
222
|
+
id: z.string(),
|
|
223
|
+
name: z.string(),
|
|
224
|
+
expression: z.string(),
|
|
225
|
+
enabled: z.boolean(),
|
|
226
|
+
lastRun: z.string().optional(),
|
|
227
|
+
nextRun: z.string().optional(),
|
|
228
|
+
})
|
|
229
|
+
),
|
|
230
|
+
}),
|
|
231
|
+
handle: async (_, ctx) => {
|
|
232
|
+
return { tasks: ctx.plugins.demo.getCronTasks() };
|
|
233
|
+
},
|
|
234
|
+
})
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
export default demo;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class DeleteCacheHandler implements Handler<Routes.Api.Cache.Delete> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Cache.Delete.Input): Routes.Api.Cache.Delete.Output {
|
|
13
|
-
return this.ctx.plugins.demo.cacheDelete(input.key);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class GetCacheHandler implements Handler<Routes.Api.Cache.Get> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Cache.Get.Input): Routes.Api.Cache.Get.Output {
|
|
13
|
-
return this.ctx.plugins.demo.cacheGet(input.key);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class KeysCacheHandler implements Handler<Routes.Api.Cache.Keys> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Cache.Keys.Input): Routes.Api.Cache.Keys.Output {
|
|
13
|
-
return this.ctx.plugins.demo.cacheKeys();
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class SetCacheHandler implements Handler<Routes.Api.Cache.Set> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Cache.Set.Input): Routes.Api.Cache.Set.Output {
|
|
13
|
-
return this.ctx.plugins.demo.cacheSet(input.key, input.value, input.ttl);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { SetCacheHandler } from "./handlers/set";
|
|
5
|
-
import { GetCacheHandler } from "./handlers/get";
|
|
6
|
-
import { DeleteCacheHandler } from "./handlers/delete";
|
|
7
|
-
import { KeysCacheHandler } from "./handlers/keys";
|
|
8
|
-
|
|
9
|
-
const router = createRouter("api");
|
|
10
|
-
|
|
11
|
-
router.route("cache.set").typed(
|
|
12
|
-
defineRoute({
|
|
13
|
-
input: z.object({
|
|
14
|
-
key: z.string(),
|
|
15
|
-
value: z.any(),
|
|
16
|
-
ttl: z.number().optional()
|
|
17
|
-
}),
|
|
18
|
-
output: z.object({ success: z.boolean() }),
|
|
19
|
-
handle: SetCacheHandler,
|
|
20
|
-
})
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
router.route("cache.get").typed(
|
|
24
|
-
defineRoute({
|
|
25
|
-
input: z.object({ key: z.string() }),
|
|
26
|
-
output: z.object({ value: z.any().optional(), exists: z.boolean() }),
|
|
27
|
-
handle: GetCacheHandler,
|
|
28
|
-
})
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
router.route("cache.delete").typed(
|
|
32
|
-
defineRoute({
|
|
33
|
-
input: z.object({ key: z.string() }),
|
|
34
|
-
output: z.object({ success: z.boolean() }),
|
|
35
|
-
handle: DeleteCacheHandler,
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
router.route("cache.keys").typed(
|
|
40
|
-
defineRoute({
|
|
41
|
-
output: z.object({ keys: z.array(z.string()) }),
|
|
42
|
-
handle: KeysCacheHandler,
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
export default router;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class DecrementCounterHandler implements Handler<Routes.Api.Counter.Decrement> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Counter.Decrement.Input): Routes.Api.Counter.Decrement.Output {
|
|
13
|
-
return {
|
|
14
|
-
count: this.ctx.plugins.demo.decrement(),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class GetCounterHandler implements Handler<Routes.Api.Counter.Get> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Counter.Get.Input): Routes.Api.Counter.Get.Output {
|
|
13
|
-
return {
|
|
14
|
-
count: this.ctx.plugins.demo.getCounter(),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class IncrementCounterHandler implements Handler<Routes.Api.Counter.Increment> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Counter.Increment.Input): Routes.Api.Counter.Increment.Output {
|
|
13
|
-
return {
|
|
14
|
-
count: this.ctx.plugins.demo.increment(),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class ResetCounterHandler implements Handler<Routes.Api.Counter.Reset> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Counter.Reset.Input): Routes.Api.Counter.Reset.Output {
|
|
13
|
-
return {
|
|
14
|
-
count: this.ctx.plugins.demo.reset(),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { GetCounterHandler } from "./handlers/get";
|
|
5
|
-
import { IncrementCounterHandler } from "./handlers/increment";
|
|
6
|
-
import { DecrementCounterHandler } from "./handlers/decrement";
|
|
7
|
-
import { ResetCounterHandler } from "./handlers/reset";
|
|
8
|
-
|
|
9
|
-
const router = createRouter("api");
|
|
10
|
-
|
|
11
|
-
router.route("counter.get").typed(
|
|
12
|
-
defineRoute({
|
|
13
|
-
output: z.object({ count: z.number() }),
|
|
14
|
-
handle: GetCounterHandler,
|
|
15
|
-
})
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
router.route("counter.increment").typed(
|
|
19
|
-
defineRoute({
|
|
20
|
-
output: z.object({ count: z.number() }),
|
|
21
|
-
handle: IncrementCounterHandler,
|
|
22
|
-
})
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
router.route("counter.decrement").typed(
|
|
26
|
-
defineRoute({
|
|
27
|
-
output: z.object({ count: z.number() }),
|
|
28
|
-
handle: DecrementCounterHandler,
|
|
29
|
-
})
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
router.route("counter.reset").typed(
|
|
33
|
-
defineRoute({
|
|
34
|
-
output: z.object({ count: z.number() }),
|
|
35
|
-
handle: ResetCounterHandler,
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
export default router;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class ListCronHandler implements Handler<Routes.Api.Cron.List> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Cron.List.Input): Routes.Api.Cron.List.Output {
|
|
13
|
-
return {
|
|
14
|
-
tasks: this.ctx.plugins.demo.getCronTasks()
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { ListCronHandler } from "./handlers/list";
|
|
5
|
-
|
|
6
|
-
const router = createRouter("api");
|
|
7
|
-
|
|
8
|
-
router.route("cron.list").typed(
|
|
9
|
-
defineRoute({
|
|
10
|
-
output: z.object({
|
|
11
|
-
tasks: z.array(z.object({
|
|
12
|
-
id: z.string(),
|
|
13
|
-
name: z.string(),
|
|
14
|
-
expression: z.string(),
|
|
15
|
-
enabled: z.boolean(),
|
|
16
|
-
lastRun: z.string().optional(),
|
|
17
|
-
nextRun: z.string().optional()
|
|
18
|
-
}))
|
|
19
|
-
}),
|
|
20
|
-
handle: ListCronHandler,
|
|
21
|
-
})
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
export default router;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class EmitEventHandler implements Handler<Routes.Api.Events.Emit> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Events.Emit.Input): Routes.Api.Events.Emit.Output {
|
|
13
|
-
return this.ctx.plugins.demo.emitEvent(input.event!, input.data);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { EmitEventHandler } from "./handlers/emit";
|
|
5
|
-
|
|
6
|
-
const router = createRouter("api");
|
|
7
|
-
|
|
8
|
-
router.route("events.emit").typed(
|
|
9
|
-
defineRoute({
|
|
10
|
-
input: z.object({
|
|
11
|
-
event: z.string().default("demo.test"),
|
|
12
|
-
data: z.any().default({ test: true })
|
|
13
|
-
}),
|
|
14
|
-
output: z.object({ success: z.boolean() }),
|
|
15
|
-
handle: EmitEventHandler,
|
|
16
|
-
})
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
export default router;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// Export all route modules
|
|
2
|
-
export { default as counterRoutes } from "./counter";
|
|
3
|
-
export { default as cacheRoutes } from "./cache";
|
|
4
|
-
export { default as jobsRoutes } from "./jobs";
|
|
5
|
-
export { default as cronRoutes } from "./cron";
|
|
6
|
-
export { default as ratelimitRoutes } from "./ratelimit";
|
|
7
|
-
export { default as eventsRoutes } from "./events";
|
|
8
|
-
export { default as sseRoutes } from "./sse";
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class EnqueueJobHandler implements Handler<Routes.Api.Jobs.Enqueue> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Jobs.Enqueue.Input): Routes.Api.Jobs.Enqueue.Output {
|
|
13
|
-
return this.ctx.plugins.demo.enqueueJob(input.name!, input.data, input.delay);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class StatsJobHandler implements Handler<Routes.Api.Jobs.Stats> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Jobs.Stats.Input): Routes.Api.Jobs.Stats.Output {
|
|
13
|
-
return this.ctx.plugins.demo.getJobStats();
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { EnqueueJobHandler } from "./handlers/enqueue";
|
|
5
|
-
import { StatsJobHandler } from "./handlers/stats";
|
|
6
|
-
|
|
7
|
-
const router = createRouter("api");
|
|
8
|
-
|
|
9
|
-
router.route("jobs.enqueue").typed(
|
|
10
|
-
defineRoute({
|
|
11
|
-
input: z.object({
|
|
12
|
-
name: z.string().default("demo-job"),
|
|
13
|
-
data: z.any().default({}),
|
|
14
|
-
delay: z.number().optional()
|
|
15
|
-
}),
|
|
16
|
-
output: z.object({ jobId: z.string() }),
|
|
17
|
-
handle: EnqueueJobHandler,
|
|
18
|
-
})
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
router.route("jobs.stats").typed(
|
|
22
|
-
defineRoute({
|
|
23
|
-
output: z.object({ pending: z.number(), running: z.number(), completed: z.number() }),
|
|
24
|
-
handle: StatsJobHandler,
|
|
25
|
-
})
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
export default router;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class CheckRateLimitHandler implements Handler<Routes.Api.Ratelimit.Check> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Ratelimit.Check.Input): Routes.Api.Ratelimit.Check.Output {
|
|
13
|
-
return this.ctx.plugins.demo.checkRateLimit(input.key!, input.limit!, input.window!);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class ResetRateLimitHandler implements Handler<Routes.Api.Ratelimit.Reset> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Ratelimit.Reset.Input): Routes.Api.Ratelimit.Reset.Output {
|
|
13
|
-
return this.ctx.plugins.demo.resetRateLimit(input.key!);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { CheckRateLimitHandler } from "./handlers/check";
|
|
5
|
-
import { ResetRateLimitHandler } from "./handlers/reset";
|
|
6
|
-
|
|
7
|
-
const router = createRouter("api");
|
|
8
|
-
|
|
9
|
-
router.route("ratelimit.check").typed(
|
|
10
|
-
defineRoute({
|
|
11
|
-
input: z.object({
|
|
12
|
-
key: z.string().default("demo"),
|
|
13
|
-
limit: z.number().default(5),
|
|
14
|
-
window: z.number().default(60000)
|
|
15
|
-
}),
|
|
16
|
-
output: z.object({ allowed: z.boolean(), remaining: z.number(), resetAt: z.date() }),
|
|
17
|
-
handle: CheckRateLimitHandler,
|
|
18
|
-
})
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
router.route("ratelimit.reset").typed(
|
|
22
|
-
defineRoute({
|
|
23
|
-
input: z.object({ key: z.string().default("demo") }),
|
|
24
|
-
output: z.object({ success: z.boolean() }),
|
|
25
|
-
handle: ResetRateLimitHandler,
|
|
26
|
-
})
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
export default router;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class BroadcastSseHandler implements Handler<Routes.Api.Sse.Broadcast> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Sse.Broadcast.Input): Routes.Api.Sse.Broadcast.Output {
|
|
13
|
-
return this.ctx.plugins.demo.broadcast(input.channel!, input.event!, input.data);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Handler, AppContext } from "$lib/api";
|
|
3
|
-
import type { Routes } from "$lib/api";
|
|
4
|
-
|
|
5
|
-
export class ClientsSseHandler implements Handler<Routes.Api.Sse.Clients> {
|
|
6
|
-
ctx: AppContext;
|
|
7
|
-
|
|
8
|
-
constructor(ctx: AppContext) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
handle(input: Routes.Api.Sse.Clients.Input): Routes.Api.Sse.Clients.Output {
|
|
13
|
-
return this.ctx.plugins.demo.getSSEClients();
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { createRouter, defineRoute } from "@donkeylabs/server";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { BroadcastSseHandler } from "./handlers/broadcast";
|
|
5
|
-
import { ClientsSseHandler } from "./handlers/clients";
|
|
6
|
-
|
|
7
|
-
const router = createRouter("api");
|
|
8
|
-
|
|
9
|
-
router.route("sse.broadcast").typed(
|
|
10
|
-
defineRoute({
|
|
11
|
-
input: z.object({
|
|
12
|
-
channel: z.string().default("events"),
|
|
13
|
-
event: z.string().default("manual"),
|
|
14
|
-
data: z.any()
|
|
15
|
-
}),
|
|
16
|
-
output: z.object({ success: z.boolean(), recipients: z.number() }),
|
|
17
|
-
handle: BroadcastSseHandler,
|
|
18
|
-
})
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
router.route("sse.clients").typed(
|
|
22
|
-
defineRoute({
|
|
23
|
-
output: z.object({ total: z.number(), byChannel: z.number() }),
|
|
24
|
-
handle: ClientsSseHandler,
|
|
25
|
-
})
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
export default router;
|