includio-cms 0.7.2 → 0.13.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/CHANGELOG.md +110 -0
- package/ROADMAP.md +40 -2
- package/dist/admin/api/generate-styles.d.ts +2 -0
- package/dist/admin/api/generate-styles.js +32 -0
- package/dist/admin/api/handler.js +33 -0
- package/dist/admin/api/media-gc.js +10 -4
- package/dist/admin/api/rest/handler.js +17 -0
- package/dist/admin/api/rest/routes/collections.js +25 -13
- package/dist/admin/api/rest/routes/entries.d.ts +1 -1
- package/dist/admin/api/rest/routes/entries.js +10 -10
- package/dist/admin/api/rest/routes/media.d.ts +2 -0
- package/dist/admin/api/rest/routes/media.js +9 -0
- package/dist/admin/api/rest/routes/schema.d.ts +5 -0
- package/dist/admin/api/rest/routes/schema.js +152 -0
- package/dist/admin/api/rest/routes/singletons.d.ts +1 -1
- package/dist/admin/api/rest/routes/singletons.js +8 -7
- package/dist/admin/api/rest/routes/upload.d.ts +2 -0
- package/dist/admin/api/rest/routes/upload.js +28 -0
- package/dist/admin/api/upload.js +13 -0
- package/dist/admin/client/collection/collection-entries.svelte +19 -6
- package/dist/admin/client/entry/entry.svelte +21 -23
- package/dist/admin/client/entry/header/a11y-validator.js +2 -2
- package/dist/admin/client/entry/header/publish-panel.svelte +33 -85
- package/dist/admin/client/entry/header/status-badge.svelte +2 -2
- package/dist/admin/client/entry/header/version-history-sheet.svelte +9 -9
- package/dist/admin/client/entry/header/visibility.svelte +16 -10
- package/dist/admin/client/entry/utils.d.ts +3 -0
- package/dist/admin/client/entry/utils.js +22 -4
- package/dist/admin/client/form/form-submission/form-submission-page.svelte +4 -1
- package/dist/admin/client/form/form-submission/submission-field.svelte +10 -0
- package/dist/admin/client/index.d.ts +1 -0
- package/dist/admin/client/index.js +1 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +146 -2
- package/dist/admin/components/fields/blocks-field.svelte +9 -10
- package/dist/admin/components/fields/field-renderer.svelte +4 -8
- package/dist/admin/components/fields/object-field.svelte +7 -12
- package/dist/admin/components/fields/select-field.svelte +8 -2
- package/dist/admin/components/fields/seo-field.svelte +40 -93
- package/dist/admin/components/fields/simple-array-field.svelte +5 -5
- package/dist/admin/components/fields/text-field-wrapper.svelte +52 -197
- package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
- package/dist/admin/components/fields/url-field-wrapper.svelte +15 -25
- package/dist/admin/components/fields/url-field.svelte +61 -72
- package/dist/admin/components/media/file-upload.svelte +5 -1
- package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
- package/dist/admin/components/media/media-library.svelte +109 -37
- package/dist/admin/components/media/media-selector.svelte +79 -11
- package/dist/admin/components/media/tag-sidebar.svelte +10 -6
- package/dist/admin/components/media/tag-sidebar.svelte.d.ts +7 -2
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +21 -93
- package/dist/admin/components/tiptap/inline-block-node.js +6 -5
- package/dist/admin/components/tiptap/link-dialog.svelte +10 -11
- package/dist/admin/components/tiptap/slash-command.js +1 -1
- package/dist/admin/remote/entry.remote.d.ts +2 -5
- package/dist/admin/remote/entry.remote.js +22 -27
- package/dist/admin/remote/media.remote.d.ts +15 -0
- package/dist/admin/remote/media.remote.js +18 -2
- package/dist/admin/remote/preview.remote.js +3 -1
- package/dist/admin/utils/entryLabel.js +9 -6
- package/dist/admin/utils/translationStatus.js +1 -2
- package/dist/cli/scaffold/admin.js +34 -2
- package/dist/cms/runtime/api.d.ts +16 -12
- package/dist/cms/runtime/api.js +7 -6
- package/dist/cms/runtime/remote.js +2 -2
- package/dist/cms/runtime/schemas.d.ts +1 -1
- package/dist/cms/runtime/schemas.js +1 -1
- package/dist/cms/runtime/types.d.ts +118 -112
- package/dist/cms/runtime/types.js +0 -12
- package/dist/core/cms.d.ts +3 -1
- package/dist/core/cms.js +30 -0
- package/dist/core/fields/fieldSchemaToTs.js +9 -15
- package/dist/core/fields/formFieldSchemaToTs.js +7 -0
- package/dist/core/server/entries/operations/create.js +10 -4
- package/dist/core/server/entries/operations/get.d.ts +1 -0
- package/dist/core/server/entries/operations/get.js +186 -191
- package/dist/core/server/entries/operations/update.d.ts +6 -7
- package/dist/core/server/entries/operations/update.js +20 -38
- package/dist/core/server/fields/populateEntry.js +16 -52
- package/dist/core/server/fields/resolveImageFields.js +69 -120
- package/dist/core/server/fields/resolveRelationFields.js +30 -51
- package/dist/core/server/fields/resolveRichtextLinks.js +46 -100
- package/dist/core/server/fields/resolveTypographyOrphans.bench.d.ts +1 -0
- package/dist/core/server/fields/resolveTypographyOrphans.bench.js +87 -0
- package/dist/core/server/fields/resolveTypographyOrphans.d.ts +3 -0
- package/dist/core/server/fields/resolveTypographyOrphans.js +128 -0
- package/dist/core/server/fields/resolveUrlFields.js +47 -56
- package/dist/core/server/fields/utils/fixOrphans.d.ts +5 -0
- package/dist/core/server/fields/utils/fixOrphans.js +12 -0
- package/dist/core/server/fields/utils/imageStyles.d.ts +4 -2
- package/dist/core/server/fields/utils/imageStyles.js +41 -25
- package/dist/core/server/fields/utils/resolveMedia.js +1 -6
- package/dist/core/server/forms/submissions/operations/delete.js +26 -2
- package/dist/core/server/forms/submissions/utils/parseMultipart.d.ts +2 -0
- package/dist/core/server/forms/submissions/utils/parseMultipart.js +75 -0
- package/dist/core/server/generator/fields.d.ts +6 -0
- package/dist/core/server/generator/fields.js +43 -5
- package/dist/core/server/generator/formFieldSchemaToString.js +10 -0
- package/dist/core/server/generator/formFields.js +1 -0
- package/dist/core/server/generator/generator.js +98 -30
- package/dist/core/server/media/operations/getFiles.d.ts +5 -0
- package/dist/core/server/media/operations/getFiles.js +6 -0
- package/dist/core/server/media/operations/uploadPrivateFile.d.ts +4 -0
- package/dist/core/server/media/operations/uploadPrivateFile.js +8 -0
- package/dist/core/server/media/styles/operations/batchGenerateStyles.d.ts +16 -0
- package/dist/core/server/media/styles/operations/batchGenerateStyles.js +144 -0
- package/dist/db-postgres/index.js +303 -37
- package/dist/db-postgres/schema/entry.d.ts +0 -94
- package/dist/db-postgres/schema/entry.js +0 -6
- package/dist/db-postgres/schema/entryVersion.d.ts +17 -0
- package/dist/db-postgres/schema/entryVersion.js +1 -0
- package/dist/entity/index.d.ts +9 -4
- package/dist/entity/index.js +24 -24
- package/dist/files-local/index.js +43 -0
- package/dist/paraglide/messages/_index.d.ts +36 -3
- package/dist/paraglide/messages/_index.js +71 -3
- package/dist/paraglide/messages/en.d.ts +5 -0
- package/dist/paraglide/messages/en.js +14 -0
- package/dist/paraglide/messages/pl.d.ts +5 -0
- package/dist/paraglide/messages/pl.js +14 -0
- package/dist/sveltekit/components/preview.svelte +2 -326
- package/dist/sveltekit/components/preview.svelte.d.ts +5 -16
- package/dist/sveltekit/server/index.d.ts +2 -1
- package/dist/sveltekit/server/index.js +2 -1
- package/dist/sveltekit/server/preview.js +4 -7
- package/dist/types/adapters/db.d.ts +15 -1
- package/dist/types/adapters/files.d.ts +6 -0
- package/dist/types/cms.d.ts +5 -0
- package/dist/types/entries.d.ts +54 -18
- package/dist/types/fields.d.ts +14 -24
- package/dist/types/formFields.d.ts +7 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/structured-content.d.ts +5 -0
- package/dist/updates/0.10.0/index.d.ts +2 -0
- package/dist/updates/0.10.0/index.js +15 -0
- package/dist/updates/0.11.0/index.d.ts +2 -0
- package/dist/updates/0.11.0/index.js +12 -0
- package/dist/updates/0.12.0/index.d.ts +2 -0
- package/dist/updates/0.12.0/index.js +12 -0
- package/dist/updates/0.13.0/index.d.ts +2 -0
- package/dist/updates/0.13.0/index.js +10 -0
- package/dist/updates/0.7.3/index.d.ts +2 -0
- package/dist/updates/0.7.3/index.js +10 -0
- package/dist/updates/0.8.0/index.d.ts +2 -0
- package/dist/updates/0.8.0/index.js +18 -0
- package/dist/updates/0.8.0/migrate.d.ts +2 -0
- package/dist/updates/0.8.0/migrate.js +101 -0
- package/dist/updates/0.9.0/index.d.ts +2 -0
- package/dist/updates/0.9.0/index.js +38 -0
- package/dist/updates/index.js +8 -1
- package/package.json +7 -6
- package/dist/admin/components/fields/image-field.svelte +0 -198
- package/dist/admin/components/fields/image-field.svelte.d.ts +0 -8
- package/dist/admin/components/fields/richtext-field.svelte +0 -13
- package/dist/admin/components/fields/richtext-field.svelte.d.ts +0 -8
- package/dist/admin/components/tiptap.svelte +0 -11
- package/dist/admin/components/tiptap.svelte.d.ts +0 -6
- package/dist/core/server/entries/utils/getEntryTranslation.d.ts +0 -1
- package/dist/core/server/entries/utils/getEntryTranslation.js +0 -18
- package/dist/paraglide/messages/hello_world.d.ts +0 -5
- package/dist/paraglide/messages/hello_world.js +0 -33
- package/dist/paraglide/messages/login_hello.d.ts +0 -16
- package/dist/paraglide/messages/login_hello.js +0 -34
- package/dist/paraglide/messages/login_please_login.d.ts +0 -16
- package/dist/paraglide/messages/login_please_login.js +0 -34
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { getCMS } from '../../../../cms.js';
|
|
2
|
+
import { isProcessableImage, defaultStyles, expandStyleFormats, getOriginalFormat } from '../../../fields/utils/imageStyles.js';
|
|
3
|
+
import { getImageStyle } from './getImageStyle.js';
|
|
4
|
+
const IMAGE_MIME_TYPES = [
|
|
5
|
+
'image/jpeg',
|
|
6
|
+
'image/png',
|
|
7
|
+
'image/webp',
|
|
8
|
+
'image/avif',
|
|
9
|
+
'image/gif',
|
|
10
|
+
'image/tiff'
|
|
11
|
+
];
|
|
12
|
+
async function generateStylesForFile(file) {
|
|
13
|
+
const cms = getCMS();
|
|
14
|
+
const origFormat = getOriginalFormat(file);
|
|
15
|
+
const expanded = expandStyleFormats(defaultStyles, origFormat);
|
|
16
|
+
let created = 0;
|
|
17
|
+
let skipped = 0;
|
|
18
|
+
for (const style of expanded) {
|
|
19
|
+
const existing = await cms.databaseAdapter.getImageStyle(file.id, style);
|
|
20
|
+
if (existing) {
|
|
21
|
+
skipped++;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
await getImageStyle(file.id, style);
|
|
25
|
+
created++;
|
|
26
|
+
}
|
|
27
|
+
if (style.srcset && file.width) {
|
|
28
|
+
const widths = style.srcset.filter((w) => w <= file.width);
|
|
29
|
+
for (const w of widths) {
|
|
30
|
+
const widthStyle = {
|
|
31
|
+
...style,
|
|
32
|
+
name: `${style.name}_${w}w`,
|
|
33
|
+
width: w,
|
|
34
|
+
srcset: undefined,
|
|
35
|
+
sizes: undefined
|
|
36
|
+
};
|
|
37
|
+
const existingW = await cms.databaseAdapter.getImageStyle(file.id, widthStyle);
|
|
38
|
+
if (existingW) {
|
|
39
|
+
skipped++;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
await getImageStyle(file.id, widthStyle);
|
|
43
|
+
created++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { created, skipped };
|
|
49
|
+
}
|
|
50
|
+
export async function* batchGenerateAllStyles(signal) {
|
|
51
|
+
const allFiles = await getCMS().databaseAdapter.getMediaFiles({
|
|
52
|
+
data: { mimeTypes: IMAGE_MIME_TYPES }
|
|
53
|
+
});
|
|
54
|
+
const files = allFiles.filter(isProcessableImage);
|
|
55
|
+
const total = files.length;
|
|
56
|
+
let totalCreated = 0;
|
|
57
|
+
let totalSkipped = 0;
|
|
58
|
+
for (let i = 0; i < files.length; i++) {
|
|
59
|
+
if (signal?.aborted)
|
|
60
|
+
return;
|
|
61
|
+
const file = files[i];
|
|
62
|
+
yield {
|
|
63
|
+
type: 'progress',
|
|
64
|
+
total,
|
|
65
|
+
processed: i,
|
|
66
|
+
created: totalCreated,
|
|
67
|
+
skipped: totalSkipped,
|
|
68
|
+
currentFile: file.name
|
|
69
|
+
};
|
|
70
|
+
try {
|
|
71
|
+
const { created, skipped } = await generateStylesForFile(file);
|
|
72
|
+
totalCreated += created;
|
|
73
|
+
totalSkipped += skipped;
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
yield {
|
|
77
|
+
type: 'error',
|
|
78
|
+
total,
|
|
79
|
+
processed: i + 1,
|
|
80
|
+
created: totalCreated,
|
|
81
|
+
skipped: totalSkipped,
|
|
82
|
+
currentFile: file.name,
|
|
83
|
+
error: e instanceof Error ? e.message : String(e)
|
|
84
|
+
};
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
yield { type: 'done', total, processed: total, created: totalCreated, skipped: totalSkipped };
|
|
89
|
+
}
|
|
90
|
+
function getExpectedStyleNames(file) {
|
|
91
|
+
const origFormat = getOriginalFormat(file);
|
|
92
|
+
const expanded = expandStyleFormats(defaultStyles, origFormat);
|
|
93
|
+
const names = [];
|
|
94
|
+
for (const style of expanded) {
|
|
95
|
+
names.push(style.name);
|
|
96
|
+
if (style.srcset && file.width) {
|
|
97
|
+
for (const w of style.srcset.filter((sw) => sw <= file.width)) {
|
|
98
|
+
names.push(`${style.name}_${w}w`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return names;
|
|
103
|
+
}
|
|
104
|
+
export async function getStylesStatus() {
|
|
105
|
+
const cms = getCMS();
|
|
106
|
+
const [allFiles, allStyles] = await Promise.all([
|
|
107
|
+
cms.databaseAdapter.getMediaFiles({ data: { mimeTypes: IMAGE_MIME_TYPES } }),
|
|
108
|
+
cms.databaseAdapter.getAllImageStyles()
|
|
109
|
+
]);
|
|
110
|
+
const files = allFiles.filter(isProcessableImage);
|
|
111
|
+
// Group existing styles by mediaFileId → set of style names
|
|
112
|
+
const existingByFile = new Map();
|
|
113
|
+
for (const s of allStyles) {
|
|
114
|
+
const fileId = s.mediaFileId;
|
|
115
|
+
if (!existingByFile.has(fileId))
|
|
116
|
+
existingByFile.set(fileId, new Set());
|
|
117
|
+
// We only have id/url/mediaFileId from getAllImageStyles, not name.
|
|
118
|
+
// So we count per-file total instead
|
|
119
|
+
}
|
|
120
|
+
// Count expected per-file and compare with existing count per-file
|
|
121
|
+
let expectedStyles = 0;
|
|
122
|
+
let missingStyles = 0;
|
|
123
|
+
// Count existing per mediaFileId
|
|
124
|
+
const existingCountByFile = new Map();
|
|
125
|
+
for (const s of allStyles) {
|
|
126
|
+
const fileId = s.mediaFileId;
|
|
127
|
+
existingCountByFile.set(fileId, (existingCountByFile.get(fileId) ?? 0) + 1);
|
|
128
|
+
}
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
const expectedNames = getExpectedStyleNames(file);
|
|
131
|
+
const expected = expectedNames.length;
|
|
132
|
+
const existing = existingCountByFile.get(file.id) ?? 0;
|
|
133
|
+
expectedStyles += expected;
|
|
134
|
+
if (existing < expected) {
|
|
135
|
+
missingStyles += expected - existing;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
processableImages: files.length,
|
|
140
|
+
expectedStyles,
|
|
141
|
+
existingStyles: allStyles.length,
|
|
142
|
+
missingStyles
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -56,6 +56,142 @@ function buildJsonLikeConditions(dataLike) {
|
|
|
56
56
|
});
|
|
57
57
|
return conditions;
|
|
58
58
|
}
|
|
59
|
+
// Helper function to build JSON path conditions (ILIKE match, OR logic)
|
|
60
|
+
function buildJsonILikeOrConditions(dataILikeOr) {
|
|
61
|
+
const conditions = [];
|
|
62
|
+
function processValue(value, path = []) {
|
|
63
|
+
if (value === null || value === undefined)
|
|
64
|
+
return;
|
|
65
|
+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
66
|
+
Object.entries(value).forEach(([key, nestedValue]) => {
|
|
67
|
+
processValue(nestedValue, [...path, key]);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
validateJsonPathKeys(path);
|
|
72
|
+
const jsonPath = path.join(',');
|
|
73
|
+
conditions.push(sql `${schema.entryVersionsTable.data}#>>'{${sql.raw(jsonPath)}}' ILIKE ${`%${String(value)}%`}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
Object.entries(dataILikeOr).forEach(([key, value]) => {
|
|
77
|
+
processValue(value, [key]);
|
|
78
|
+
});
|
|
79
|
+
if (conditions.length === 0)
|
|
80
|
+
return undefined;
|
|
81
|
+
if (conditions.length === 1)
|
|
82
|
+
return conditions[0];
|
|
83
|
+
return sql `(${sql.join(conditions, sql ` OR `)})`;
|
|
84
|
+
}
|
|
85
|
+
// Raw SQL condition builders for CTE queries (use v.column / e.column aliases)
|
|
86
|
+
function buildRawJsonConditions(dataValue) {
|
|
87
|
+
const conditions = [];
|
|
88
|
+
function processValue(value, path = []) {
|
|
89
|
+
if (value === null || value === undefined)
|
|
90
|
+
return;
|
|
91
|
+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
92
|
+
Object.entries(value).forEach(([key, nestedValue]) => {
|
|
93
|
+
processValue(nestedValue, [...path, key]);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
validateJsonPathKeys(path);
|
|
98
|
+
const jsonPath = path.join(',');
|
|
99
|
+
conditions.push(sql `v.data#>>'{${sql.raw(jsonPath)}}' = ${String(value)}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
Object.entries(dataValue).forEach(([key, value]) => {
|
|
103
|
+
processValue(value, [key]);
|
|
104
|
+
});
|
|
105
|
+
return conditions;
|
|
106
|
+
}
|
|
107
|
+
function buildRawJsonLikeConditions(dataLike) {
|
|
108
|
+
const conditions = [];
|
|
109
|
+
function processValue(value, path = []) {
|
|
110
|
+
if (value === null || value === undefined)
|
|
111
|
+
return;
|
|
112
|
+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
113
|
+
Object.entries(value).forEach(([key, nestedValue]) => {
|
|
114
|
+
processValue(nestedValue, [...path, key]);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
validateJsonPathKeys(path);
|
|
119
|
+
const jsonPath = path.join(',');
|
|
120
|
+
conditions.push(sql `v.data#>>'{${sql.raw(jsonPath)}}' LIKE ${`%${String(value)}%`}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
Object.entries(dataLike).forEach(([key, value]) => {
|
|
124
|
+
processValue(value, [key]);
|
|
125
|
+
});
|
|
126
|
+
return conditions;
|
|
127
|
+
}
|
|
128
|
+
function buildRawJsonILikeOrConditions(dataILikeOr) {
|
|
129
|
+
const conditions = [];
|
|
130
|
+
function processValue(value, path = []) {
|
|
131
|
+
if (value === null || value === undefined)
|
|
132
|
+
return;
|
|
133
|
+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
134
|
+
Object.entries(value).forEach(([key, nestedValue]) => {
|
|
135
|
+
processValue(nestedValue, [...path, key]);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
validateJsonPathKeys(path);
|
|
140
|
+
const jsonPath = path.join(',');
|
|
141
|
+
conditions.push(sql `v.data#>>'{${sql.raw(jsonPath)}}' ILIKE ${`%${String(value)}%`}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
Object.entries(dataILikeOr).forEach(([key, value]) => {
|
|
145
|
+
processValue(value, [key]);
|
|
146
|
+
});
|
|
147
|
+
if (conditions.length === 0)
|
|
148
|
+
return undefined;
|
|
149
|
+
if (conditions.length === 1)
|
|
150
|
+
return conditions[0];
|
|
151
|
+
return sql `(${sql.join(conditions, sql ` OR `)})`;
|
|
152
|
+
}
|
|
153
|
+
function buildCteVersionConditions(options) {
|
|
154
|
+
const conditions = [sql `v.lang = ${options.language}`];
|
|
155
|
+
if (options.status === 'published') {
|
|
156
|
+
conditions.push(sql `v.published_at IS NOT NULL`);
|
|
157
|
+
conditions.push(sql `v.published_at <= NOW()`);
|
|
158
|
+
}
|
|
159
|
+
else if (options.status === 'draft') {
|
|
160
|
+
conditions.push(sql `v.published_at IS NULL`);
|
|
161
|
+
}
|
|
162
|
+
else if (options.status === 'scheduled') {
|
|
163
|
+
conditions.push(sql `v.published_at IS NOT NULL`);
|
|
164
|
+
conditions.push(sql `v.published_at > NOW()`);
|
|
165
|
+
}
|
|
166
|
+
if (options.dataValues) {
|
|
167
|
+
conditions.push(...buildRawJsonConditions(options.dataValues));
|
|
168
|
+
}
|
|
169
|
+
if (options.dataLike) {
|
|
170
|
+
conditions.push(...buildRawJsonLikeConditions(options.dataLike));
|
|
171
|
+
}
|
|
172
|
+
if (options.dataILikeOr) {
|
|
173
|
+
const orCondition = buildRawJsonILikeOrConditions(options.dataILikeOr);
|
|
174
|
+
if (orCondition)
|
|
175
|
+
conditions.push(orCondition);
|
|
176
|
+
}
|
|
177
|
+
return conditions;
|
|
178
|
+
}
|
|
179
|
+
function buildCteEntryConditions(options) {
|
|
180
|
+
const conditions = [];
|
|
181
|
+
if (options.status === 'archived') {
|
|
182
|
+
conditions.push(sql `e.archived_at IS NOT NULL`);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
conditions.push(sql `e.archived_at IS NULL`);
|
|
186
|
+
}
|
|
187
|
+
if (options.slug) {
|
|
188
|
+
conditions.push(sql `e.slug = ${options.slug}`);
|
|
189
|
+
}
|
|
190
|
+
if (options.ids && options.ids.length > 0) {
|
|
191
|
+
conditions.push(sql `e.id = ANY(${options.ids})`);
|
|
192
|
+
}
|
|
193
|
+
return conditions;
|
|
194
|
+
}
|
|
59
195
|
/** Hydrate tags onto raw media file rows */
|
|
60
196
|
async function hydrateFileTags(db, files) {
|
|
61
197
|
if (files.length === 0)
|
|
@@ -84,6 +220,48 @@ async function hydrateFileTags(db, files) {
|
|
|
84
220
|
}
|
|
85
221
|
return files.map((f) => ({ ...f, tags: fileTagMap.get(f.id) || [] }));
|
|
86
222
|
}
|
|
223
|
+
function buildMediaFileConditions(db, options) {
|
|
224
|
+
const conditions = [];
|
|
225
|
+
if (options.data) {
|
|
226
|
+
if (options.data.search) {
|
|
227
|
+
conditions.push(or(ilike(schema.mediaFilesTable.name, `%${options.data.search}%`), ilike(schema.mediaFilesTable.alt, `%${options.data.search}%`)));
|
|
228
|
+
}
|
|
229
|
+
if (options.data.ids) {
|
|
230
|
+
conditions.push(inArray(schema.mediaFilesTable.id, options.data.ids));
|
|
231
|
+
}
|
|
232
|
+
if (options.data.mimeTypes) {
|
|
233
|
+
const exact = options.data.mimeTypes.filter((t) => !t.endsWith('/*'));
|
|
234
|
+
const wildcards = options.data.mimeTypes.filter((t) => t.endsWith('/*'));
|
|
235
|
+
const mimeTypeConditions = wildcards.map((t) => ilike(schema.mediaFilesTable.mimeType, t.replace('*', '%')));
|
|
236
|
+
const typeConditions = wildcards.map((t) => {
|
|
237
|
+
const baseType = t.replace('/*', '');
|
|
238
|
+
return eq(schema.mediaFilesTable.type, baseType);
|
|
239
|
+
});
|
|
240
|
+
const mimeConditions = [
|
|
241
|
+
...(exact.length ? [inArray(schema.mediaFilesTable.mimeType, exact)] : []),
|
|
242
|
+
...mimeTypeConditions,
|
|
243
|
+
...typeConditions
|
|
244
|
+
];
|
|
245
|
+
if (mimeConditions.length) {
|
|
246
|
+
conditions.push(or(...mimeConditions));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (options.data.tagIds && options.data.tagIds.length > 0) {
|
|
250
|
+
const fileIdsWithTags = db
|
|
251
|
+
.select({ fileId: schema.mediaFileTagsTable.fileId })
|
|
252
|
+
.from(schema.mediaFileTagsTable)
|
|
253
|
+
.where(inArray(schema.mediaFileTagsTable.tagId, options.data.tagIds));
|
|
254
|
+
conditions.push(inArray(schema.mediaFilesTable.id, fileIdsWithTags));
|
|
255
|
+
}
|
|
256
|
+
if (options.data.untagged) {
|
|
257
|
+
const fileIdsWithAnyTag = db
|
|
258
|
+
.select({ fileId: schema.mediaFileTagsTable.fileId })
|
|
259
|
+
.from(schema.mediaFileTagsTable);
|
|
260
|
+
conditions.push(sql `${schema.mediaFilesTable.id} NOT IN (${fileIdsWithAnyTag})`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return conditions;
|
|
264
|
+
}
|
|
87
265
|
export function pg(config) {
|
|
88
266
|
const client = postgres(config.databaseUrl);
|
|
89
267
|
const db = drizzle(client, { schema });
|
|
@@ -142,6 +320,92 @@ export function pg(config) {
|
|
|
142
320
|
.where(and(...conditions));
|
|
143
321
|
return result?.count ?? 0;
|
|
144
322
|
},
|
|
323
|
+
getPaginatedEntries: async (options) => {
|
|
324
|
+
const versionWhereParts = buildCteVersionConditions(options);
|
|
325
|
+
const versionWhere = sql.join(versionWhereParts, sql ` AND `);
|
|
326
|
+
const entryWhereParts = buildCteEntryConditions(options);
|
|
327
|
+
const entryWhere = sql.join(entryWhereParts, sql ` AND `);
|
|
328
|
+
let orderClause;
|
|
329
|
+
if (options.dataOrderBy) {
|
|
330
|
+
const field = options.dataOrderBy.field;
|
|
331
|
+
if (!SAFE_JSON_KEY.test(field)) {
|
|
332
|
+
throw new Error(`Invalid dataOrderBy field: "${field}"`);
|
|
333
|
+
}
|
|
334
|
+
const dir = options.dataOrderBy.direction === 'desc' ? 'DESC' : 'ASC';
|
|
335
|
+
orderClause = sql.raw(`m.data->>'${field}' ${dir}`);
|
|
336
|
+
}
|
|
337
|
+
else if (options.orderBy) {
|
|
338
|
+
const col = options.orderBy.column === 'sortOrder' ? 'sort_order' : options.orderBy.column === 'createdAt' ? 'created_at' : 'updated_at';
|
|
339
|
+
orderClause = sql.raw(`e.${col} ${options.orderBy.direction}`);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
orderClause = sql.raw(`e.sort_order ASC`);
|
|
343
|
+
}
|
|
344
|
+
const rows = await db.execute(sql `
|
|
345
|
+
WITH matched AS (
|
|
346
|
+
SELECT DISTINCT ON (v.entry_id)
|
|
347
|
+
v.*
|
|
348
|
+
FROM entry_version v
|
|
349
|
+
WHERE ${versionWhere}
|
|
350
|
+
ORDER BY v.entry_id, v.version_number DESC
|
|
351
|
+
)
|
|
352
|
+
SELECT
|
|
353
|
+
e.id AS e_id, e.slug AS e_slug, e.type AS e_type,
|
|
354
|
+
e.created_at AS e_created_at, e.updated_at AS e_updated_at,
|
|
355
|
+
e.archived_at AS e_archived_at, e.sort_order AS e_sort_order,
|
|
356
|
+
m.id AS v_id, m.entry_id AS v_entry_id, m.lang AS v_lang,
|
|
357
|
+
m.version_number AS v_version_number, m.data AS v_data,
|
|
358
|
+
m.created_at AS v_created_at, m.created_by AS v_created_by,
|
|
359
|
+
m.published_at AS v_published_at, m.published_by AS v_published_by
|
|
360
|
+
FROM entry e
|
|
361
|
+
INNER JOIN matched m ON e.id = m.entry_id
|
|
362
|
+
WHERE ${entryWhere}
|
|
363
|
+
ORDER BY ${orderClause}
|
|
364
|
+
LIMIT ${options.limit} OFFSET ${options.offset}
|
|
365
|
+
`);
|
|
366
|
+
return rows.map((row) => ({
|
|
367
|
+
entry: {
|
|
368
|
+
id: row.e_id,
|
|
369
|
+
slug: row.e_slug,
|
|
370
|
+
type: row.e_type,
|
|
371
|
+
createdAt: new Date(row.e_created_at),
|
|
372
|
+
updatedAt: new Date(row.e_updated_at),
|
|
373
|
+
archivedAt: row.e_archived_at ? new Date(row.e_archived_at) : null,
|
|
374
|
+
sortOrder: row.e_sort_order
|
|
375
|
+
},
|
|
376
|
+
version: {
|
|
377
|
+
id: row.v_id,
|
|
378
|
+
entryId: row.v_entry_id,
|
|
379
|
+
lang: row.v_lang,
|
|
380
|
+
versionNumber: row.v_version_number,
|
|
381
|
+
data: row.v_data,
|
|
382
|
+
createdAt: new Date(row.v_created_at),
|
|
383
|
+
createdBy: row.v_created_by,
|
|
384
|
+
publishedAt: row.v_published_at ? new Date(row.v_published_at) : null,
|
|
385
|
+
publishedBy: row.v_published_by
|
|
386
|
+
}
|
|
387
|
+
}));
|
|
388
|
+
},
|
|
389
|
+
countPaginatedEntries: async (options) => {
|
|
390
|
+
const versionWhereParts = buildCteVersionConditions(options);
|
|
391
|
+
const versionWhere = sql.join(versionWhereParts, sql ` AND `);
|
|
392
|
+
const entryWhereParts = buildCteEntryConditions(options);
|
|
393
|
+
const entryWhere = sql.join(entryWhereParts, sql ` AND `);
|
|
394
|
+
const [result] = await db.execute(sql `
|
|
395
|
+
WITH matched AS (
|
|
396
|
+
SELECT DISTINCT ON (v.entry_id)
|
|
397
|
+
v.entry_id
|
|
398
|
+
FROM entry_version v
|
|
399
|
+
WHERE ${versionWhere}
|
|
400
|
+
ORDER BY v.entry_id, v.version_number DESC
|
|
401
|
+
)
|
|
402
|
+
SELECT COUNT(*)::int AS count
|
|
403
|
+
FROM entry e
|
|
404
|
+
INNER JOIN matched m ON e.id = m.entry_id
|
|
405
|
+
WHERE ${entryWhere}
|
|
406
|
+
`);
|
|
407
|
+
return result?.count ?? 0;
|
|
408
|
+
},
|
|
145
409
|
updateEntry: async ({ id, data }) => {
|
|
146
410
|
return db.transaction(async (tx) => {
|
|
147
411
|
const [result] = await tx
|
|
@@ -182,7 +446,7 @@ export function pg(config) {
|
|
|
182
446
|
.values({
|
|
183
447
|
...data,
|
|
184
448
|
versionNumber: data.versionNumber ??
|
|
185
|
-
sql `COALESCE((SELECT MAX(version_number) FROM ${schema.entryVersionsTable} WHERE entry_id = ${data.entryId}), 0) + 1`
|
|
449
|
+
sql `COALESCE((SELECT MAX(version_number) FROM ${schema.entryVersionsTable} WHERE entry_id = ${data.entryId} AND lang = ${data.lang}), 0) + 1`
|
|
186
450
|
})
|
|
187
451
|
.returning();
|
|
188
452
|
return newVersion;
|
|
@@ -195,13 +459,17 @@ export function pg(config) {
|
|
|
195
459
|
options.entryIds
|
|
196
460
|
? inArray(schema.entryVersionsTable.entryId, options.entryIds)
|
|
197
461
|
: undefined,
|
|
198
|
-
options.ids ? inArray(schema.entryVersionsTable.id, options.ids) : undefined
|
|
462
|
+
options.ids ? inArray(schema.entryVersionsTable.id, options.ids) : undefined,
|
|
463
|
+
options.lang ? eq(schema.entryVersionsTable.lang, options.lang) : undefined
|
|
199
464
|
].filter(Boolean);
|
|
200
465
|
const jsonConditions = options.dataValues ? buildJsonConditions(options.dataValues) : [];
|
|
201
466
|
const jsonLikeConditions = options.dataLike
|
|
202
467
|
? buildJsonLikeConditions(options.dataLike)
|
|
203
468
|
: [];
|
|
204
|
-
const
|
|
469
|
+
const jsonILikeOrCondition = options.dataILikeOr
|
|
470
|
+
? buildJsonILikeOrConditions(options.dataILikeOr)
|
|
471
|
+
: undefined;
|
|
472
|
+
const allConditions = [...baseConditions, ...jsonConditions, ...jsonLikeConditions, ...(jsonILikeOrCondition ? [jsonILikeOrCondition] : [])];
|
|
205
473
|
if (allConditions.length > 0) {
|
|
206
474
|
query.where(and(...allConditions));
|
|
207
475
|
}
|
|
@@ -351,48 +619,46 @@ export function pg(config) {
|
|
|
351
619
|
await db.delete(schema.mediaFilesTable).where(inArray(schema.mediaFilesTable.id, ids));
|
|
352
620
|
},
|
|
353
621
|
getMediaFiles: async (options) => {
|
|
354
|
-
const conditions =
|
|
355
|
-
if (options.data) {
|
|
356
|
-
if (options.data.search) {
|
|
357
|
-
conditions.push(or(ilike(schema.mediaFilesTable.name, `%${options.data.search}%`), ilike(schema.mediaFilesTable.alt, `%${options.data.search}%`)));
|
|
358
|
-
}
|
|
359
|
-
if (options.data.ids) {
|
|
360
|
-
conditions.push(inArray(schema.mediaFilesTable.id, options.data.ids));
|
|
361
|
-
}
|
|
362
|
-
if (options.data.mimeTypes) {
|
|
363
|
-
const exact = options.data.mimeTypes.filter((t) => !t.endsWith('/*'));
|
|
364
|
-
const wildcards = options.data.mimeTypes.filter((t) => t.endsWith('/*'));
|
|
365
|
-
const mimeTypeConditions = wildcards.map((t) => ilike(schema.mediaFilesTable.mimeType, t.replace('*', '%')));
|
|
366
|
-
// Fallback to type field for files with null mimeType
|
|
367
|
-
const typeConditions = wildcards.map((t) => {
|
|
368
|
-
const baseType = t.replace('/*', '');
|
|
369
|
-
return eq(schema.mediaFilesTable.type, baseType);
|
|
370
|
-
});
|
|
371
|
-
const mimeConditions = [
|
|
372
|
-
...(exact.length ? [inArray(schema.mediaFilesTable.mimeType, exact)] : []),
|
|
373
|
-
...mimeTypeConditions,
|
|
374
|
-
...typeConditions
|
|
375
|
-
];
|
|
376
|
-
if (mimeConditions.length) {
|
|
377
|
-
conditions.push(or(...mimeConditions));
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
if (options.data.tagIds && options.data.tagIds.length > 0) {
|
|
381
|
-
const fileIdsWithTags = db
|
|
382
|
-
.select({ fileId: schema.mediaFileTagsTable.fileId })
|
|
383
|
-
.from(schema.mediaFileTagsTable)
|
|
384
|
-
.where(inArray(schema.mediaFileTagsTable.tagId, options.data.tagIds));
|
|
385
|
-
conditions.push(inArray(schema.mediaFilesTable.id, fileIdsWithTags));
|
|
386
|
-
}
|
|
387
|
-
}
|
|
622
|
+
const conditions = buildMediaFileConditions(db, options);
|
|
388
623
|
const query = db
|
|
389
624
|
.select()
|
|
390
625
|
.from(schema.mediaFilesTable)
|
|
391
626
|
.where(and(...conditions.filter(Boolean)))
|
|
392
627
|
.orderBy(desc(schema.mediaFilesTable.createdAt));
|
|
628
|
+
if (options.data.limit != null) {
|
|
629
|
+
query.limit(options.data.limit);
|
|
630
|
+
}
|
|
631
|
+
if (options.data.offset != null) {
|
|
632
|
+
query.offset(options.data.offset);
|
|
633
|
+
}
|
|
393
634
|
const rawFiles = await query;
|
|
394
635
|
return hydrateFileTags(db, rawFiles);
|
|
395
636
|
},
|
|
637
|
+
countMediaFiles: async (options) => {
|
|
638
|
+
const conditions = buildMediaFileConditions(db, options);
|
|
639
|
+
const [result] = await db
|
|
640
|
+
.select({ count: count() })
|
|
641
|
+
.from(schema.mediaFilesTable)
|
|
642
|
+
.where(and(...conditions.filter(Boolean)));
|
|
643
|
+
return result?.count ?? 0;
|
|
644
|
+
},
|
|
645
|
+
getMediaTagsWithCounts: async () => {
|
|
646
|
+
const rows = await db
|
|
647
|
+
.select({
|
|
648
|
+
id: schema.mediaTagsTable.id,
|
|
649
|
+
name: schema.mediaTagsTable.name,
|
|
650
|
+
color: schema.mediaTagsTable.color,
|
|
651
|
+
count: count(schema.mediaFileTagsTable.fileId)
|
|
652
|
+
})
|
|
653
|
+
.from(schema.mediaTagsTable)
|
|
654
|
+
.leftJoin(schema.mediaFileTagsTable, eq(schema.mediaTagsTable.id, schema.mediaFileTagsTable.tagId))
|
|
655
|
+
.groupBy(schema.mediaTagsTable.id, schema.mediaTagsTable.name, schema.mediaTagsTable.color)
|
|
656
|
+
.orderBy(schema.mediaTagsTable.name);
|
|
657
|
+
return rows.map((r) => ({
|
|
658
|
+
tag: { id: r.id, name: r.name, color: r.color },
|
|
659
|
+
count: r.count
|
|
660
|
+
}));
|
|
661
|
+
},
|
|
396
662
|
getMediaFile: async (options) => {
|
|
397
663
|
const query = db.select().from(schema.mediaFilesTable);
|
|
398
664
|
if (options.data) {
|
|
@@ -55,49 +55,6 @@ export declare const entriesTable: import("drizzle-orm/pg-core/table", { with: {
|
|
|
55
55
|
}, {}, {
|
|
56
56
|
$type: "collection" | "singleton";
|
|
57
57
|
}>;
|
|
58
|
-
availableLocales: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
59
|
-
name: "available_locales";
|
|
60
|
-
tableName: "entry";
|
|
61
|
-
dataType: "array";
|
|
62
|
-
columnType: "PgArray";
|
|
63
|
-
data: string[];
|
|
64
|
-
driverParam: string | string[];
|
|
65
|
-
notNull: false;
|
|
66
|
-
hasDefault: false;
|
|
67
|
-
isPrimaryKey: false;
|
|
68
|
-
isAutoincrement: false;
|
|
69
|
-
hasRuntimeDefault: false;
|
|
70
|
-
enumValues: [string, ...string[]];
|
|
71
|
-
baseColumn: import("drizzle-orm/column", { with: { "resolution-mode": "require" } }).Column<{
|
|
72
|
-
name: "available_locales";
|
|
73
|
-
tableName: "entry";
|
|
74
|
-
dataType: "string";
|
|
75
|
-
columnType: "PgText";
|
|
76
|
-
data: string;
|
|
77
|
-
driverParam: string;
|
|
78
|
-
notNull: false;
|
|
79
|
-
hasDefault: false;
|
|
80
|
-
isPrimaryKey: false;
|
|
81
|
-
isAutoincrement: false;
|
|
82
|
-
hasRuntimeDefault: false;
|
|
83
|
-
enumValues: [string, ...string[]];
|
|
84
|
-
baseColumn: never;
|
|
85
|
-
identity: undefined;
|
|
86
|
-
generated: undefined;
|
|
87
|
-
}, {}, {}>;
|
|
88
|
-
identity: undefined;
|
|
89
|
-
generated: undefined;
|
|
90
|
-
}, {}, {
|
|
91
|
-
size: undefined;
|
|
92
|
-
baseBuilder: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumnBuilder<{
|
|
93
|
-
name: "available_locales";
|
|
94
|
-
dataType: "string";
|
|
95
|
-
columnType: "PgText";
|
|
96
|
-
data: string;
|
|
97
|
-
enumValues: [string, ...string[]];
|
|
98
|
-
driverParam: string;
|
|
99
|
-
}, {}, {}, import("drizzle-orm/column-builder", { with: { "resolution-mode": "require" } }).ColumnBuilderExtraConfig>;
|
|
100
|
-
}>;
|
|
101
58
|
createdAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
102
59
|
name: "created_at";
|
|
103
60
|
tableName: "entry";
|
|
@@ -149,57 +106,6 @@ export declare const entriesTable: import("drizzle-orm/pg-core/table", { with: {
|
|
|
149
106
|
identity: undefined;
|
|
150
107
|
generated: undefined;
|
|
151
108
|
}, {}, {}>;
|
|
152
|
-
publishedAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
153
|
-
name: "published_at";
|
|
154
|
-
tableName: "entry";
|
|
155
|
-
dataType: "date";
|
|
156
|
-
columnType: "PgTimestamp";
|
|
157
|
-
data: Date;
|
|
158
|
-
driverParam: string;
|
|
159
|
-
notNull: false;
|
|
160
|
-
hasDefault: false;
|
|
161
|
-
isPrimaryKey: false;
|
|
162
|
-
isAutoincrement: false;
|
|
163
|
-
hasRuntimeDefault: false;
|
|
164
|
-
enumValues: undefined;
|
|
165
|
-
baseColumn: never;
|
|
166
|
-
identity: undefined;
|
|
167
|
-
generated: undefined;
|
|
168
|
-
}, {}, {}>;
|
|
169
|
-
publishedVersionId: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
170
|
-
name: "published_version_id";
|
|
171
|
-
tableName: "entry";
|
|
172
|
-
dataType: "string";
|
|
173
|
-
columnType: "PgUUID";
|
|
174
|
-
data: string;
|
|
175
|
-
driverParam: string;
|
|
176
|
-
notNull: false;
|
|
177
|
-
hasDefault: false;
|
|
178
|
-
isPrimaryKey: false;
|
|
179
|
-
isAutoincrement: false;
|
|
180
|
-
hasRuntimeDefault: false;
|
|
181
|
-
enumValues: undefined;
|
|
182
|
-
baseColumn: never;
|
|
183
|
-
identity: undefined;
|
|
184
|
-
generated: undefined;
|
|
185
|
-
}, {}, {}>;
|
|
186
|
-
publishedBy: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
187
|
-
name: "published_by";
|
|
188
|
-
tableName: "entry";
|
|
189
|
-
dataType: "string";
|
|
190
|
-
columnType: "PgText";
|
|
191
|
-
data: string;
|
|
192
|
-
driverParam: string;
|
|
193
|
-
notNull: false;
|
|
194
|
-
hasDefault: false;
|
|
195
|
-
isPrimaryKey: false;
|
|
196
|
-
isAutoincrement: false;
|
|
197
|
-
hasRuntimeDefault: false;
|
|
198
|
-
enumValues: [string, ...string[]];
|
|
199
|
-
baseColumn: never;
|
|
200
|
-
identity: undefined;
|
|
201
|
-
generated: undefined;
|
|
202
|
-
}, {}, {}>;
|
|
203
109
|
sortOrder: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
204
110
|
name: "sort_order";
|
|
205
111
|
tableName: "entry";
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { integer, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
2
|
-
import { entryVersionsTable } from './entryVersion.js';
|
|
3
2
|
export const entriesTable = pgTable('entry', {
|
|
4
3
|
id: uuid('id').primaryKey().defaultRandom(),
|
|
5
4
|
slug: text('slug').notNull(),
|
|
6
5
|
type: text('type').notNull().$type(),
|
|
7
|
-
availableLocales: text('available_locales').array(),
|
|
8
6
|
// Metadane
|
|
9
7
|
createdAt: timestamp('created_at').defaultNow().notNull(),
|
|
10
8
|
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
|
11
9
|
archivedAt: timestamp('archived_at'), // soft delete / archiwum
|
|
12
|
-
// Publish state
|
|
13
|
-
publishedAt: timestamp('published_at'),
|
|
14
|
-
publishedVersionId: uuid('published_version_id').references(() => entryVersionsTable.id, { onDelete: 'set null' }),
|
|
15
|
-
publishedBy: text('published_by'),
|
|
16
10
|
// Manual ordering
|
|
17
11
|
sortOrder: integer('sort_order')
|
|
18
12
|
});
|
|
@@ -37,6 +37,23 @@ export declare const entryVersionsTable: import("drizzle-orm/pg-core/table", { w
|
|
|
37
37
|
identity: undefined;
|
|
38
38
|
generated: undefined;
|
|
39
39
|
}, {}, {}>;
|
|
40
|
+
lang: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
41
|
+
name: "lang";
|
|
42
|
+
tableName: "entry_version";
|
|
43
|
+
dataType: "string";
|
|
44
|
+
columnType: "PgText";
|
|
45
|
+
data: string;
|
|
46
|
+
driverParam: string;
|
|
47
|
+
notNull: true;
|
|
48
|
+
hasDefault: false;
|
|
49
|
+
isPrimaryKey: false;
|
|
50
|
+
isAutoincrement: false;
|
|
51
|
+
hasRuntimeDefault: false;
|
|
52
|
+
enumValues: [string, ...string[]];
|
|
53
|
+
baseColumn: never;
|
|
54
|
+
identity: undefined;
|
|
55
|
+
generated: undefined;
|
|
56
|
+
}, {}, {}>;
|
|
40
57
|
versionNumber: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
41
58
|
name: "version_number";
|
|
42
59
|
tableName: "entry_version";
|