@oktis-works/api 0.1.3 → 0.1.6
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/dist/index.js +10 -23
- package/dist/index.js.map +1 -1
- package/dist/middleware/auth.d.ts +2 -2
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +7 -4
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/csrf.d.ts +10 -6
- package/dist/middleware/csrf.d.ts.map +1 -1
- package/dist/middleware/csrf.js +27 -8
- package/dist/middleware/csrf.js.map +1 -1
- package/dist/routes/audit-logs/index.d.ts +1 -1
- package/dist/routes/audit-logs/index.d.ts.map +1 -1
- package/dist/routes/auth/index.d.ts +1 -1
- package/dist/routes/auth/index.d.ts.map +1 -1
- package/dist/routes/auth/index.js +81 -15
- package/dist/routes/auth/index.js.map +1 -1
- package/dist/routes/auth/me.d.ts +1 -1
- package/dist/routes/auth/me.d.ts.map +1 -1
- package/dist/routes/categories/index.d.ts +1 -1
- package/dist/routes/categories/index.d.ts.map +1 -1
- package/dist/routes/content/index.d.ts +1 -1
- package/dist/routes/content/index.d.ts.map +1 -1
- package/dist/routes/content/index.js +4 -0
- package/dist/routes/content/index.js.map +1 -1
- package/dist/routes/content-types/index.d.ts +1 -1
- package/dist/routes/content-types/index.d.ts.map +1 -1
- package/dist/routes/deployments/index.d.ts +1 -1
- package/dist/routes/deployments/index.d.ts.map +1 -1
- package/dist/routes/docs/index.d.ts +1 -1
- package/dist/routes/docs/index.d.ts.map +1 -1
- package/dist/routes/events/index.d.ts +1 -1
- package/dist/routes/events/index.d.ts.map +1 -1
- package/dist/routes/field-groups/index.d.ts +1 -1
- package/dist/routes/field-groups/index.d.ts.map +1 -1
- package/dist/routes/field-types/index.d.ts +1 -1
- package/dist/routes/field-types/index.d.ts.map +1 -1
- package/dist/routes/health/index.d.ts +1 -1
- package/dist/routes/health/index.d.ts.map +1 -1
- package/dist/routes/hooks/index.d.ts +1 -1
- package/dist/routes/hooks/index.d.ts.map +1 -1
- package/dist/routes/media/index.d.ts +1 -1
- package/dist/routes/media/index.d.ts.map +1 -1
- package/dist/routes/media/index.js +120 -18
- package/dist/routes/media/index.js.map +1 -1
- package/dist/routes/menus/index.d.ts +1 -1
- package/dist/routes/menus/index.d.ts.map +1 -1
- package/dist/routes/metrics/index.d.ts +1 -1
- package/dist/routes/metrics/index.d.ts.map +1 -1
- package/dist/routes/pages/index.d.ts +1 -1
- package/dist/routes/pages/index.d.ts.map +1 -1
- package/dist/routes/plugins/index.d.ts +1 -1
- package/dist/routes/plugins/index.d.ts.map +1 -1
- package/dist/routes/posts/index.d.ts +1 -1
- package/dist/routes/posts/index.d.ts.map +1 -1
- package/dist/routes/roles/index.d.ts +1 -1
- package/dist/routes/roles/index.d.ts.map +1 -1
- package/dist/routes/settings/index.d.ts +1 -1
- package/dist/routes/settings/index.d.ts.map +1 -1
- package/dist/routes/shared/content-collection.d.ts.map +1 -1
- package/dist/routes/shared/content-collection.js +4 -0
- package/dist/routes/shared/content-collection.js.map +1 -1
- package/dist/routes/tags/index.d.ts +1 -1
- package/dist/routes/tags/index.d.ts.map +1 -1
- package/dist/routes/taxonomies/index.d.ts +1 -1
- package/dist/routes/taxonomies/index.d.ts.map +1 -1
- package/dist/routes/tenants/index.d.ts +1 -1
- package/dist/routes/tenants/index.d.ts.map +1 -1
- package/dist/routes/themes/index.d.ts +1 -1
- package/dist/routes/themes/index.d.ts.map +1 -1
- package/dist/routes/users/index.d.ts +1 -1
- package/dist/routes/users/index.d.ts.map +1 -1
- package/dist/routes/users/index.js +94 -4
- package/dist/routes/users/index.js.map +1 -1
- package/dist/routes/webhooks/index.d.ts +1 -1
- package/dist/routes/webhooks/index.d.ts.map +1 -1
- package/dist/routes/webhooks/index.js +1 -1
- package/dist/routes/webhooks/index.js.map +1 -1
- package/dist/storage.d.ts +17 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +95 -0
- package/dist/storage.js.map +1 -0
- package/dist/utils/csrf.d.ts +6 -0
- package/dist/utils/csrf.d.ts.map +1 -0
- package/dist/utils/csrf.js +29 -0
- package/dist/utils/csrf.js.map +1 -0
- package/package.json +8 -8
- package/dist/middleware/csrf.test.d.ts +0 -2
- package/dist/middleware/csrf.test.d.ts.map +0 -1
- package/dist/middleware/csrf.test.js +0 -94
- package/dist/middleware/csrf.test.js.map +0 -1
- package/dist/middleware/rate-limit.test.d.ts +0 -2
- package/dist/middleware/rate-limit.test.d.ts.map +0 -1
- package/dist/middleware/rate-limit.test.js +0 -58
- package/dist/middleware/rate-limit.test.js.map +0 -1
- package/dist/middleware/trace.test.d.ts +0 -2
- package/dist/middleware/trace.test.d.ts.map +0 -1
- package/dist/middleware/trace.test.js +0 -27
- package/dist/middleware/trace.test.js.map +0 -1
- package/dist/routes/metrics/index.test.d.ts +0 -2
- package/dist/routes/metrics/index.test.d.ts.map +0 -1
- package/dist/routes/metrics/index.test.js +0 -64
- package/dist/routes/metrics/index.test.js.map +0 -1
- package/dist/routes/posts/revisions.test.d.ts +0 -2
- package/dist/routes/posts/revisions.test.d.ts.map +0 -1
- package/dist/routes/posts/revisions.test.js +0 -57
- package/dist/routes/posts/revisions.test.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/field-groups/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/field-groups/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AAyE1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/field-types/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/field-types/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AAoB1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const healthRouter: Hono<import("hono").
|
|
2
|
+
declare const healthRouter: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default healthRouter;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/health/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/health/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,YAAY,4EAAa,CAAC;AAmChC,eAAe,YAAY,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/hooks/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/hooks/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AAQ1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/media/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/media/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAe5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AAiK1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,29 +1,96 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { mediaService } from '@oktis-works/core';
|
|
3
|
+
import { resolveTenantId } from '@oktis-works/database';
|
|
3
4
|
import { authMiddleware, requirePermission } from '../../middleware/auth.js';
|
|
5
|
+
import { MAX_UPLOAD_BYTES, isSafeSegment, isSafeStoredPath, mimeFor, readUpload, removeUpload, saveUpload, } from '../../storage.js';
|
|
4
6
|
const router = new Hono();
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return c
|
|
7
|
+
// GET /file/* é o asset público do site (tema/web sem token — o nome aleatório
|
|
8
|
+
// do arquivo é o "segredo"); todo o resto passa pela sessão. O regex cobre o
|
|
9
|
+
// path montado (/api/v1/media/file/...) e a rota chamada direto nos testes.
|
|
10
|
+
router.use('*', async (c, next) => {
|
|
11
|
+
if (c.req.method === 'GET' && /^\/(?:api\/v1\/media\/)?file\/[^/]+\/[^/]+$/.test(c.req.path)) {
|
|
12
|
+
return next();
|
|
13
|
+
}
|
|
14
|
+
return authMiddleware(c, next);
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Servir o arquivo do disco (sem DB/RLS): <uploadDir>/<tenant>/<filename>.
|
|
18
|
+
* Cache imutável — o nome tem uuid, arquivo novo = URL nova.
|
|
19
|
+
*/
|
|
20
|
+
router.get('/file/:tenant/:filename', async (c) => {
|
|
21
|
+
const tenant = c.req.param('tenant');
|
|
22
|
+
const filename = c.req.param('filename');
|
|
23
|
+
if (!isSafeSegment(tenant) || !isSafeStoredPath(`${tenant}/${filename}`)) {
|
|
24
|
+
return c.notFound();
|
|
25
|
+
}
|
|
26
|
+
const data = await readUpload(`${tenant}/${filename}`);
|
|
27
|
+
if (!data)
|
|
28
|
+
return c.notFound();
|
|
29
|
+
return c.body(new Uint8Array(data), 200, {
|
|
30
|
+
'Content-Type': mimeFor(filename),
|
|
31
|
+
'Cache-Control': 'public, max-age=31536000, immutable',
|
|
32
|
+
});
|
|
13
33
|
});
|
|
14
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Upload real: multipart FormData ({file, alt?, caption?}) — o que o
|
|
36
|
+
* api-client (`uploadMedia`) e a página /media da admin enviam. O JSON puro
|
|
37
|
+
* (metadados já existentes no disco) continua aceito em POST / para
|
|
38
|
+
* compatibilidade.
|
|
39
|
+
*/
|
|
40
|
+
async function handleUpload(c) {
|
|
41
|
+
const contentType = c.req.header('content-type') ?? '';
|
|
42
|
+
if (!contentType.includes('multipart/form-data')) {
|
|
43
|
+
// Legado: JSON com metadados (path/url apontam para arquivo já existente)
|
|
44
|
+
try {
|
|
45
|
+
const body = await c.req.json();
|
|
46
|
+
const userId = c.get('userId');
|
|
47
|
+
const tenantId = await resolveTenantId(String(c.get('tenantId') ?? 'default'));
|
|
48
|
+
const result = await mediaService.create({
|
|
49
|
+
tenantId,
|
|
50
|
+
filename: body.filename,
|
|
51
|
+
mimeType: body.mimeType,
|
|
52
|
+
size: body.size,
|
|
53
|
+
path: body.path,
|
|
54
|
+
url: body.url,
|
|
55
|
+
alt: body.alt,
|
|
56
|
+
caption: body.caption,
|
|
57
|
+
metadata: body.metadata,
|
|
58
|
+
uploadedBy: userId,
|
|
59
|
+
});
|
|
60
|
+
return c.json(result, 201);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const message = error instanceof Error ? error.message : 'Failed to upload media';
|
|
64
|
+
return c.json({ error: message }, 400);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
15
67
|
try {
|
|
16
|
-
const body = await c.req.
|
|
68
|
+
const body = await c.req.parseBody();
|
|
69
|
+
const file = body['file'];
|
|
70
|
+
if (!(file instanceof File)) {
|
|
71
|
+
return c.json({ error: 'Missing file field "file"' }, 400);
|
|
72
|
+
}
|
|
73
|
+
if (file.size === 0) {
|
|
74
|
+
return c.json({ error: 'Empty file' }, 400);
|
|
75
|
+
}
|
|
76
|
+
if (file.size > MAX_UPLOAD_BYTES) {
|
|
77
|
+
return c.json({ error: `File too large (max ${MAX_UPLOAD_BYTES} bytes)` }, 413);
|
|
78
|
+
}
|
|
79
|
+
const rawTenant = String(c.get('tenantId') ?? 'default');
|
|
80
|
+
const tenantId = await resolveTenantId(rawTenant);
|
|
17
81
|
const userId = c.get('userId');
|
|
82
|
+
const data = new Uint8Array(await file.arrayBuffer());
|
|
83
|
+
const stored = await saveUpload(tenantId, file.name, data);
|
|
84
|
+
const origin = new URL(c.req.url).origin;
|
|
18
85
|
const result = await mediaService.create({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
86
|
+
tenantId,
|
|
87
|
+
filename: file.name,
|
|
88
|
+
mimeType: file.type || mimeFor(file.name),
|
|
89
|
+
size: stored.size,
|
|
90
|
+
path: stored.path,
|
|
91
|
+
url: `${origin}/api/v1/media/file/${stored.path}`,
|
|
92
|
+
alt: typeof body['alt'] === 'string' ? body['alt'] : undefined,
|
|
93
|
+
caption: typeof body['caption'] === 'string' ? body['caption'] : undefined,
|
|
27
94
|
uploadedBy: userId,
|
|
28
95
|
});
|
|
29
96
|
return c.json(result, 201);
|
|
@@ -32,6 +99,16 @@ router.post('/', requirePermission('upload', 'media'), async (c) => {
|
|
|
32
99
|
const message = error instanceof Error ? error.message : 'Failed to upload media';
|
|
33
100
|
return c.json({ error: message }, 400);
|
|
34
101
|
}
|
|
102
|
+
}
|
|
103
|
+
router.post('/upload', requirePermission('upload', 'media'), handleUpload);
|
|
104
|
+
router.post('/', requirePermission('upload', 'media'), handleUpload);
|
|
105
|
+
router.get('/', requirePermission('read', 'media'), async (c) => {
|
|
106
|
+
const page = Number(c.req.query('page') ?? 1);
|
|
107
|
+
const limit = Number(c.req.query('limit') ?? 20);
|
|
108
|
+
const mimeType = c.req.query('mimeType') || undefined;
|
|
109
|
+
const search = c.req.query('search') || undefined;
|
|
110
|
+
const result = await mediaService.list({ page, limit, mimeType, search });
|
|
111
|
+
return c.json(result);
|
|
35
112
|
});
|
|
36
113
|
router.get('/:id', requirePermission('read', 'media'), async (c) => {
|
|
37
114
|
const id = c.req.param('id');
|
|
@@ -40,11 +117,36 @@ router.get('/:id', requirePermission('read', 'media'), async (c) => {
|
|
|
40
117
|
return c.json({ error: 'Media not found' }, 404);
|
|
41
118
|
return c.json(result);
|
|
42
119
|
});
|
|
120
|
+
/** Edição de alt/caption/metadata (sem reupload). */
|
|
121
|
+
router.put('/:id', requirePermission('update', 'media'), async (c) => {
|
|
122
|
+
try {
|
|
123
|
+
const id = c.req.param('id');
|
|
124
|
+
const body = await c.req.json();
|
|
125
|
+
const result = await mediaService.update(id, {
|
|
126
|
+
alt: body.alt,
|
|
127
|
+
caption: body.caption,
|
|
128
|
+
metadata: body.metadata,
|
|
129
|
+
});
|
|
130
|
+
if (!result)
|
|
131
|
+
return c.json({ error: 'Media not found' }, 404);
|
|
132
|
+
return c.json(result);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const message = error instanceof Error ? error.message : 'Failed to update media';
|
|
136
|
+
return c.json({ error: message }, 400);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
43
139
|
router.delete('/:id', requirePermission('delete', 'media'), async (c) => {
|
|
44
140
|
const id = c.req.param('id');
|
|
141
|
+
const existing = await mediaService.getById(id);
|
|
142
|
+
if (!existing)
|
|
143
|
+
return c.json({ error: 'Media not found' }, 404);
|
|
45
144
|
const deleted = await mediaService.delete(id);
|
|
46
145
|
if (!deleted)
|
|
47
146
|
return c.json({ error: 'Media not found' }, 404);
|
|
147
|
+
if (existing.path && isSafeStoredPath(existing.path)) {
|
|
148
|
+
await removeUpload(existing.path);
|
|
149
|
+
}
|
|
48
150
|
return c.json({ success: true });
|
|
49
151
|
});
|
|
50
152
|
export default router;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/routes/media/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/routes/media/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/menus/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/menus/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AAuD1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/metrics/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/metrics/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AAkC1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
declare const pagesRouter: import("hono").Hono<import("hono").
|
|
1
|
+
declare const pagesRouter: import("hono").Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
2
2
|
export default pagesRouter;
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/index.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/index.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,WAAW,2FAAgE,CAAC;AAElF,eAAe,WAAW,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const plugins: Hono<import("hono").
|
|
2
|
+
declare const plugins: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default plugins;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,QAAA,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/plugins/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,QAAA,MAAM,OAAO,4EAAa,CAAA;AA8D1B,eAAe,OAAO,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
declare const postsRouter: import("hono").Hono<import("hono").
|
|
1
|
+
declare const postsRouter: import("hono").Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
2
2
|
export default postsRouter;
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/posts/index.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/posts/index.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,WAAW,2FAAgE,CAAC;AAElF,eAAe,WAAW,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const rolesRouter: Hono<import("hono").
|
|
2
|
+
declare const rolesRouter: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default rolesRouter;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/roles/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/roles/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,WAAW,4EAAa,CAAC;AA0D/B,eAAe,WAAW,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AA+B1B,eAAe,MAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-collection.d.ts","sourceRoot":"","sources":["../../../src/routes/shared/content-collection.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"content-collection.d.ts","sourceRoot":"","sources":["../../../src/routes/shared/content-collection.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM5B,MAAM,WAAW,uBAAuB;IACtC,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAwHnF"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Elimina duplicação entre /posts e /pages (mesmo contrato, type diferente).
|
|
3
3
|
import { Hono } from 'hono';
|
|
4
4
|
import { contentService } from '@oktis-works/core';
|
|
5
|
+
import { resolveTenantId } from '@oktis-works/database';
|
|
5
6
|
import { authMiddleware, requirePermission } from '../../middleware/auth.js';
|
|
6
7
|
export function createContentCollectionRouter(config) {
|
|
7
8
|
const Label = config.name.charAt(0).toUpperCase() + config.name.slice(1);
|
|
@@ -21,8 +22,11 @@ export function createContentCollectionRouter(config) {
|
|
|
21
22
|
try {
|
|
22
23
|
const body = await c.req.json();
|
|
23
24
|
const userId = c.get('userId');
|
|
25
|
+
// Depois do spread: tenant/autoridade vêm do JWT, nunca do body do cliente.
|
|
26
|
+
const tenantId = await resolveTenantId(String(c.get('tenantId') ?? 'default'));
|
|
24
27
|
const content = await contentService.create({
|
|
25
28
|
...body,
|
|
29
|
+
tenantId,
|
|
26
30
|
type: config.type,
|
|
27
31
|
authorId: userId,
|
|
28
32
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-collection.js","sourceRoot":"","sources":["../../../src/routes/shared/content-collection.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,6EAA6E;AAE7E,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"content-collection.js","sourceRoot":"","sources":["../../../src/routes/shared/content-collection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/tags/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/tags/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AA8B1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/taxonomies/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/taxonomies/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AA+G1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const tenantsRouter: Hono<import("hono").
|
|
2
|
+
declare const tenantsRouter: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default tenantsRouter;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/tenants/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/tenants/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,QAAA,MAAM,aAAa,4EAAa,CAAC;AAwEjC,eAAe,aAAa,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const themes: Hono<import("hono").
|
|
2
|
+
declare const themes: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default themes;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/themes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,QAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/themes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,QAAA,MAAM,MAAM,4EAAa,CAAA;AAmDzB,eAAe,MAAM,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
declare const router: Hono<import("hono").
|
|
2
|
+
declare const router: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
3
3
|
export default router;
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/users/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/users/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAO5B,QAAA,MAAM,MAAM,4EAAa,CAAC;AA2K1B,eAAe,MAAM,CAAC"}
|
|
@@ -1,28 +1,60 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { userService } from '@oktis-works/core';
|
|
3
|
+
import { resolveTenantId } from '@oktis-works/database';
|
|
3
4
|
import { authMiddleware, requirePermission } from '../../middleware/auth.js';
|
|
4
5
|
import { hashPassword } from '@oktis-works/auth';
|
|
5
6
|
const router = new Hono();
|
|
6
7
|
router.use('*', authMiddleware);
|
|
8
|
+
/** JWT guarda o slug (ex.: "default") ou UUID — resolve para o UUID do tenant. */
|
|
9
|
+
async function tenantUuid(c) {
|
|
10
|
+
const raw = c.get('tenantId') ?? 'default';
|
|
11
|
+
return resolveTenantId(raw);
|
|
12
|
+
}
|
|
13
|
+
/** Remove credenciais da resposta (o SELECT * traz password_hash). */
|
|
14
|
+
function sanitizeUser(user) {
|
|
15
|
+
const { password_hash: _ph, passwordHash: _ph2, ...rest } = user;
|
|
16
|
+
return rest;
|
|
17
|
+
}
|
|
7
18
|
router.get('/', requirePermission('read', 'user'), async (c) => {
|
|
8
19
|
const page = Number(c.req.query('page') ?? 1);
|
|
9
20
|
const limit = Number(c.req.query('limit') ?? 20);
|
|
10
21
|
const search = c.req.query('search') || undefined;
|
|
11
22
|
const status = (c.req.query('status') || undefined);
|
|
12
23
|
const result = await userService.list({ page, limit, search, status });
|
|
13
|
-
|
|
24
|
+
const tenantId = await tenantUuid(c);
|
|
25
|
+
const rolesMap = await userService.rolesForUsers(result.data.map((u) => u.id), tenantId);
|
|
26
|
+
return c.json({
|
|
27
|
+
data: result.data.map((u) => ({
|
|
28
|
+
...sanitizeUser(u),
|
|
29
|
+
roles: rolesMap[u.id] ?? [],
|
|
30
|
+
})),
|
|
31
|
+
total: result.total,
|
|
32
|
+
});
|
|
14
33
|
});
|
|
15
34
|
router.post('/', requirePermission('create', 'user'), async (c) => {
|
|
16
35
|
try {
|
|
17
36
|
const body = await c.req.json();
|
|
18
37
|
const passwordHash = await hashPassword(body.password);
|
|
38
|
+
// Valida o papel ANTES de criar (evita usuário órfão se o roleId for ruim)
|
|
39
|
+
let role = null;
|
|
40
|
+
if (typeof body.roleId === 'string' && body.roleId) {
|
|
41
|
+
role = await userService.getRoleById(body.roleId);
|
|
42
|
+
if (!role)
|
|
43
|
+
return c.json({ error: 'Role not found' }, 404);
|
|
44
|
+
}
|
|
19
45
|
const result = await userService.create({
|
|
20
46
|
email: body.email,
|
|
21
47
|
name: body.name,
|
|
22
48
|
passwordHash,
|
|
23
49
|
avatar: body.avatar,
|
|
24
50
|
});
|
|
25
|
-
|
|
51
|
+
let roles = [];
|
|
52
|
+
if (role) {
|
|
53
|
+
const tenantId = await tenantUuid(c);
|
|
54
|
+
await userService.addToTenant(result.id, tenantId, role.id);
|
|
55
|
+
roles = [role.slug];
|
|
56
|
+
}
|
|
57
|
+
return c.json({ ...sanitizeUser(result), roles }, 201);
|
|
26
58
|
}
|
|
27
59
|
catch (error) {
|
|
28
60
|
const message = error instanceof Error ? error.message : 'Failed to create user';
|
|
@@ -34,7 +66,12 @@ router.get('/:id', requirePermission('read', 'user'), async (c) => {
|
|
|
34
66
|
const result = await userService.getById(id);
|
|
35
67
|
if (!result)
|
|
36
68
|
return c.json({ error: 'User not found' }, 404);
|
|
37
|
-
|
|
69
|
+
const tenantId = await tenantUuid(c);
|
|
70
|
+
const roles = await userService.rolesFor(result.id, tenantId);
|
|
71
|
+
return c.json({
|
|
72
|
+
...sanitizeUser(result),
|
|
73
|
+
roles,
|
|
74
|
+
});
|
|
38
75
|
});
|
|
39
76
|
router.put('/:id', requirePermission('update', 'user'), async (c) => {
|
|
40
77
|
try {
|
|
@@ -48,13 +85,66 @@ router.put('/:id', requirePermission('update', 'user'), async (c) => {
|
|
|
48
85
|
});
|
|
49
86
|
if (!result)
|
|
50
87
|
return c.json({ error: 'User not found' }, 404);
|
|
51
|
-
return c.json(result);
|
|
88
|
+
return c.json(sanitizeUser(result));
|
|
52
89
|
}
|
|
53
90
|
catch (error) {
|
|
54
91
|
const message = error instanceof Error ? error.message : 'Failed to update user';
|
|
55
92
|
return c.json({ error: message }, 400);
|
|
56
93
|
}
|
|
57
94
|
});
|
|
95
|
+
router.put('/:id/password', requirePermission('update', 'user'), async (c) => {
|
|
96
|
+
try {
|
|
97
|
+
const id = c.req.param('id');
|
|
98
|
+
const body = await c.req.json();
|
|
99
|
+
if (typeof body.password !== 'string' || body.password.length < 8) {
|
|
100
|
+
return c.json({ error: 'Password must be at least 8 characters' }, 400);
|
|
101
|
+
}
|
|
102
|
+
const passwordHash = await hashPassword(body.password);
|
|
103
|
+
const updated = await userService.updatePassword(id, passwordHash);
|
|
104
|
+
if (!updated)
|
|
105
|
+
return c.json({ error: 'User not found' }, 404);
|
|
106
|
+
return c.json({ success: true });
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : 'Failed to update password';
|
|
110
|
+
return c.json({ error: message }, 400);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
/** Atribui (ou troca) o papel do usuário no tenant atual. */
|
|
114
|
+
router.post('/:id/roles', requirePermission('update', 'user'), async (c) => {
|
|
115
|
+
try {
|
|
116
|
+
const id = c.req.param('id');
|
|
117
|
+
const body = await c.req.json();
|
|
118
|
+
if (typeof body.roleId !== 'string' || !body.roleId) {
|
|
119
|
+
return c.json({ error: 'Missing roleId' }, 400);
|
|
120
|
+
}
|
|
121
|
+
const user = await userService.getById(id);
|
|
122
|
+
if (!user)
|
|
123
|
+
return c.json({ error: 'User not found' }, 404);
|
|
124
|
+
const tenantId = await tenantUuid(c);
|
|
125
|
+
const roleExists = await userService.getRoleById(body.roleId);
|
|
126
|
+
if (!roleExists)
|
|
127
|
+
return c.json({ error: 'Role not found' }, 404);
|
|
128
|
+
await userService.addToTenant(user.id, tenantId, body.roleId);
|
|
129
|
+
const roles = await userService.rolesFor(user.id, tenantId);
|
|
130
|
+
return c.json({ success: true, roles });
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const message = error instanceof Error ? error.message : 'Failed to assign role';
|
|
134
|
+
return c.json({ error: message }, 400);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
router.delete('/:id/roles/:roleId', requirePermission('update', 'user'), async (c) => {
|
|
138
|
+
const id = c.req.param('id');
|
|
139
|
+
const roleId = c.req.param('roleId');
|
|
140
|
+
const user = await userService.getById(id);
|
|
141
|
+
if (!user)
|
|
142
|
+
return c.json({ error: 'User not found' }, 404);
|
|
143
|
+
const tenantId = await tenantUuid(c);
|
|
144
|
+
await userService.removeFromTenant(user.id, tenantId, roleId);
|
|
145
|
+
const roles = await userService.rolesFor(user.id, tenantId);
|
|
146
|
+
return c.json({ success: true, roles });
|
|
147
|
+
});
|
|
58
148
|
router.delete('/:id', requirePermission('delete', 'user'), async (c) => {
|
|
59
149
|
const id = c.req.param('id');
|
|
60
150
|
const deleted = await userService.delete(id);
|