@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
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Sitemap route.
3
+ * Generates dynamic XML sitemap for SEO.
4
+ */
5
+ import { getFormsByStatus, getPages } from '@reverso/db';
6
+ /**
7
+ * Build XML sitemap from URLs.
8
+ */
9
+ function buildSitemapXml(urls) {
10
+ const urlEntries = urls
11
+ .map((url) => {
12
+ const parts = [` <url>`, ` <loc>${escapeXml(url.loc)}</loc>`];
13
+ if (url.lastmod) {
14
+ parts.push(` <lastmod>${url.lastmod}</lastmod>`);
15
+ }
16
+ if (url.changefreq) {
17
+ parts.push(` <changefreq>${url.changefreq}</changefreq>`);
18
+ }
19
+ if (url.priority !== undefined) {
20
+ parts.push(` <priority>${url.priority.toFixed(1)}</priority>`);
21
+ }
22
+ parts.push(` </url>`);
23
+ return parts.join('\n');
24
+ })
25
+ .join('\n');
26
+ return `<?xml version="1.0" encoding="UTF-8"?>
27
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
28
+ ${urlEntries}
29
+ </urlset>`;
30
+ }
31
+ /**
32
+ * Escape special XML characters.
33
+ */
34
+ function escapeXml(str) {
35
+ return str
36
+ .replace(/&/g, '&amp;')
37
+ .replace(/</g, '&lt;')
38
+ .replace(/>/g, '&gt;')
39
+ .replace(/"/g, '&quot;')
40
+ .replace(/'/g, '&apos;');
41
+ }
42
+ /**
43
+ * Format date for sitemap (ISO 8601 date format).
44
+ */
45
+ function formatDate(date) {
46
+ if (!date)
47
+ return undefined;
48
+ const d = typeof date === 'string' ? new Date(date) : date;
49
+ return d.toISOString().split('T')[0];
50
+ }
51
+ // Cache for sitemap (1 hour)
52
+ let sitemapCache = null;
53
+ const CACHE_TTL = 60 * 60 * 1000; // 1 hour in milliseconds
54
+ const sitemapRoutes = async (fastify) => {
55
+ /**
56
+ * GET /sitemap.xml
57
+ * Generate dynamic sitemap.
58
+ */
59
+ fastify.get('/sitemap.xml', async (request, reply) => {
60
+ try {
61
+ // Check cache
62
+ const now = Date.now();
63
+ if (sitemapCache && now - sitemapCache.timestamp < CACHE_TTL) {
64
+ reply.header('Content-Type', 'application/xml');
65
+ reply.header('Cache-Control', 'public, max-age=3600');
66
+ return sitemapCache.xml;
67
+ }
68
+ const db = request.db;
69
+ // Get base URL from request or config
70
+ const protocol = request.protocol || 'https';
71
+ const host = request.hostname || 'localhost';
72
+ const baseUrl = `${protocol}://${host}`;
73
+ const urls = [];
74
+ // Add homepage
75
+ urls.push({
76
+ loc: baseUrl,
77
+ changefreq: 'daily',
78
+ priority: 1.0,
79
+ });
80
+ // Get all pages
81
+ const pages = await getPages(db);
82
+ for (const page of pages) {
83
+ // Skip home page (already added)
84
+ if (page.slug === 'home' || page.slug === 'index') {
85
+ continue;
86
+ }
87
+ urls.push({
88
+ loc: `${baseUrl}/${page.slug}`,
89
+ lastmod: formatDate(page.updatedAt),
90
+ changefreq: 'weekly',
91
+ priority: 0.8,
92
+ });
93
+ }
94
+ // Get published forms (for public form pages)
95
+ const publishedForms = await getFormsByStatus(db, 'published');
96
+ for (const form of publishedForms) {
97
+ urls.push({
98
+ loc: `${baseUrl}/forms/${form.slug}`,
99
+ lastmod: formatDate(form.updatedAt),
100
+ changefreq: 'monthly',
101
+ priority: 0.6,
102
+ });
103
+ }
104
+ // Build XML
105
+ const xml = buildSitemapXml(urls);
106
+ // Update cache
107
+ sitemapCache = { xml, timestamp: now };
108
+ reply.header('Content-Type', 'application/xml');
109
+ reply.header('Cache-Control', 'public, max-age=3600');
110
+ return xml;
111
+ }
112
+ catch (error) {
113
+ fastify.log.error(error, 'Failed to generate sitemap');
114
+ return reply.status(500).send({
115
+ success: false,
116
+ error: 'Internal error',
117
+ message: 'Failed to generate sitemap',
118
+ });
119
+ }
120
+ });
121
+ /**
122
+ * POST /sitemap/invalidate
123
+ * Invalidate sitemap cache (admin only).
124
+ */
125
+ fastify.post('/sitemap/invalidate', async (request, reply) => {
126
+ try {
127
+ sitemapCache = null;
128
+ return {
129
+ success: true,
130
+ message: 'Sitemap cache invalidated',
131
+ };
132
+ }
133
+ catch (error) {
134
+ fastify.log.error(error, 'Failed to invalidate sitemap cache');
135
+ return reply.status(500).send({
136
+ success: false,
137
+ error: 'Internal error',
138
+ message: 'Failed to invalidate sitemap cache',
139
+ });
140
+ }
141
+ });
142
+ };
143
+ export default sitemapRoutes;
144
+ //# sourceMappingURL=sitemap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../../src/routes/sitemap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Fastify server setup.
3
+ */
4
+ import { type FastifyInstance } from 'fastify';
5
+ export interface ServerConfig {
6
+ /** Server port */
7
+ port?: number;
8
+ /** Server host */
9
+ host?: string;
10
+ /** Enable CORS */
11
+ cors?: boolean | CorsOptions;
12
+ /** Cookie secret */
13
+ cookieSecret?: string;
14
+ /** Uploads directory */
15
+ uploadsDir?: string;
16
+ /** Enable request logging */
17
+ logger?: boolean;
18
+ /** API prefix */
19
+ prefix?: string;
20
+ /** API key for authentication */
21
+ apiKey?: string;
22
+ /** Enable authentication (default: true in production) */
23
+ authEnabled?: boolean;
24
+ }
25
+ export interface CorsOptions {
26
+ origin?: string | string[] | boolean;
27
+ methods?: string[];
28
+ credentials?: boolean;
29
+ }
30
+ /**
31
+ * Create a Fastify server instance.
32
+ */
33
+ export declare function createServer(config?: ServerConfig): Promise<FastifyInstance>;
34
+ /**
35
+ * Start the server.
36
+ */
37
+ export declare function startServer(server: FastifyInstance, config?: ServerConfig): Promise<string>;
38
+ /**
39
+ * Stop the server gracefully.
40
+ */
41
+ export declare function stopServer(server: FastifyInstance): Promise<void>;
42
+ declare module 'fastify' {
43
+ interface FastifyInstance {
44
+ config: Required<ServerConfig>;
45
+ }
46
+ }
47
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,OAAgB,EAAE,KAAK,eAAe,EAA6B,MAAM,SAAS,CAAC;AAGnF,MAAM,WAAW,YAAY;IAC3B,kBAAkB;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,IAAI,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IAC7B,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iBAAiB;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAgCD;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,eAAe,CAAC,CA2KtF;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,eAAe,EACvB,MAAM,GAAE,YAAiB,GACxB,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAGD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;KAChC;CACF"}
package/dist/server.js ADDED
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Fastify server setup.
3
+ */
4
+ import { existsSync, mkdirSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import cookie from '@fastify/cookie';
7
+ import cors from '@fastify/cors';
8
+ import csrfProtection from '@fastify/csrf-protection';
9
+ import helmet from '@fastify/helmet';
10
+ import multipart from '@fastify/multipart';
11
+ import rateLimit from '@fastify/rate-limit';
12
+ import fastifyStatic from '@fastify/static';
13
+ import Fastify, {} from 'fastify';
14
+ import authPlugin, {} from './plugins/auth.js';
15
+ /**
16
+ * Get cookie secret from environment or use development fallback.
17
+ * In production, REVERSO_COOKIE_SECRET must be set.
18
+ */
19
+ function getCookieSecret() {
20
+ const envSecret = process.env.REVERSO_COOKIE_SECRET;
21
+ if (envSecret) {
22
+ return envSecret;
23
+ }
24
+ // Only allow fallback in non-production environments
25
+ if (process.env.NODE_ENV === 'production') {
26
+ throw new Error('REVERSO_COOKIE_SECRET environment variable must be set in production');
27
+ }
28
+ return 'reverso-dev-secret-do-not-use-in-production';
29
+ }
30
+ const defaultConfig = {
31
+ port: 3001,
32
+ host: '0.0.0.0',
33
+ cors: true,
34
+ cookieSecret: getCookieSecret(),
35
+ uploadsDir: '.reverso/uploads',
36
+ logger: true,
37
+ prefix: '/api/reverso',
38
+ apiKey: process.env.REVERSO_API_KEY || '',
39
+ authEnabled: process.env.NODE_ENV === 'production',
40
+ };
41
+ /**
42
+ * Create a Fastify server instance.
43
+ */
44
+ export async function createServer(config = {}) {
45
+ const opts = { ...defaultConfig, ...config };
46
+ const fastifyOptions = {
47
+ logger: opts.logger
48
+ ? {
49
+ transport: {
50
+ target: 'pino-pretty',
51
+ options: {
52
+ translateTime: 'HH:MM:ss Z',
53
+ ignore: 'pid,hostname',
54
+ },
55
+ },
56
+ }
57
+ : false,
58
+ };
59
+ const server = Fastify(fastifyOptions);
60
+ // Register CORS
61
+ if (opts.cors) {
62
+ // In production, require explicit origin configuration
63
+ const isProduction = process.env.NODE_ENV === 'production';
64
+ const defaultOrigin = isProduction
65
+ ? process.env.REVERSO_CORS_ORIGIN || 'http://localhost:3000'
66
+ : true;
67
+ const corsOptions = typeof opts.cors === 'object'
68
+ ? {
69
+ origin: opts.cors.origin ?? defaultOrigin,
70
+ methods: opts.cors.methods ?? ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
71
+ credentials: opts.cors.credentials ?? true,
72
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key'],
73
+ }
74
+ : {
75
+ origin: defaultOrigin,
76
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
77
+ credentials: true,
78
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key'],
79
+ };
80
+ await server.register(cors, corsOptions);
81
+ }
82
+ // Register security headers (helmet)
83
+ await server.register(helmet, {
84
+ contentSecurityPolicy: {
85
+ directives: {
86
+ defaultSrc: ["'self'"],
87
+ styleSrc: ["'self'", "'unsafe-inline'"],
88
+ scriptSrc: ["'self'"],
89
+ imgSrc: ["'self'", 'data:', 'blob:'],
90
+ fontSrc: ["'self'"],
91
+ objectSrc: ["'none'"],
92
+ upgradeInsecureRequests: process.env.NODE_ENV === 'production' ? [] : null,
93
+ },
94
+ },
95
+ crossOriginEmbedderPolicy: false, // Disable for media uploads
96
+ crossOriginResourcePolicy: { policy: 'cross-origin' }, // Allow cross-origin for assets
97
+ });
98
+ // Register cookies
99
+ await server.register(cookie, {
100
+ secret: opts.cookieSecret,
101
+ parseOptions: {},
102
+ });
103
+ // Register CSRF protection (enabled by default, can be disabled in dev with env var)
104
+ const csrfEnabled = process.env.REVERSO_CSRF_DISABLED !== 'true';
105
+ if (csrfEnabled) {
106
+ await server.register(csrfProtection, {
107
+ sessionPlugin: '@fastify/cookie',
108
+ cookieOpts: {
109
+ signed: true,
110
+ httpOnly: true,
111
+ sameSite: 'strict',
112
+ secure: process.env.NODE_ENV === 'production',
113
+ path: '/',
114
+ },
115
+ // Skip CSRF for public endpoints and API key authentication
116
+ getToken: (request) => {
117
+ return (request.headers['x-csrf-token']?.toString() ||
118
+ request.headers['csrf-token']?.toString());
119
+ },
120
+ });
121
+ // Add hook to skip CSRF for safe methods and API key auth
122
+ server.addHook('onRequest', async (request, reply) => {
123
+ const safeMethods = ['GET', 'HEAD', 'OPTIONS'];
124
+ if (safeMethods.includes(request.method))
125
+ return;
126
+ // Skip CSRF if API key is provided
127
+ if (request.headers['x-api-key'])
128
+ return;
129
+ // Skip CSRF for public form submissions (they have their own validation)
130
+ if (request.url.startsWith('/api/reverso/public/'))
131
+ return;
132
+ });
133
+ // Add CSRF token generation endpoint
134
+ server.get('/api/csrf-token', async (request, reply) => {
135
+ const token = await reply.generateCsrf();
136
+ return { csrfToken: token };
137
+ });
138
+ }
139
+ // Register rate limiting
140
+ await server.register(rateLimit, {
141
+ max: 100, // Max 100 requests per window
142
+ timeWindow: '1 minute',
143
+ // Stricter limits for specific routes
144
+ keyGenerator: (request) => {
145
+ // Use API key or IP for rate limiting
146
+ return (request.headers['x-api-key']?.toString() ||
147
+ request.headers['x-forwarded-for']?.toString() ||
148
+ request.ip);
149
+ },
150
+ // Skip rate limiting for health checks
151
+ allowList: ['/health'],
152
+ // Custom error response
153
+ errorResponseBuilder: () => ({
154
+ success: false,
155
+ error: 'Too Many Requests',
156
+ message: 'Rate limit exceeded. Please try again later.',
157
+ }),
158
+ });
159
+ // Register multipart for file uploads
160
+ await server.register(multipart, {
161
+ limits: {
162
+ fileSize: 50 * 1024 * 1024, // 50MB
163
+ files: 10,
164
+ },
165
+ });
166
+ // Register authentication plugin
167
+ await server.register(authPlugin, {
168
+ apiKey: opts.apiKey,
169
+ enabled: opts.authEnabled,
170
+ publicPaths: [
171
+ /^\/api\/reverso\/public\//,
172
+ /^\/sitemap\.xml$/,
173
+ ],
174
+ });
175
+ // Ensure uploads directory exists
176
+ if (!existsSync(opts.uploadsDir)) {
177
+ mkdirSync(opts.uploadsDir, { recursive: true });
178
+ }
179
+ // Serve static files from uploads directory
180
+ await server.register(fastifyStatic, {
181
+ root: join(process.cwd(), opts.uploadsDir),
182
+ prefix: '/uploads/',
183
+ decorateReply: false,
184
+ });
185
+ // Health check endpoint
186
+ server.get('/health', async () => ({
187
+ status: 'ok',
188
+ timestamp: new Date().toISOString(),
189
+ }));
190
+ // Store config in server instance
191
+ server.decorate('config', opts);
192
+ return server;
193
+ }
194
+ /**
195
+ * Start the server.
196
+ */
197
+ export async function startServer(server, config = {}) {
198
+ // Use server's stored config as base, then override with passed config
199
+ const opts = { ...defaultConfig, ...server.config, ...config };
200
+ try {
201
+ const address = await server.listen({
202
+ port: opts.port,
203
+ host: opts.host,
204
+ });
205
+ return address;
206
+ }
207
+ catch (err) {
208
+ server.log.error(err);
209
+ throw err;
210
+ }
211
+ }
212
+ /**
213
+ * Stop the server gracefully.
214
+ */
215
+ export async function stopServer(server) {
216
+ await server.close();
217
+ }
218
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * API type definitions.
3
+ */
4
+ import type { ContentValue, ProjectSchema } from '@reverso/core';
5
+ import type { DrizzleDatabase } from '@reverso/db';
6
+ import type { FastifyRequest } from 'fastify';
7
+ import type { AuthUser } from './plugins/auth.js';
8
+ export type { AuthUser };
9
+ /**
10
+ * Extended FastifyRequest with database.
11
+ */
12
+ export interface ApiRequest extends FastifyRequest {
13
+ db: DrizzleDatabase;
14
+ }
15
+ /**
16
+ * API error response.
17
+ */
18
+ export interface ApiError {
19
+ error: string;
20
+ message: string;
21
+ statusCode: number;
22
+ details?: unknown;
23
+ }
24
+ /**
25
+ * API success response.
26
+ */
27
+ export interface ApiResponse<T = unknown> {
28
+ success: boolean;
29
+ data?: T;
30
+ meta?: {
31
+ total?: number;
32
+ page?: number;
33
+ limit?: number;
34
+ };
35
+ }
36
+ /**
37
+ * Schema sync request body.
38
+ */
39
+ export interface SchemaSyncBody {
40
+ schema: ProjectSchema;
41
+ deleteRemoved?: boolean;
42
+ }
43
+ /**
44
+ * Content update request body.
45
+ */
46
+ export interface ContentUpdateBody {
47
+ value: ContentValue;
48
+ locale?: string;
49
+ publish?: boolean;
50
+ }
51
+ /**
52
+ * Bulk content update request body.
53
+ */
54
+ export interface BulkContentUpdateBody {
55
+ updates: Array<{
56
+ path: string;
57
+ value: ContentValue;
58
+ locale?: string;
59
+ }>;
60
+ }
61
+ /**
62
+ * Media upload result.
63
+ */
64
+ export interface MediaUploadResult {
65
+ id: string;
66
+ url: string;
67
+ filename: string;
68
+ mimeType: string;
69
+ size: number;
70
+ width?: number;
71
+ height?: number;
72
+ }
73
+ /**
74
+ * Pagination params.
75
+ */
76
+ export interface PaginationParams {
77
+ page?: number;
78
+ limit?: number;
79
+ offset?: number;
80
+ }
81
+ /**
82
+ * List response with pagination.
83
+ */
84
+ export interface ListResponse<T> {
85
+ items: T[];
86
+ total: number;
87
+ page: number;
88
+ limit: number;
89
+ totalPages: number;
90
+ }
91
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlD,YAAY,EAAE,QAAQ,EAAE,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,cAAc;IAChD,EAAE,EAAE,eAAe,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,YAAY,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * API type definitions.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Utility exports.
3
+ */
4
+ export { isUrlSafeForSSRF, isRedirectUrlSafe, generateWebhookSignature, verifyWebhookSignature, isValidTokenFormat, } from './security.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Utility exports.
3
+ */
4
+ export { isUrlSafeForSSRF, isRedirectUrlSafe, generateWebhookSignature, verifyWebhookSignature, isValidTokenFormat, } from './security.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Security utilities for the API.
3
+ */
4
+ /**
5
+ * Check if a URL is safe for server-side requests (SSRF protection).
6
+ * Blocks private IPs, localhost, and cloud metadata endpoints.
7
+ */
8
+ export declare function isUrlSafeForSSRF(urlString: string): {
9
+ safe: boolean;
10
+ reason?: string;
11
+ };
12
+ /**
13
+ * Check if a redirect URL is safe (prevents open redirect attacks).
14
+ * Only allows relative URLs or URLs to whitelisted domains.
15
+ */
16
+ export declare function isRedirectUrlSafe(urlString: string | undefined | null): {
17
+ safe: boolean;
18
+ reason?: string;
19
+ };
20
+ /**
21
+ * Generate HMAC-SHA256 signature for webhook payloads.
22
+ */
23
+ export declare function generateWebhookSignature(payload: string, secret: string): string;
24
+ /**
25
+ * Verify HMAC-SHA256 signature for webhook payloads.
26
+ */
27
+ export declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
28
+ /**
29
+ * Validate token format and length.
30
+ */
31
+ export declare function isValidTokenFormat(token: string): boolean;
32
+ //# sourceMappingURL=security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmCH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CA+BtF;AAcD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAuC1G;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIhF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAQlG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAkBzD"}