@reverso/api 0.1.0

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.
Files changed (74) hide show
  1. package/README.md +47 -0
  2. package/dist/index.d.ts +41 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +56 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/plugins/auth.d.ts +72 -0
  7. package/dist/plugins/auth.d.ts.map +1 -0
  8. package/dist/plugins/auth.js +173 -0
  9. package/dist/plugins/auth.js.map +1 -0
  10. package/dist/plugins/database.d.ts +19 -0
  11. package/dist/plugins/database.d.ts.map +1 -0
  12. package/dist/plugins/database.js +23 -0
  13. package/dist/plugins/database.js.map +1 -0
  14. package/dist/plugins/index.d.ts +5 -0
  15. package/dist/plugins/index.d.ts.map +1 -0
  16. package/dist/plugins/index.js +5 -0
  17. package/dist/plugins/index.js.map +1 -0
  18. package/dist/routes/auth.d.ts +6 -0
  19. package/dist/routes/auth.d.ts.map +1 -0
  20. package/dist/routes/auth.js +258 -0
  21. package/dist/routes/auth.js.map +1 -0
  22. package/dist/routes/content.d.ts +8 -0
  23. package/dist/routes/content.d.ts.map +1 -0
  24. package/dist/routes/content.js +339 -0
  25. package/dist/routes/content.js.map +1 -0
  26. package/dist/routes/forms.d.ts +8 -0
  27. package/dist/routes/forms.d.ts.map +1 -0
  28. package/dist/routes/forms.js +953 -0
  29. package/dist/routes/forms.js.map +1 -0
  30. package/dist/routes/index.d.ts +19 -0
  31. package/dist/routes/index.d.ts.map +1 -0
  32. package/dist/routes/index.js +31 -0
  33. package/dist/routes/index.js.map +1 -0
  34. package/dist/routes/media.d.ts +8 -0
  35. package/dist/routes/media.d.ts.map +1 -0
  36. package/dist/routes/media.js +400 -0
  37. package/dist/routes/media.js.map +1 -0
  38. package/dist/routes/pages.d.ts +8 -0
  39. package/dist/routes/pages.d.ts.map +1 -0
  40. package/dist/routes/pages.js +220 -0
  41. package/dist/routes/pages.js.map +1 -0
  42. package/dist/routes/redirects.d.ts +8 -0
  43. package/dist/routes/redirects.d.ts.map +1 -0
  44. package/dist/routes/redirects.js +462 -0
  45. package/dist/routes/redirects.js.map +1 -0
  46. package/dist/routes/schema.d.ts +8 -0
  47. package/dist/routes/schema.d.ts.map +1 -0
  48. package/dist/routes/schema.js +151 -0
  49. package/dist/routes/schema.js.map +1 -0
  50. package/dist/routes/sitemap.d.ts +8 -0
  51. package/dist/routes/sitemap.d.ts.map +1 -0
  52. package/dist/routes/sitemap.js +144 -0
  53. package/dist/routes/sitemap.js.map +1 -0
  54. package/dist/server.d.ts +47 -0
  55. package/dist/server.d.ts.map +1 -0
  56. package/dist/server.js +218 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/types.d.ts +91 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +5 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/index.d.ts +5 -0
  63. package/dist/utils/index.d.ts.map +1 -0
  64. package/dist/utils/index.js +5 -0
  65. package/dist/utils/index.js.map +1 -0
  66. package/dist/utils/security.d.ts +32 -0
  67. package/dist/utils/security.d.ts.map +1 -0
  68. package/dist/utils/security.js +154 -0
  69. package/dist/utils/security.js.map +1 -0
  70. package/dist/validation.d.ts +402 -0
  71. package/dist/validation.d.ts.map +1 -0
  72. package/dist/validation.js +308 -0
  73. package/dist/validation.js.map +1 -0
  74. package/package.json +76 -0
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @reverso/api
2
+
3
+ REST API server for Reverso CMS using Fastify.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @reverso/api
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { createServer } from '@reverso/api';
15
+
16
+ const server = await createServer({
17
+ database: './data/reverso.db',
18
+ port: 4000,
19
+ });
20
+
21
+ await server.listen();
22
+ ```
23
+
24
+ ## Endpoints
25
+
26
+ - `GET /schema` - Get project schema
27
+ - `GET /pages` - List all pages
28
+ - `GET /pages/:slug` - Get page details
29
+ - `GET /content/:path` - Get content by path
30
+ - `PUT /content/:path` - Update content
31
+ - `GET /media` - List media files
32
+ - `POST /media` - Upload media
33
+
34
+ ## Features
35
+
36
+ - **Fastify**: High-performance web framework
37
+ - **Security**: Rate limiting, CORS, CSRF protection, Helmet
38
+ - **Authentication**: Session-based auth with brute force protection
39
+ - **File Uploads**: Multipart handling with validation
40
+
41
+ ## Documentation
42
+
43
+ See [https://reverso.dev/docs/packages/api](https://reverso.dev/docs/packages/api)
44
+
45
+ ## License
46
+
47
+ MIT
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @reverso/api
3
+ *
4
+ * REST and GraphQL API server for Reverso CMS using Fastify.
5
+ * Provides endpoints for content management, media, forms, and authentication.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createServer, startServer, registerRoutes } from '@reverso/api';
10
+ * import { databasePlugin } from '@reverso/api/plugins';
11
+ *
12
+ * const server = await createServer({ port: 3001 });
13
+ *
14
+ * // Register database
15
+ * await server.register(databasePlugin, { url: '.reverso/dev.db' });
16
+ *
17
+ * // Register routes
18
+ * await registerRoutes(server);
19
+ *
20
+ * // Start server
21
+ * await startServer(server);
22
+ * ```
23
+ */
24
+ export declare const VERSION = "0.0.0";
25
+ export { createServer, startServer, stopServer, type ServerConfig, type CorsOptions, } from './server.js';
26
+ export { databasePlugin } from './plugins/index.js';
27
+ export { registerRoutes, schemaRoutes, pagesRoutes, contentRoutes, mediaRoutes, } from './routes/index.js';
28
+ export type { ApiRequest, ApiError, ApiResponse, AuthUser, SchemaSyncBody, ContentUpdateBody, BulkContentUpdateBody, MediaUploadResult, PaginationParams, ListResponse, } from './types.js';
29
+ /**
30
+ * Create and start a fully configured API server.
31
+ * Convenience function that combines createServer, registerDatabase, and registerRoutes.
32
+ */
33
+ export declare function createApiServer(options: {
34
+ port?: number;
35
+ host?: string;
36
+ databaseUrl: string;
37
+ prefix?: string;
38
+ cors?: boolean;
39
+ logger?: boolean;
40
+ }): Promise<import("fastify").FastifyInstance<import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>>;
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,UAAU,EACV,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,oQAyBA"}
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @reverso/api
3
+ *
4
+ * REST and GraphQL API server for Reverso CMS using Fastify.
5
+ * Provides endpoints for content management, media, forms, and authentication.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createServer, startServer, registerRoutes } from '@reverso/api';
10
+ * import { databasePlugin } from '@reverso/api/plugins';
11
+ *
12
+ * const server = await createServer({ port: 3001 });
13
+ *
14
+ * // Register database
15
+ * await server.register(databasePlugin, { url: '.reverso/dev.db' });
16
+ *
17
+ * // Register routes
18
+ * await registerRoutes(server);
19
+ *
20
+ * // Start server
21
+ * await startServer(server);
22
+ * ```
23
+ */
24
+ export const VERSION = '0.0.0';
25
+ // Server exports
26
+ export { createServer, startServer, stopServer, } from './server.js';
27
+ // Plugin exports
28
+ export { databasePlugin } from './plugins/index.js';
29
+ // Route exports
30
+ export { registerRoutes, schemaRoutes, pagesRoutes, contentRoutes, mediaRoutes, } from './routes/index.js';
31
+ /**
32
+ * Create and start a fully configured API server.
33
+ * Convenience function that combines createServer, registerDatabase, and registerRoutes.
34
+ */
35
+ export async function createApiServer(options) {
36
+ const { createServer, startServer } = await import('./server.js');
37
+ const { databasePlugin } = await import('./plugins/index.js');
38
+ const { registerRoutes } = await import('./routes/index.js');
39
+ const { createDatabaseSchema } = await import('@reverso/db');
40
+ // Create database if needed
41
+ await createDatabaseSchema(options.databaseUrl);
42
+ // Create server
43
+ const server = await createServer({
44
+ port: options.port,
45
+ host: options.host,
46
+ cors: options.cors,
47
+ logger: options.logger,
48
+ prefix: options.prefix,
49
+ });
50
+ // Register database plugin
51
+ await server.register(databasePlugin, { url: options.databaseUrl });
52
+ // Register routes
53
+ await registerRoutes(server, options.prefix);
54
+ return server;
55
+ }
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,iBAAiB;AACjB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,UAAU,GAGX,MAAM,aAAa,CAAC;AAErB,iBAAiB;AACjB,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,gBAAgB;AAChB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAgB3B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAOrC;IACC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAClE,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC9D,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC7D,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAE7D,4BAA4B;IAC5B,MAAM,oBAAoB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAEhD,gBAAgB;IAChB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,2BAA2B;IAC3B,MAAM,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAEpE,kBAAkB;IAClB,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Authentication plugin for Reverso API.
3
+ *
4
+ * Supports:
5
+ * - API Key authentication (for CI/CD, scripts, MCP)
6
+ * - Session token authentication (for admin panel)
7
+ */
8
+ import type { FastifyInstance } from 'fastify';
9
+ export interface AuthPluginOptions {
10
+ /** API key for programmatic access */
11
+ apiKey?: string;
12
+ /** Skip auth for these paths (regex patterns) */
13
+ publicPaths?: RegExp[];
14
+ /** Enable auth (default: true in production) */
15
+ enabled?: boolean;
16
+ }
17
+ export interface AuthUser {
18
+ id: string;
19
+ email?: string;
20
+ role: 'admin' | 'editor' | 'viewer';
21
+ authMethod: 'api_key' | 'session';
22
+ }
23
+ declare module 'fastify' {
24
+ interface FastifyRequest {
25
+ user?: AuthUser;
26
+ }
27
+ }
28
+ declare function authPlugin(fastify: FastifyInstance, options: AuthPluginOptions): Promise<void>;
29
+ declare const _default: typeof authPlugin;
30
+ export default _default;
31
+ declare module 'fastify' {
32
+ interface FastifyInstance {
33
+ requireAuth: (roles?: AuthUser['role'][]) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
34
+ }
35
+ }
36
+ /**
37
+ * Role hierarchy for permission checks.
38
+ * admin > editor > viewer
39
+ */
40
+ export declare const ROLE_HIERARCHY: Record<AuthUser['role'], number>;
41
+ /**
42
+ * Check if user has at least the required role level.
43
+ */
44
+ export declare function hasMinimumRole(userRole: AuthUser['role'], requiredRole: AuthUser['role']): boolean;
45
+ /**
46
+ * Permission definitions for different actions.
47
+ */
48
+ export declare const PERMISSIONS: {
49
+ readonly 'content:read': AuthUser["role"][];
50
+ readonly 'content:write': AuthUser["role"][];
51
+ readonly 'content:delete': AuthUser["role"][];
52
+ readonly 'content:publish': AuthUser["role"][];
53
+ readonly 'media:read': AuthUser["role"][];
54
+ readonly 'media:upload': AuthUser["role"][];
55
+ readonly 'media:delete': AuthUser["role"][];
56
+ readonly 'forms:read': AuthUser["role"][];
57
+ readonly 'forms:write': AuthUser["role"][];
58
+ readonly 'forms:delete': AuthUser["role"][];
59
+ readonly 'schema:read': AuthUser["role"][];
60
+ readonly 'schema:sync': AuthUser["role"][];
61
+ readonly 'redirects:read': AuthUser["role"][];
62
+ readonly 'redirects:write': AuthUser["role"][];
63
+ readonly 'redirects:delete': AuthUser["role"][];
64
+ readonly 'users:read': AuthUser["role"][];
65
+ readonly 'users:write': AuthUser["role"][];
66
+ };
67
+ export type Permission = keyof typeof PERMISSIONS;
68
+ /**
69
+ * Check if user has a specific permission.
70
+ */
71
+ export declare function hasPermission(userRole: AuthUser['role'], permission: Permission): boolean;
72
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/plugins/auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAG7E,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpC,UAAU,EAAE,SAAS,GAAG,SAAS,CAAC;CACnC;AAED,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,IAAI,CAAC,EAAE,QAAQ,CAAC;KACjB;CACF;AASD,iBAAe,UAAU,CACvB,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAiHf;;AAED,wBAGG;AAGH,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAC3C,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;KACpB;CACF;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAI3D,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAElG;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;6BAE2B,QAAQ,CAAC,MAAM,CAAC,EAAE;8BAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE;+BAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE;gCACP,QAAQ,CAAC,MAAM,CAAC,EAAE;2BAGb,QAAQ,CAAC,MAAM,CAAC,EAAE;6BAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE;6BAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE;2BAGA,QAAQ,CAAC,MAAM,CAAC,EAAE;4BAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE;6BAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE;4BAGC,QAAQ,CAAC,MAAM,CAAC,EAAE;4BACtC,QAAQ,CAAC,MAAM,CAAC,EAAE;+BAGK,QAAQ,CAAC,MAAM,CAAC,EAAE;gCAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE;iCAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE;2BAGxB,QAAQ,CAAC,MAAM,CAAC,EAAE;4BACjB,QAAQ,CAAC,MAAM,CAAC,EAAE;CACtC,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,WAAW,CAAC;AAElD;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAGzF"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Authentication plugin for Reverso API.
3
+ *
4
+ * Supports:
5
+ * - API Key authentication (for CI/CD, scripts, MCP)
6
+ * - Session token authentication (for admin panel)
7
+ */
8
+ import fp from 'fastify-plugin';
9
+ const DEFAULT_PUBLIC_PATHS = [
10
+ /^\/health$/,
11
+ /^\/uploads\//,
12
+ /^\/sitemap\.xml$/,
13
+ /^\/api\/reverso\/public\//,
14
+ ];
15
+ async function authPlugin(fastify, options) {
16
+ const apiKey = options.apiKey || process.env.REVERSO_API_KEY;
17
+ const isProduction = process.env.NODE_ENV === 'production';
18
+ const enabled = options.enabled ?? isProduction;
19
+ const publicPaths = [...DEFAULT_PUBLIC_PATHS, ...(options.publicPaths || [])];
20
+ // Add auth check hook
21
+ fastify.addHook('onRequest', async (request, reply) => {
22
+ // Skip if auth is disabled (dev mode)
23
+ if (!enabled) {
24
+ request.user = {
25
+ id: 'dev-user',
26
+ email: 'dev@localhost',
27
+ role: 'admin',
28
+ authMethod: 'api_key',
29
+ };
30
+ return;
31
+ }
32
+ // Check if path is public
33
+ const isPublicPath = publicPaths.some((pattern) => pattern.test(request.url));
34
+ if (isPublicPath) {
35
+ return;
36
+ }
37
+ // Try API key authentication
38
+ const authHeader = request.headers.authorization;
39
+ if (authHeader?.startsWith('Bearer ')) {
40
+ const token = authHeader.slice(7);
41
+ // Validate token format to prevent DoS from oversized tokens
42
+ if (!token || token.length < 16 || token.length > 256) {
43
+ return reply.status(401).send({
44
+ success: false,
45
+ error: 'Unauthorized',
46
+ message: 'Invalid token format',
47
+ });
48
+ }
49
+ // Check API key
50
+ if (apiKey && token === apiKey) {
51
+ request.user = {
52
+ id: 'api-key-user',
53
+ role: 'admin',
54
+ authMethod: 'api_key',
55
+ };
56
+ return;
57
+ }
58
+ // Check session token (Better Auth format)
59
+ // Session tokens should be validated against the database
60
+ const db = request.db;
61
+ if (db) {
62
+ try {
63
+ const { getSessionByToken } = await import('@reverso/db');
64
+ const session = await getSessionByToken(db, token);
65
+ if (session && new Date(session.expiresAt).getTime() > Date.now()) {
66
+ // Get role from user record (default to 'viewer' if not set)
67
+ const userRole = session.user?.role || 'viewer';
68
+ request.user = {
69
+ id: session.userId,
70
+ email: session.user?.email,
71
+ role: userRole,
72
+ authMethod: 'session',
73
+ };
74
+ return;
75
+ }
76
+ }
77
+ catch {
78
+ // Session validation failed, continue to error
79
+ }
80
+ }
81
+ }
82
+ // Check X-API-Key header (alternative)
83
+ const xApiKey = request.headers['x-api-key']?.toString();
84
+ if (xApiKey && xApiKey.length >= 16 && xApiKey.length <= 256 && apiKey && xApiKey === apiKey) {
85
+ request.user = {
86
+ id: 'api-key-user',
87
+ role: 'admin',
88
+ authMethod: 'api_key',
89
+ };
90
+ return;
91
+ }
92
+ // No valid authentication
93
+ return reply.status(401).send({
94
+ success: false,
95
+ error: 'Unauthorized',
96
+ message: 'Valid authentication required. Use Bearer token or X-API-Key header.',
97
+ });
98
+ });
99
+ // Decorate with auth helper
100
+ fastify.decorate('requireAuth', (roles) => {
101
+ return async (request, reply) => {
102
+ if (!request.user) {
103
+ return reply.status(401).send({
104
+ success: false,
105
+ error: 'Unauthorized',
106
+ message: 'Authentication required',
107
+ });
108
+ }
109
+ if (roles && !roles.includes(request.user.role)) {
110
+ return reply.status(403).send({
111
+ success: false,
112
+ error: 'Forbidden',
113
+ message: 'Insufficient permissions',
114
+ });
115
+ }
116
+ };
117
+ });
118
+ }
119
+ export default fp(authPlugin, {
120
+ name: 'reverso-auth',
121
+ fastify: '5.x',
122
+ });
123
+ /**
124
+ * Role hierarchy for permission checks.
125
+ * admin > editor > viewer
126
+ */
127
+ export const ROLE_HIERARCHY = {
128
+ admin: 3,
129
+ editor: 2,
130
+ viewer: 1,
131
+ };
132
+ /**
133
+ * Check if user has at least the required role level.
134
+ */
135
+ export function hasMinimumRole(userRole, requiredRole) {
136
+ return ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole];
137
+ }
138
+ /**
139
+ * Permission definitions for different actions.
140
+ */
141
+ export const PERMISSIONS = {
142
+ // Content management
143
+ 'content:read': ['viewer', 'editor', 'admin'],
144
+ 'content:write': ['editor', 'admin'],
145
+ 'content:delete': ['admin'],
146
+ 'content:publish': ['editor', 'admin'],
147
+ // Media management
148
+ 'media:read': ['viewer', 'editor', 'admin'],
149
+ 'media:upload': ['editor', 'admin'],
150
+ 'media:delete': ['admin'],
151
+ // Forms management
152
+ 'forms:read': ['viewer', 'editor', 'admin'],
153
+ 'forms:write': ['editor', 'admin'],
154
+ 'forms:delete': ['admin'],
155
+ // Schema management
156
+ 'schema:read': ['viewer', 'editor', 'admin'],
157
+ 'schema:sync': ['admin'],
158
+ // Redirects management
159
+ 'redirects:read': ['viewer', 'editor', 'admin'],
160
+ 'redirects:write': ['editor', 'admin'],
161
+ 'redirects:delete': ['admin'],
162
+ // User management
163
+ 'users:read': ['admin'],
164
+ 'users:write': ['admin'],
165
+ };
166
+ /**
167
+ * Check if user has a specific permission.
168
+ */
169
+ export function hasPermission(userRole, permission) {
170
+ const allowedRoles = PERMISSIONS[permission];
171
+ return allowedRoles.includes(userRole);
172
+ }
173
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/plugins/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Database plugin for Fastify.
3
+ * Adds db instance to request.
4
+ */
5
+ import { type DatabaseConfig, type DrizzleDatabase } from '@reverso/db';
6
+ import type { FastifyPluginAsync } from 'fastify';
7
+ export interface DatabasePluginOptions extends DatabaseConfig {
8
+ }
9
+ declare const _default: FastifyPluginAsync<DatabasePluginOptions>;
10
+ export default _default;
11
+ declare module 'fastify' {
12
+ interface FastifyInstance {
13
+ db: DrizzleDatabase;
14
+ }
15
+ interface FastifyRequest {
16
+ db: DrizzleDatabase;
17
+ }
18
+ }
19
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/plugins/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAA6B,MAAM,aAAa,CAAC;AACnG,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGnE,MAAM,WAAW,qBAAsB,SAAQ,cAAc;CAAG;;AAqBhE,wBAGG;AAGH,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,EAAE,EAAE,eAAe,CAAC;KACrB;IACD,UAAU,cAAc;QACtB,EAAE,EAAE,eAAe,CAAC;KACrB;CACF"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Database plugin for Fastify.
3
+ * Adds db instance to request.
4
+ */
5
+ import { getDatabase, initDatabase } from '@reverso/db';
6
+ import fp from 'fastify-plugin';
7
+ const databasePlugin = async (fastify, options) => {
8
+ // Initialize database
9
+ const { db } = initDatabase(options);
10
+ // Decorate fastify instance with db
11
+ fastify.decorate('db', db);
12
+ // Add db to each request using addHook
13
+ fastify.addHook('onRequest', async (request) => {
14
+ request.db = db;
15
+ });
16
+ // Log database connection
17
+ fastify.log.info(`Database connected: ${options.url}`);
18
+ };
19
+ export default fp(databasePlugin, {
20
+ name: 'database',
21
+ fastify: '5.x',
22
+ });
23
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/plugins/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA6C,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEnG,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAIhC,MAAM,cAAc,GAA8C,KAAK,EACrE,OAAwB,EACxB,OAA8B,EAC9B,EAAE;IACF,sBAAsB;IACtB,MAAM,EAAE,EAAE,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAErC,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAE3B,uCAAuC;IACvC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5C,OAAe,CAAC,EAAE,GAAG,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AACzD,CAAC,CAAC;AAEF,eAAe,EAAE,CAAC,cAAc,EAAE;IAChC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,KAAK;CACf,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Plugin exports.
3
+ */
4
+ export { default as databasePlugin } from './database.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Plugin exports.
3
+ */
4
+ export { default as databasePlugin } from './database.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Authentication routes.
3
+ */
4
+ import type { FastifyInstance } from 'fastify';
5
+ export default function authRoutes(fastify: FastifyInstance): Promise<void>;
6
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAwC7E,wBAA8B,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA0QhF"}