@oktis-works/core 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.
- package/dist/bootstrap/index.d.ts +7 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +16 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/build/service.d.ts +28 -0
- package/dist/build/service.d.ts.map +1 -0
- package/dist/build/service.js +71 -0
- package/dist/build/service.js.map +1 -0
- package/dist/cache/index.d.ts +31 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +110 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/category/service.d.ts +21 -0
- package/dist/category/service.d.ts.map +1 -0
- package/dist/category/service.js +67 -0
- package/dist/category/service.js.map +1 -0
- package/dist/content/field-resolver.d.ts +31 -0
- package/dist/content/field-resolver.d.ts.map +1 -0
- package/dist/content/field-resolver.js +40 -0
- package/dist/content/field-resolver.js.map +1 -0
- package/dist/content/post-types.d.ts +28 -0
- package/dist/content/post-types.d.ts.map +1 -0
- package/dist/content/post-types.js +127 -0
- package/dist/content/post-types.js.map +1 -0
- package/dist/content/publisher.d.ts +26 -0
- package/dist/content/publisher.d.ts.map +1 -0
- package/dist/content/publisher.js +156 -0
- package/dist/content/publisher.js.map +1 -0
- package/dist/content/service.d.ts +53 -0
- package/dist/content/service.d.ts.map +1 -0
- package/dist/content/service.js +335 -0
- package/dist/content/service.js.map +1 -0
- package/dist/deployment/service.d.ts +25 -0
- package/dist/deployment/service.d.ts.map +1 -0
- package/dist/deployment/service.js +102 -0
- package/dist/deployment/service.js.map +1 -0
- package/dist/events/bus.d.ts +25 -0
- package/dist/events/bus.d.ts.map +1 -0
- package/dist/events/bus.js +89 -0
- package/dist/events/bus.js.map +1 -0
- package/dist/events/store.d.ts +19 -0
- package/dist/events/store.d.ts.map +1 -0
- package/dist/events/store.js +52 -0
- package/dist/events/store.js.map +1 -0
- package/dist/fields/clone.d.ts +2 -0
- package/dist/fields/clone.d.ts.map +1 -0
- package/dist/fields/clone.js +3 -0
- package/dist/fields/clone.js.map +1 -0
- package/dist/fields/conditional.d.ts +2 -0
- package/dist/fields/conditional.d.ts.map +1 -0
- package/dist/fields/conditional.js +3 -0
- package/dist/fields/conditional.js.map +1 -0
- package/dist/fields/index.d.ts +5 -0
- package/dist/fields/index.d.ts.map +1 -0
- package/dist/fields/index.js +6 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/fields/location-rules.d.ts +16 -0
- package/dist/fields/location-rules.d.ts.map +1 -0
- package/dist/fields/location-rules.js +22 -0
- package/dist/fields/location-rules.js.map +1 -0
- package/dist/fields/service.d.ts +77 -0
- package/dist/fields/service.d.ts.map +1 -0
- package/dist/fields/service.js +278 -0
- package/dist/fields/service.js.map +1 -0
- package/dist/fields/types/registry.d.ts +20 -0
- package/dist/fields/types/registry.d.ts.map +1 -0
- package/dist/fields/types/registry.js +68 -0
- package/dist/fields/types/registry.js.map +1 -0
- package/dist/fields/types/validators/basic.d.ts +2 -0
- package/dist/fields/types/validators/basic.d.ts.map +1 -0
- package/dist/fields/types/validators/basic.js +3 -0
- package/dist/fields/types/validators/basic.js.map +1 -0
- package/dist/fields/types/validators/choice.d.ts +2 -0
- package/dist/fields/types/validators/choice.d.ts.map +1 -0
- package/dist/fields/types/validators/choice.js +3 -0
- package/dist/fields/types/validators/choice.js.map +1 -0
- package/dist/fields/types/validators/client-validate.d.ts +2 -0
- package/dist/fields/types/validators/client-validate.d.ts.map +1 -0
- package/dist/fields/types/validators/client-validate.js +3 -0
- package/dist/fields/types/validators/client-validate.js.map +1 -0
- package/dist/fields/types/validators/index.d.ts +8 -0
- package/dist/fields/types/validators/index.d.ts.map +1 -0
- package/dist/fields/types/validators/index.js +10 -0
- package/dist/fields/types/validators/index.js.map +1 -0
- package/dist/fields/types/validators/media.d.ts +2 -0
- package/dist/fields/types/validators/media.d.ts.map +1 -0
- package/dist/fields/types/validators/media.js +3 -0
- package/dist/fields/types/validators/media.js.map +1 -0
- package/dist/fields/types/validators/relational.d.ts +2 -0
- package/dist/fields/types/validators/relational.d.ts.map +1 -0
- package/dist/fields/types/validators/relational.js +3 -0
- package/dist/fields/types/validators/relational.js.map +1 -0
- package/dist/fields/types/validators/structural.d.ts +2 -0
- package/dist/fields/types/validators/structural.d.ts.map +1 -0
- package/dist/fields/types/validators/structural.js +3 -0
- package/dist/fields/types/validators/structural.js.map +1 -0
- package/dist/fields/validation.d.ts +16 -0
- package/dist/fields/validation.d.ts.map +1 -0
- package/dist/fields/validation.js +123 -0
- package/dist/fields/validation.js.map +1 -0
- package/dist/hooks/catalog.d.ts +20 -0
- package/dist/hooks/catalog.d.ts.map +1 -0
- package/dist/hooks/catalog.js +59 -0
- package/dist/hooks/catalog.js.map +1 -0
- package/dist/hooks/points.d.ts +26 -0
- package/dist/hooks/points.d.ts.map +1 -0
- package/dist/hooks/points.js +33 -0
- package/dist/hooks/points.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/index.d.ts +17 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/index.js +79 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/media/service.d.ts +32 -0
- package/dist/media/service.d.ts.map +1 -0
- package/dist/media/service.js +87 -0
- package/dist/media/service.js.map +1 -0
- package/dist/menu/service.d.ts +19 -0
- package/dist/menu/service.d.ts.map +1 -0
- package/dist/menu/service.js +64 -0
- package/dist/menu/service.js.map +1 -0
- package/dist/observability/audit.d.ts +26 -0
- package/dist/observability/audit.d.ts.map +1 -0
- package/dist/observability/audit.js +67 -0
- package/dist/observability/audit.js.map +1 -0
- package/dist/observability/event-audit.d.ts +8 -0
- package/dist/observability/event-audit.d.ts.map +1 -0
- package/dist/observability/event-audit.js +20 -0
- package/dist/observability/event-audit.js.map +1 -0
- package/dist/observability/index.d.ts +5 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +6 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/logger.d.ts +13 -0
- package/dist/observability/logger.d.ts.map +1 -0
- package/dist/observability/logger.js +45 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +34 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +56 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/plugin/service.d.ts +32 -0
- package/dist/plugin/service.d.ts.map +1 -0
- package/dist/plugin/service.js +190 -0
- package/dist/plugin/service.js.map +1 -0
- package/dist/role/service.d.ts +19 -0
- package/dist/role/service.d.ts.map +1 -0
- package/dist/role/service.js +65 -0
- package/dist/role/service.js.map +1 -0
- package/dist/settings/service.d.ts +16 -0
- package/dist/settings/service.d.ts.map +1 -0
- package/dist/settings/service.js +64 -0
- package/dist/settings/service.js.map +1 -0
- package/dist/storage/driver.d.ts +20 -0
- package/dist/storage/driver.d.ts.map +1 -0
- package/dist/storage/driver.js +3 -0
- package/dist/storage/driver.js.map +1 -0
- package/dist/storage/drivers/local.d.ts +17 -0
- package/dist/storage/drivers/local.d.ts.map +1 -0
- package/dist/storage/drivers/local.js +88 -0
- package/dist/storage/drivers/local.js.map +1 -0
- package/dist/storage/drivers/s3.d.ts +28 -0
- package/dist/storage/drivers/s3.d.ts.map +1 -0
- package/dist/storage/drivers/s3.js +105 -0
- package/dist/storage/drivers/s3.js.map +1 -0
- package/dist/storage/index.d.ts +16 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +58 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/migrator.d.ts +18 -0
- package/dist/storage/migrator.d.ts.map +1 -0
- package/dist/storage/migrator.js +46 -0
- package/dist/storage/migrator.js.map +1 -0
- package/dist/tag/service.d.ts +13 -0
- package/dist/tag/service.d.ts.map +1 -0
- package/dist/tag/service.js +37 -0
- package/dist/tag/service.js.map +1 -0
- package/dist/taxonomies/service.d.ts +50 -0
- package/dist/taxonomies/service.d.ts.map +1 -0
- package/dist/taxonomies/service.js +179 -0
- package/dist/taxonomies/service.js.map +1 -0
- package/dist/tenant/context.d.ts +9 -0
- package/dist/tenant/context.d.ts.map +1 -0
- package/dist/tenant/context.js +67 -0
- package/dist/tenant/context.js.map +1 -0
- package/dist/tenant/service.d.ts +31 -0
- package/dist/tenant/service.d.ts.map +1 -0
- package/dist/tenant/service.js +90 -0
- package/dist/tenant/service.js.map +1 -0
- package/dist/theme/service.d.ts +16 -0
- package/dist/theme/service.d.ts.map +1 -0
- package/dist/theme/service.js +57 -0
- package/dist/theme/service.js.map +1 -0
- package/dist/user/service.d.ts +31 -0
- package/dist/user/service.d.ts.map +1 -0
- package/dist/user/service.js +95 -0
- package/dist/user/service.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ApplicationLifecycle } from '../lifecycle/index.js';
|
|
2
|
+
export interface BootstrapOptions {
|
|
3
|
+
configPath?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function bootstrap(_options?: BootstrapOptions): Promise<ApplicationLifecycle>;
|
|
6
|
+
export declare function bootstrapForTenant(tenantId: string): Promise<ApplicationLifecycle>;
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bootstrap/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAG7D,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,SAAS,CAAC,QAAQ,GAAE,gBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAO9F;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAIxF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// @oktis-works/core - Application Bootstrap
|
|
2
|
+
import { loadConfig } from '@oktis-works/config';
|
|
3
|
+
import { ApplicationLifecycle } from '../lifecycle/index.js';
|
|
4
|
+
import { establishTenantContext } from '../tenant/context.js';
|
|
5
|
+
export async function bootstrap(_options = {}) {
|
|
6
|
+
const config = loadConfig();
|
|
7
|
+
const lifecycle = new ApplicationLifecycle(config);
|
|
8
|
+
await lifecycle.start();
|
|
9
|
+
return lifecycle;
|
|
10
|
+
}
|
|
11
|
+
export async function bootstrapForTenant(tenantId) {
|
|
12
|
+
const lifecycle = await bootstrap();
|
|
13
|
+
await establishTenantContext(tenantId);
|
|
14
|
+
return lifecycle;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bootstrap/index.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAE5C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAM9D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,WAA6B,EAAE;IAC7D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAEnD,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IAExB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB;IACvD,MAAM,SAAS,GAAG,MAAM,SAAS,EAAE,CAAC;IACpC,MAAM,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACvC,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Build } from '@oktis-works/types';
|
|
2
|
+
export declare class BuildService {
|
|
3
|
+
list(options: {
|
|
4
|
+
page?: number;
|
|
5
|
+
limit?: number;
|
|
6
|
+
status?: string;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
data: Build[];
|
|
9
|
+
total: number;
|
|
10
|
+
}>;
|
|
11
|
+
getById(id: string): Promise<Build | null>;
|
|
12
|
+
create(input: {
|
|
13
|
+
coreVersion: string;
|
|
14
|
+
plugins: Record<string, string>;
|
|
15
|
+
theme: {
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
};
|
|
19
|
+
}): Promise<Build>;
|
|
20
|
+
updateStatus(id: string, status: string, options?: {
|
|
21
|
+
dockerImage?: string;
|
|
22
|
+
checksum?: string;
|
|
23
|
+
buildLog?: string;
|
|
24
|
+
error?: string;
|
|
25
|
+
}): Promise<Build | null>;
|
|
26
|
+
}
|
|
27
|
+
export declare const buildService: BuildService;
|
|
28
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/build/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,qBAAa,YAAY;IACjB,IAAI,CAAC,OAAO,EAAE;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IA4BvC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAM1C,MAAM,CAAC,KAAK,EAAE;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KAC1C,GAAG,OAAO,CAAC,KAAK,CAAC;IAeZ,YAAY,CAChB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GACA,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;CA0CzB;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { getConnection } from '@oktis-works/database';
|
|
3
|
+
export class BuildService {
|
|
4
|
+
async list(options) {
|
|
5
|
+
const sql = getConnection();
|
|
6
|
+
const { page = 1, limit = 20, status } = options;
|
|
7
|
+
const offset = (page - 1) * limit;
|
|
8
|
+
let whereClause = 'WHERE 1=1';
|
|
9
|
+
const params = [];
|
|
10
|
+
if (status) {
|
|
11
|
+
whereClause += ` AND status = $${params.length + 1}`;
|
|
12
|
+
params.push(status);
|
|
13
|
+
}
|
|
14
|
+
const dataResult = await sql.unsafe(`SELECT * FROM builds ${whereClause} ORDER BY created_at DESC LIMIT ${limit} OFFSET ${offset}`, params);
|
|
15
|
+
const countResult = await sql.unsafe(`SELECT COUNT(*) as total FROM builds ${whereClause}`, params);
|
|
16
|
+
return {
|
|
17
|
+
data: dataResult,
|
|
18
|
+
total: Number(countResult[0]?.['total'] ?? 0),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async getById(id) {
|
|
22
|
+
const sql = getConnection();
|
|
23
|
+
const result = await sql.unsafe('SELECT * FROM builds WHERE id = $1', [id]);
|
|
24
|
+
return result[0] ?? null;
|
|
25
|
+
}
|
|
26
|
+
async create(input) {
|
|
27
|
+
const sql = getConnection();
|
|
28
|
+
const id = randomUUID();
|
|
29
|
+
const now = new Date().toISOString();
|
|
30
|
+
const result = await sql.unsafe(`INSERT INTO builds (id, status, core_version, plugins, theme, created_at, updated_at)
|
|
31
|
+
VALUES ($1, 'PENDING', $2, $3::jsonb, $4::jsonb, $5, $5)
|
|
32
|
+
RETURNING *`, [id, input.coreVersion, JSON.stringify(input.plugins), JSON.stringify(input.theme), now]);
|
|
33
|
+
return result[0];
|
|
34
|
+
}
|
|
35
|
+
async updateStatus(id, status, options) {
|
|
36
|
+
const sql = getConnection();
|
|
37
|
+
const existing = await this.getById(id);
|
|
38
|
+
if (!existing)
|
|
39
|
+
return null;
|
|
40
|
+
const setClauses = ['status = $1'];
|
|
41
|
+
const setParams = [status];
|
|
42
|
+
if (options?.dockerImage !== undefined) {
|
|
43
|
+
setClauses.push(`docker_image = $${setParams.length + 1}`);
|
|
44
|
+
setParams.push(options.dockerImage);
|
|
45
|
+
}
|
|
46
|
+
if (options?.checksum !== undefined) {
|
|
47
|
+
setClauses.push(`checksum = $${setParams.length + 1}`);
|
|
48
|
+
setParams.push(options.checksum);
|
|
49
|
+
}
|
|
50
|
+
if (options?.buildLog !== undefined) {
|
|
51
|
+
setClauses.push(`build_log = $${setParams.length + 1}`);
|
|
52
|
+
setParams.push(options.buildLog);
|
|
53
|
+
}
|
|
54
|
+
if (options?.error !== undefined) {
|
|
55
|
+
setClauses.push(`error = $${setParams.length + 1}`);
|
|
56
|
+
setParams.push(options.error);
|
|
57
|
+
}
|
|
58
|
+
if (status === 'RUNNING') {
|
|
59
|
+
setClauses.push(`started_at = NOW()`);
|
|
60
|
+
}
|
|
61
|
+
if (status === 'COMPLETED' || status === 'FAILED') {
|
|
62
|
+
setClauses.push(`completed_at = NOW()`);
|
|
63
|
+
}
|
|
64
|
+
setClauses.push(`updated_at = NOW()`);
|
|
65
|
+
setParams.push(id);
|
|
66
|
+
const result = await sql.unsafe(`UPDATE builds SET ${setClauses.join(', ')} WHERE id = $${setParams.length} RETURNING *`, setParams);
|
|
67
|
+
return result[0] ?? null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export const buildService = new BuildService();
|
|
71
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/build/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CacheConfig, RedisConfig } from '@oktis-works/config';
|
|
2
|
+
export interface CacheEntry<T> {
|
|
3
|
+
value: T;
|
|
4
|
+
expiresAt: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class MemoryCache {
|
|
7
|
+
private store;
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config: CacheConfig);
|
|
10
|
+
get<T>(key: string): Promise<T | null>;
|
|
11
|
+
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
12
|
+
del(key: string): Promise<void>;
|
|
13
|
+
has(key: string): Promise<boolean>;
|
|
14
|
+
clear(): Promise<void>;
|
|
15
|
+
keys(pattern?: string): Promise<string[]>;
|
|
16
|
+
cleanup(): Promise<number>;
|
|
17
|
+
get size(): number;
|
|
18
|
+
}
|
|
19
|
+
export interface CacheAdapter {
|
|
20
|
+
get<T>(key: string): Promise<T | null>;
|
|
21
|
+
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
22
|
+
del(key: string): Promise<void>;
|
|
23
|
+
has(key: string): Promise<boolean>;
|
|
24
|
+
/** Lista chaves (glob opcional, ex.: `user:*`) sem o prefixo. */
|
|
25
|
+
keys(pattern?: string): Promise<string[]>;
|
|
26
|
+
/** Remove entradas expiradas. Redis já expira via TTL — implementação pode retornar 0. */
|
|
27
|
+
cleanup(): Promise<number>;
|
|
28
|
+
}
|
|
29
|
+
export declare function getCache(): CacheAdapter;
|
|
30
|
+
export declare function createCache(config: CacheConfig, redisConfig?: RedisConfig): CacheAdapter;
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEpE,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,WAAW;IAIzB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAYtC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQzC,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAchC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,iEAAiE;IACjE,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,0FAA0F;IAC1F,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5B;AAID,wBAAgB,QAAQ,IAAI,YAAY,CAKvC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,YAAY,CAsCxF"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// @oktis-works/core - Cache Layer (Redis Cluster opcional + fallback memória)
|
|
2
|
+
// Se REDIS_HOST=disabled ou Redis indisponível, usa MemoryCache para não quebrar o CMS.
|
|
3
|
+
// Se REDIS_CLUSTER=true, usa ioredis Cluster; senão single.
|
|
4
|
+
export class MemoryCache {
|
|
5
|
+
store = new Map();
|
|
6
|
+
config;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
}
|
|
10
|
+
async get(key) {
|
|
11
|
+
const entry = this.store.get(key);
|
|
12
|
+
if (!entry)
|
|
13
|
+
return null;
|
|
14
|
+
if (Date.now() > entry.expiresAt) {
|
|
15
|
+
this.store.delete(key);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return entry.value;
|
|
19
|
+
}
|
|
20
|
+
async set(key, value, ttl) {
|
|
21
|
+
const resolvedTtl = ttl ?? this.config.ttl;
|
|
22
|
+
const expiresAt = Date.now() + resolvedTtl * 1000;
|
|
23
|
+
this.store.set(key, { value, expiresAt });
|
|
24
|
+
}
|
|
25
|
+
async del(key) {
|
|
26
|
+
this.store.delete(key);
|
|
27
|
+
}
|
|
28
|
+
async has(key) {
|
|
29
|
+
const entry = this.store.get(key);
|
|
30
|
+
if (!entry)
|
|
31
|
+
return false;
|
|
32
|
+
if (Date.now() > entry.expiresAt) {
|
|
33
|
+
this.store.delete(key);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
async clear() {
|
|
39
|
+
this.store.clear();
|
|
40
|
+
}
|
|
41
|
+
async keys(pattern) {
|
|
42
|
+
const allKeys = Array.from(this.store.keys());
|
|
43
|
+
if (!pattern)
|
|
44
|
+
return allKeys;
|
|
45
|
+
const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`);
|
|
46
|
+
return allKeys.filter(key => regex.test(key));
|
|
47
|
+
}
|
|
48
|
+
async cleanup() {
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
let removed = 0;
|
|
51
|
+
for (const [key, entry] of this.store.entries()) {
|
|
52
|
+
if (now > entry.expiresAt) {
|
|
53
|
+
this.store.delete(key);
|
|
54
|
+
removed++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return removed;
|
|
58
|
+
}
|
|
59
|
+
get size() {
|
|
60
|
+
return this.store.size;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let _cache = null;
|
|
64
|
+
export function getCache() {
|
|
65
|
+
if (!_cache) {
|
|
66
|
+
throw new Error('Cache not initialized. Call createCache() first.');
|
|
67
|
+
}
|
|
68
|
+
return _cache;
|
|
69
|
+
}
|
|
70
|
+
export function createCache(config, redisConfig) {
|
|
71
|
+
// Fallback memória se Redis desabilitado
|
|
72
|
+
if (!redisConfig || redisConfig.enabled === false || redisConfig.host === 'disabled') {
|
|
73
|
+
_cache = new MemoryCache(config);
|
|
74
|
+
return _cache;
|
|
75
|
+
}
|
|
76
|
+
// Tenta Redis, fallback memória em falha — CMS nunca fica sem cache
|
|
77
|
+
try {
|
|
78
|
+
// Lazy import para não quebrar se ioredis não instalado
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
80
|
+
const Redis = require('ioredis');
|
|
81
|
+
const redis = redisConfig.cluster
|
|
82
|
+
? new Redis.Cluster([{ host: redisConfig.host, port: redisConfig.port }], { scaleReads: 'slave' })
|
|
83
|
+
: new Redis({ host: redisConfig.host, port: redisConfig.port, password: redisConfig.password, db: redisConfig.db });
|
|
84
|
+
const adapter = {
|
|
85
|
+
get: async (k) => {
|
|
86
|
+
const v = await redis.get(config.prefix + k);
|
|
87
|
+
return v ? JSON.parse(v) : null;
|
|
88
|
+
},
|
|
89
|
+
set: async (k, v, ttl) => {
|
|
90
|
+
const t = ttl ?? config.ttl;
|
|
91
|
+
await redis.set(config.prefix + k, JSON.stringify(v), 'EX', t);
|
|
92
|
+
},
|
|
93
|
+
del: async (k) => { await redis.del(config.prefix + k); },
|
|
94
|
+
has: async (k) => (await redis.exists(config.prefix + k)) === 1,
|
|
95
|
+
keys: async (pattern) => {
|
|
96
|
+
const found = await redis.keys(config.prefix + (pattern ?? '*'));
|
|
97
|
+
return found.map((k) => (k.startsWith(config.prefix) ? k.slice(config.prefix.length) : k));
|
|
98
|
+
},
|
|
99
|
+
// Redis expira chaves via TTL nativamente — nada a limpar manualmente.
|
|
100
|
+
cleanup: async () => 0,
|
|
101
|
+
};
|
|
102
|
+
_cache = adapter;
|
|
103
|
+
return _cache;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
_cache = new MemoryCache(config);
|
|
107
|
+
return _cache;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Category } from '@oktis-works/types';
|
|
2
|
+
export declare class CategoryService {
|
|
3
|
+
list(): Promise<Category[]>;
|
|
4
|
+
getById(id: string): Promise<Category | null>;
|
|
5
|
+
getBySlug(slug: string): Promise<Category | null>;
|
|
6
|
+
create(input: {
|
|
7
|
+
name: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
parentId?: string;
|
|
11
|
+
}): Promise<Category>;
|
|
12
|
+
update(id: string, input: {
|
|
13
|
+
name?: string;
|
|
14
|
+
slug?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
parentId?: string;
|
|
17
|
+
}): Promise<Category | null>;
|
|
18
|
+
delete(id: string): Promise<boolean>;
|
|
19
|
+
}
|
|
20
|
+
export declare const categoryService: CategoryService;
|
|
21
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/category/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,qBAAa,eAAe;IACpB,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAM3B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAM7C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAMjD,MAAM,CAAC,KAAK,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAcf,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GACA,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAuCrB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAU3C;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { getConnection } from '@oktis-works/database';
|
|
3
|
+
export class CategoryService {
|
|
4
|
+
async list() {
|
|
5
|
+
const sql = getConnection();
|
|
6
|
+
const result = await sql.unsafe('SELECT * FROM categories ORDER BY name ASC');
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
async getById(id) {
|
|
10
|
+
const sql = getConnection();
|
|
11
|
+
const result = await sql.unsafe('SELECT * FROM categories WHERE id = $1', [id]);
|
|
12
|
+
return result[0] ?? null;
|
|
13
|
+
}
|
|
14
|
+
async getBySlug(slug) {
|
|
15
|
+
const sql = getConnection();
|
|
16
|
+
const result = await sql.unsafe('SELECT * FROM categories WHERE slug = $1', [slug]);
|
|
17
|
+
return result[0] ?? null;
|
|
18
|
+
}
|
|
19
|
+
async create(input) {
|
|
20
|
+
const sql = getConnection();
|
|
21
|
+
const id = randomUUID();
|
|
22
|
+
const result = await sql.unsafe(`INSERT INTO categories (id, name, slug, description, parent_id)
|
|
23
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
24
|
+
RETURNING *`, [id, input.name, input.slug, input.description ?? null, input.parentId ?? null]);
|
|
25
|
+
return result[0];
|
|
26
|
+
}
|
|
27
|
+
async update(id, input) {
|
|
28
|
+
const sql = getConnection();
|
|
29
|
+
const existing = await this.getById(id);
|
|
30
|
+
if (!existing)
|
|
31
|
+
return null;
|
|
32
|
+
const setClauses = [];
|
|
33
|
+
const setParams = [];
|
|
34
|
+
if (input.name !== undefined) {
|
|
35
|
+
setClauses.push(`name = $${setParams.length + 1}`);
|
|
36
|
+
setParams.push(input.name);
|
|
37
|
+
}
|
|
38
|
+
if (input.slug !== undefined) {
|
|
39
|
+
setClauses.push(`slug = $${setParams.length + 1}`);
|
|
40
|
+
setParams.push(input.slug);
|
|
41
|
+
}
|
|
42
|
+
if (input.description !== undefined) {
|
|
43
|
+
setClauses.push(`description = $${setParams.length + 1}`);
|
|
44
|
+
setParams.push(input.description);
|
|
45
|
+
}
|
|
46
|
+
if (input.parentId !== undefined) {
|
|
47
|
+
setClauses.push(`parent_id = $${setParams.length + 1}`);
|
|
48
|
+
setParams.push(input.parentId);
|
|
49
|
+
}
|
|
50
|
+
if (setClauses.length === 0)
|
|
51
|
+
return existing;
|
|
52
|
+
setClauses.push(`updated_at = NOW()`);
|
|
53
|
+
setParams.push(id);
|
|
54
|
+
const result = await sql.unsafe(`UPDATE categories SET ${setClauses.join(', ')} WHERE id = $${setParams.length} RETURNING *`, setParams);
|
|
55
|
+
return result[0] ?? null;
|
|
56
|
+
}
|
|
57
|
+
async delete(id) {
|
|
58
|
+
const sql = getConnection();
|
|
59
|
+
const existing = await this.getById(id);
|
|
60
|
+
if (!existing)
|
|
61
|
+
return false;
|
|
62
|
+
await sql.unsafe('DELETE FROM categories WHERE id = $1', [id]);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export const categoryService = new CategoryService();
|
|
67
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/category/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAC,IAAI;QACR,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,4CAA4C,CAAC,CAAC;QAC9E,OAAO,MAA+B,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,wCAAwC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAChF,OAAQ,MAAM,CAAC,CAAC,CAAyB,IAAI,IAAI,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY;QAC1B,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,0CAA0C,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QACpF,OAAQ,MAAM,CAAC,CAAC,CAAyB,IAAI,IAAI,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAKZ;QACC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QAExB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAC7B;;mBAEa,EACb,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAChF,CAAC;QAEF,OAAO,MAAM,CAAC,CAAC,CAAwB,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,MAAM,CACV,EAAU,EACV,KAKC;QAED,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAE5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,SAAS,GAAa,EAAE,CAAC;QAE/B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,UAAU,CAAC,IAAI,CAAC,WAAW,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;YACnD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,UAAU,CAAC,IAAI,CAAC,WAAW,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;YACnD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACpC,UAAU,CAAC,IAAI,CAAC,kBAAkB,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;YAC1D,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,UAAU,CAAC,IAAI,CAAC,gBAAgB,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC;YACxD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,QAAQ,CAAC;QAE7C,UAAU,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACtC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEnB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAC7B,yBAAyB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,SAAS,CAAC,MAAM,cAAc,EAC5F,SAAS,CACV,CAAC;QAEF,OAAQ,MAAM,CAAC,CAAC,CAAyB,IAAI,IAAI,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAE5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAE5B,MAAM,GAAG,CAAC,MAAM,CAAC,sCAAsC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAE/D,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ValidationResult, FieldError } from '../fields/validation.js';
|
|
2
|
+
import type { LocationContext } from '../fields/location-rules.js';
|
|
3
|
+
import type { ValidatableField } from '../fields/types/validators/structural.js';
|
|
4
|
+
export interface ContentFieldsResult {
|
|
5
|
+
groups: Array<{
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
key: string;
|
|
9
|
+
position: string;
|
|
10
|
+
displayStyle: string;
|
|
11
|
+
}>;
|
|
12
|
+
fields: ValidatableField[];
|
|
13
|
+
}
|
|
14
|
+
export declare class ContentFieldResolver {
|
|
15
|
+
/**
|
|
16
|
+
* Resolve os grupos de campos aplicáveis a um contexto de conteúdo
|
|
17
|
+
* (tipo, taxonomia, template, papel do usuário).
|
|
18
|
+
*/
|
|
19
|
+
resolveFor(context: LocationContext): Promise<ContentFieldsResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Valida os dados de campos customizados contra os grupos resolvidos.
|
|
22
|
+
*/
|
|
23
|
+
validate(context: LocationContext, data: Record<string, unknown>): Promise<ValidationResult>;
|
|
24
|
+
/**
|
|
25
|
+
* Validação tolerante: retorna erros sem interromper o fluxo
|
|
26
|
+
* (usado para feedback no editor).
|
|
27
|
+
*/
|
|
28
|
+
validateSoft(context: LocationContext, data: Record<string, unknown>): Promise<FieldError[]>;
|
|
29
|
+
}
|
|
30
|
+
export declare const contentFieldResolver: ContentFieldResolver;
|
|
31
|
+
//# sourceMappingURL=field-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-resolver.d.ts","sourceRoot":"","sources":["../../src/content/field-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClG,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED,qBAAa,oBAAoB;IAC/B;;;OAGG;IACG,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAexE;;OAEG;IACG,QAAQ,CACZ,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,gBAAgB,CAAC;IAK5B;;;OAGG;IACG,YAAY,CAChB,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,UAAU,EAAE,CAAC;CAIzB;AAED,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @oktis-works/core - Content Field Resolver (binds Field Groups to Content)
|
|
2
|
+
import { fieldGroupService } from '../fields/service.js';
|
|
3
|
+
import { validateContentData } from '../fields/validation.js';
|
|
4
|
+
import { expandCloneFields } from '../fields/clone.js';
|
|
5
|
+
export class ContentFieldResolver {
|
|
6
|
+
/**
|
|
7
|
+
* Resolve os grupos de campos aplicáveis a um contexto de conteúdo
|
|
8
|
+
* (tipo, taxonomia, template, papel do usuário).
|
|
9
|
+
*/
|
|
10
|
+
async resolveFor(context) {
|
|
11
|
+
const groups = await fieldGroupService.resolveGroupsByLocation(context);
|
|
12
|
+
return {
|
|
13
|
+
groups: groups.map((group) => ({
|
|
14
|
+
id: group.id,
|
|
15
|
+
title: group.title,
|
|
16
|
+
key: group.key ?? '',
|
|
17
|
+
position: group.position ?? 'normal',
|
|
18
|
+
displayStyle: group.displayStyle ?? 'standard',
|
|
19
|
+
})),
|
|
20
|
+
fields: expandCloneFields(await fieldGroupService.toValidatableFields(groups)),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Valida os dados de campos customizados contra os grupos resolvidos.
|
|
25
|
+
*/
|
|
26
|
+
async validate(context, data) {
|
|
27
|
+
const resolved = await this.resolveFor(context);
|
|
28
|
+
return validateContentData(resolved.fields, data);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validação tolerante: retorna erros sem interromper o fluxo
|
|
32
|
+
* (usado para feedback no editor).
|
|
33
|
+
*/
|
|
34
|
+
async validateSoft(context, data) {
|
|
35
|
+
const result = await this.validate(context, data);
|
|
36
|
+
return result.errors;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const contentFieldResolver = new ContentFieldResolver();
|
|
40
|
+
//# sourceMappingURL=field-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-resolver.js","sourceRoot":"","sources":["../../src/content/field-resolver.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAUvD,MAAM,OAAO,oBAAoB;IAC/B;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,OAAwB;QACvC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;QAExE,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC7B,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,EAAE;gBACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,QAAQ;gBACpC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,UAAU;aAC/C,CAAC,CAAC;YACH,MAAM,EAAE,iBAAiB,CAAC,MAAM,iBAAiB,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;SAC/E,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CACZ,OAAwB,EACxB,IAA6B;QAE7B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAChD,OAAO,mBAAmB,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAChB,OAAwB,EACxB,IAA6B;QAE7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClD,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface ContentTypeDefinition {
|
|
2
|
+
id?: string;
|
|
3
|
+
name: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
pluralLabel: string;
|
|
6
|
+
singularLabel: string;
|
|
7
|
+
source: 'CORE' | 'PLUGIN' | 'ADMIN';
|
|
8
|
+
sourceId?: string | null;
|
|
9
|
+
supports?: Array<'title' | 'editor' | 'thumbnail' | 'excerpt' | 'revisions'>;
|
|
10
|
+
hasArchive?: boolean;
|
|
11
|
+
menuIcon?: string;
|
|
12
|
+
public?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class PostTypeRegistry {
|
|
15
|
+
private cacheKey;
|
|
16
|
+
list(): Promise<ContentTypeDefinition[]>;
|
|
17
|
+
getBySlug(slug: string): Promise<ContentTypeDefinition | null>;
|
|
18
|
+
create(input: Omit<ContentTypeDefinition, 'id' | 'source'> & {
|
|
19
|
+
source?: 'PLUGIN' | 'ADMIN';
|
|
20
|
+
sourceId?: string;
|
|
21
|
+
}): Promise<ContentTypeDefinition>;
|
|
22
|
+
update(slug: string, patch: Partial<ContentTypeDefinition>): Promise<ContentTypeDefinition | null>;
|
|
23
|
+
delete(slug: string): Promise<boolean>;
|
|
24
|
+
private mergeCore;
|
|
25
|
+
private rowToDefinition;
|
|
26
|
+
}
|
|
27
|
+
export declare const postTypeRegistry: PostTypeRegistry;
|
|
28
|
+
//# sourceMappingURL=post-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-types.d.ts","sourceRoot":"","sources":["../../src/content/post-types.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IACpC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,CAAC,CAAC;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA2BD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAuB;IAEjC,IAAI,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAiBxC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;IAM9D,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,IAAI,GAAG,QAAQ,CAAC,GAAG;QAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAiChJ,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;IA8BlG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc5C,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,eAAe;CAiBxB;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// @oktis-works/core - Content Type Registry (Custom Post Types)
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { getConnection } from '@oktis-works/database';
|
|
4
|
+
import { getCache } from '../cache/index.js';
|
|
5
|
+
const CORE_TYPES = [
|
|
6
|
+
{
|
|
7
|
+
name: 'post',
|
|
8
|
+
slug: 'post',
|
|
9
|
+
pluralLabel: 'Posts',
|
|
10
|
+
singularLabel: 'Post',
|
|
11
|
+
source: 'CORE',
|
|
12
|
+
supports: ['title', 'editor', 'thumbnail', 'excerpt', 'revisions'],
|
|
13
|
+
hasArchive: true,
|
|
14
|
+
menuIcon: 'file-text',
|
|
15
|
+
public: true,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'page',
|
|
19
|
+
slug: 'page',
|
|
20
|
+
pluralLabel: 'Páginas',
|
|
21
|
+
singularLabel: 'Página',
|
|
22
|
+
source: 'CORE',
|
|
23
|
+
supports: ['title', 'editor', 'thumbnail', 'revisions'],
|
|
24
|
+
hasArchive: false,
|
|
25
|
+
menuIcon: 'file',
|
|
26
|
+
public: true,
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
export class PostTypeRegistry {
|
|
30
|
+
cacheKey = 'content-types:all';
|
|
31
|
+
async list() {
|
|
32
|
+
const sql = getConnection();
|
|
33
|
+
const result = await sql.unsafe('SELECT * FROM content_types ORDER BY source ASC, plural_label ASC');
|
|
34
|
+
const dbTypes = result.map((row) => this.rowToDefinition(row));
|
|
35
|
+
const bySlug = new Map();
|
|
36
|
+
for (const coreType of CORE_TYPES)
|
|
37
|
+
bySlug.set(coreType.slug, coreType);
|
|
38
|
+
for (const dbType of dbTypes)
|
|
39
|
+
bySlug.set(dbType.slug, { ...dbType, ...this.mergeCore(dbType.slug, dbType) });
|
|
40
|
+
return [...bySlug.values()];
|
|
41
|
+
}
|
|
42
|
+
async getBySlug(slug) {
|
|
43
|
+
const core = CORE_TYPES.find((type) => type.slug === slug);
|
|
44
|
+
const all = await this.list();
|
|
45
|
+
return all.find((type) => type.slug === slug) ?? core ?? null;
|
|
46
|
+
}
|
|
47
|
+
async create(input) {
|
|
48
|
+
const sql = getConnection();
|
|
49
|
+
const existing = await this.getBySlug(input.slug);
|
|
50
|
+
if (existing)
|
|
51
|
+
throw new Error(`Content type "${input.slug}" já existe`);
|
|
52
|
+
const id = randomUUID();
|
|
53
|
+
await sql.unsafe(`INSERT INTO content_types (id, name, slug, source, source_id, schema, plural_label, singular_label)
|
|
54
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7, $8)`, [
|
|
55
|
+
id,
|
|
56
|
+
input.name,
|
|
57
|
+
input.slug,
|
|
58
|
+
input.source ?? 'ADMIN',
|
|
59
|
+
input.sourceId ?? null,
|
|
60
|
+
JSON.stringify({
|
|
61
|
+
supports: input.supports ?? ['title', 'editor', 'revisions'],
|
|
62
|
+
hasArchive: input.hasArchive ?? false,
|
|
63
|
+
menuIcon: input.menuIcon ?? 'box',
|
|
64
|
+
public: input.public ?? true,
|
|
65
|
+
}),
|
|
66
|
+
input.pluralLabel,
|
|
67
|
+
input.singularLabel,
|
|
68
|
+
]);
|
|
69
|
+
await getCache().del(this.cacheKey);
|
|
70
|
+
return (await this.getBySlug(input.slug));
|
|
71
|
+
}
|
|
72
|
+
async update(slug, patch) {
|
|
73
|
+
const sql = getConnection();
|
|
74
|
+
const existingRow = await sql.unsafe('SELECT * FROM content_types WHERE slug = $1', [slug]);
|
|
75
|
+
const row = existingRow[0];
|
|
76
|
+
if (!row)
|
|
77
|
+
throw new Error(`Content type "${slug}" é do core e não pode ser alterado`);
|
|
78
|
+
const current = this.rowToDefinition(row);
|
|
79
|
+
const merged = { ...current, ...patch, slug };
|
|
80
|
+
await sql.unsafe(`UPDATE content_types SET schema = $1::jsonb, plural_label = $2, singular_label = $3, updated_at = NOW()
|
|
81
|
+
WHERE slug = $4`, [
|
|
82
|
+
JSON.stringify({
|
|
83
|
+
supports: merged.supports ?? [],
|
|
84
|
+
hasArchive: merged.hasArchive ?? false,
|
|
85
|
+
menuIcon: merged.menuIcon,
|
|
86
|
+
public: merged.public ?? true,
|
|
87
|
+
}),
|
|
88
|
+
merged.pluralLabel,
|
|
89
|
+
merged.singularLabel,
|
|
90
|
+
slug,
|
|
91
|
+
]);
|
|
92
|
+
await getCache().del(this.cacheKey);
|
|
93
|
+
return this.getBySlug(slug);
|
|
94
|
+
}
|
|
95
|
+
async delete(slug) {
|
|
96
|
+
if (CORE_TYPES.some((type) => type.slug === slug)) {
|
|
97
|
+
throw new Error(`Content type "${slug}" é do core e não pode ser removido`);
|
|
98
|
+
}
|
|
99
|
+
const sql = getConnection();
|
|
100
|
+
const result = await sql.unsafe('DELETE FROM content_types WHERE slug = $1 RETURNING id', [slug]);
|
|
101
|
+
const deleted = result.length > 0;
|
|
102
|
+
if (deleted)
|
|
103
|
+
await getCache().del(this.cacheKey);
|
|
104
|
+
return deleted;
|
|
105
|
+
}
|
|
106
|
+
mergeCore(_slug, dbType) {
|
|
107
|
+
return dbType;
|
|
108
|
+
}
|
|
109
|
+
rowToDefinition(row) {
|
|
110
|
+
const schemaJson = row['schema'] ?? {};
|
|
111
|
+
return {
|
|
112
|
+
id: String(row['id']),
|
|
113
|
+
name: String(row['name']),
|
|
114
|
+
slug: String(row['slug']),
|
|
115
|
+
pluralLabel: String(row['plural_label']),
|
|
116
|
+
singularLabel: String(row['singular_label']),
|
|
117
|
+
source: row['source'] ?? 'ADMIN',
|
|
118
|
+
sourceId: row['source_id'] ?? null,
|
|
119
|
+
supports: schemaJson['supports'] ?? ['title', 'editor'],
|
|
120
|
+
hasArchive: Boolean(schemaJson['hasArchive']),
|
|
121
|
+
menuIcon: schemaJson['menuIcon'],
|
|
122
|
+
public: schemaJson['public'] !== false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export const postTypeRegistry = new PostTypeRegistry();
|
|
127
|
+
//# sourceMappingURL=post-types.js.map
|