@xleddyl/nuxt-cms 0.1.61 → 0.2.1
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/module.json +1 -1
- package/dist/module.mjs +133 -14
- package/dist/runtime/app/components/cms/BlocksField.vue +86 -10
- package/dist/runtime/app/components/cms/BrandMark.d.vue.ts +6 -0
- package/dist/runtime/app/components/cms/BrandMark.vue +19 -0
- package/dist/runtime/app/components/cms/BrandMark.vue.d.ts +6 -0
- package/dist/runtime/app/components/cms/Checkbox.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/Checkbox.vue +21 -0
- package/dist/runtime/app/components/cms/Checkbox.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/Drawer.vue +5 -9
- package/dist/runtime/app/components/cms/EmptyState.vue +4 -2
- package/dist/runtime/app/components/cms/EntryDrawer.vue +12 -3
- package/dist/runtime/app/components/cms/EntryForm.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/EntryForm.vue +104 -30
- package/dist/runtime/app/components/cms/EntryForm.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/FieldInput.vue +20 -5
- package/dist/runtime/app/components/cms/FormField.d.vue.ts +5 -3
- package/dist/runtime/app/components/cms/FormField.vue +8 -2
- package/dist/runtime/app/components/cms/FormField.vue.d.ts +5 -3
- package/dist/runtime/app/components/cms/Icon.vue +1 -1
- package/dist/runtime/app/components/cms/Input.d.vue.ts +0 -1
- package/dist/runtime/app/components/cms/Input.vue +7 -4
- package/dist/runtime/app/components/cms/Input.vue.d.ts +0 -1
- package/dist/runtime/app/components/cms/LocaleSwitch.vue +1 -1
- package/dist/runtime/app/components/cms/MediaFolderPicker.d.vue.ts +0 -1
- package/dist/runtime/app/components/cms/MediaFolderPicker.vue +1 -3
- package/dist/runtime/app/components/cms/MediaFolderPicker.vue.d.ts +0 -1
- package/dist/runtime/app/components/cms/MediaGallery.vue +24 -10
- package/dist/runtime/app/components/cms/MediaUpload.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/MediaUpload.vue +71 -41
- package/dist/runtime/app/components/cms/MediaUpload.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/Modal.vue +5 -9
- package/dist/runtime/app/components/cms/PageHeader.d.vue.ts +5 -3
- package/dist/runtime/app/components/cms/PageHeader.vue +16 -6
- package/dist/runtime/app/components/cms/PageHeader.vue.d.ts +5 -3
- package/dist/runtime/app/components/cms/PasswordInput.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/PasswordInput.vue +81 -0
- package/dist/runtime/app/components/cms/PasswordInput.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/RelationField.vue +7 -2
- package/dist/runtime/app/components/cms/SelectMenu.d.vue.ts +0 -1
- package/dist/runtime/app/components/cms/SelectMenu.vue +4 -11
- package/dist/runtime/app/components/cms/SelectMenu.vue.d.ts +0 -1
- package/dist/runtime/app/components/cms/SettingsAccount.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsAccount.vue +138 -0
- package/dist/runtime/app/components/cms/SettingsAccount.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsColumns.d.vue.ts +16 -0
- package/dist/runtime/app/components/cms/SettingsColumns.vue +102 -0
- package/dist/runtime/app/components/cms/SettingsColumns.vue.d.ts +16 -0
- package/dist/runtime/app/components/cms/SettingsEntry.d.vue.ts +6 -0
- package/dist/runtime/app/components/cms/SettingsEntry.vue +126 -0
- package/dist/runtime/app/components/cms/SettingsEntry.vue.d.ts +6 -0
- package/dist/runtime/app/components/cms/SettingsLayout.d.vue.ts +16 -0
- package/dist/runtime/app/components/cms/SettingsLayout.vue +177 -0
- package/dist/runtime/app/components/cms/SettingsLayout.vue.d.ts +16 -0
- package/dist/runtime/app/components/cms/SettingsModal.d.vue.ts +16 -0
- package/dist/runtime/app/components/cms/SettingsModal.vue +174 -0
- package/dist/runtime/app/components/cms/SettingsModal.vue.d.ts +16 -0
- package/dist/runtime/app/components/cms/SettingsUsers.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsUsers.vue +274 -0
- package/dist/runtime/app/components/cms/SettingsUsers.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/Spinner.vue +2 -2
- package/dist/runtime/app/components/cms/Table.d.vue.ts +6 -8
- package/dist/runtime/app/components/cms/Table.vue +42 -47
- package/dist/runtime/app/components/cms/Table.vue.d.ts +6 -8
- package/dist/runtime/app/components/cms/Textarea.d.vue.ts +0 -1
- package/dist/runtime/app/components/cms/Textarea.vue +1 -2
- package/dist/runtime/app/components/cms/Textarea.vue.d.ts +0 -1
- package/dist/runtime/app/components/cms/Toaster.vue +1 -1
- package/dist/runtime/app/components/cms/WelcomeModal.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/WelcomeModal.vue +69 -0
- package/dist/runtime/app/components/cms/WelcomeModal.vue.d.ts +3 -0
- package/dist/runtime/app/composables/cms-account.d.ts +11 -0
- package/dist/runtime/app/composables/cms-account.js +23 -0
- package/dist/runtime/app/composables/cms-entry-disabled.js +1 -1
- package/dist/runtime/app/composables/cms-media-keys.d.ts +1 -0
- package/dist/runtime/app/composables/cms-media-keys.js +5 -0
- package/dist/runtime/app/composables/cms-media-library.js +3 -0
- package/dist/runtime/app/composables/cms-media-uploader.d.ts +11 -2
- package/dist/runtime/app/composables/cms-media-uploader.js +139 -12
- package/dist/runtime/app/composables/cms-overlay.d.ts +4 -0
- package/dist/runtime/app/composables/cms-overlay.js +23 -0
- package/dist/runtime/app/composables/cms-settings.d.ts +12 -0
- package/dist/runtime/app/composables/cms-settings.js +59 -0
- package/dist/runtime/app/composables/cms-theme.d.ts +9 -0
- package/dist/runtime/app/composables/cms-theme.js +27 -0
- package/dist/runtime/app/layouts/cms-admin.d.vue.ts +2 -2
- package/dist/runtime/app/layouts/cms-admin.vue +71 -17
- package/dist/runtime/app/layouts/cms-admin.vue.d.ts +2 -2
- package/dist/runtime/app/pages/admin-collection.vue +127 -101
- package/dist/runtime/app/pages/admin-entry.vue +2 -0
- package/dist/runtime/app/pages/admin-login.vue +18 -3
- package/dist/runtime/app/pages/admin-media.vue +5 -1
- package/dist/runtime/app/utils/ui.d.ts +2 -0
- package/dist/runtime/app/utils/ui.js +18 -0
- package/dist/runtime/assets/main.css +1 -1
- package/dist/runtime/generate.js +1 -1
- package/dist/runtime/server/api/account-password.put.d.ts +4 -0
- package/dist/runtime/server/api/account-password.put.js +38 -0
- package/dist/runtime/server/api/account-welcome.post.d.ts +4 -0
- package/dist/runtime/server/api/account-welcome.post.js +14 -0
- package/dist/runtime/server/api/account.put.d.ts +3 -0
- package/dist/runtime/server/api/account.put.js +25 -0
- package/dist/runtime/server/api/collection.get.js +1 -1
- package/dist/runtime/server/api/media.delete.js +2 -1
- package/dist/runtime/server/api/media.post.js +2 -1
- package/dist/runtime/server/api/media.put.js +2 -1
- package/dist/runtime/server/api/settings.delete.d.ts +4 -0
- package/dist/runtime/server/api/settings.delete.js +12 -0
- package/dist/runtime/server/api/settings.get.d.ts +3 -0
- package/dist/runtime/server/api/settings.get.js +15 -0
- package/dist/runtime/server/api/settings.put.d.ts +3 -0
- package/dist/runtime/server/api/settings.put.js +14 -0
- package/dist/runtime/server/api/user.delete.d.ts +4 -0
- package/dist/runtime/server/api/user.delete.js +12 -0
- package/dist/runtime/server/api/user.put.d.ts +3 -0
- package/dist/runtime/server/api/user.put.js +27 -0
- package/dist/runtime/server/api/users.get.d.ts +3 -0
- package/dist/runtime/server/api/users.get.js +11 -0
- package/dist/runtime/server/api/users.post.d.ts +3 -0
- package/dist/runtime/server/api/users.post.js +36 -0
- package/dist/runtime/server/plugins/session-validate.d.ts +2 -0
- package/dist/runtime/server/plugins/session-validate.js +13 -0
- package/dist/runtime/server/routes/auth/login.post.js +20 -10
- package/dist/runtime/server/utils/graphql-depth.js +12 -5
- package/dist/runtime/server/utils/graphql.js +2 -2
- package/dist/runtime/server/utils/media-folders.js +10 -2
- package/dist/runtime/server/utils/media.d.ts +1 -0
- package/dist/runtime/server/utils/media.js +4 -1
- package/dist/runtime/server/utils/migrate.js +1 -1
- package/dist/runtime/server/utils/password.d.ts +3 -0
- package/dist/runtime/server/utils/password.js +31 -0
- package/dist/runtime/server/utils/require-admin.d.ts +4 -2
- package/dist/runtime/server/utils/require-admin.js +32 -28
- package/dist/runtime/server/utils/same-origin.d.ts +2 -0
- package/dist/runtime/server/utils/same-origin.js +24 -0
- package/dist/runtime/server/utils/settings.d.ts +16 -0
- package/dist/runtime/server/utils/settings.js +31 -0
- package/dist/runtime/server/utils/users.d.ts +15 -0
- package/dist/runtime/server/utils/users.js +34 -0
- package/dist/runtime/shared/index.d.ts +13 -1
- package/dist/runtime/shared/index.js +2 -0
- package/dist/runtime/shared/layout.d.ts +33 -0
- package/dist/runtime/shared/layout.js +127 -0
- package/dist/runtime/shared/users.d.ts +18 -0
- package/dist/runtime/shared/users.js +1 -0
- package/dist/runtime/shared/validation.js +5 -4
- package/package.json +3 -3
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export const MAX_LAYOUT_ROW_FIELDS = 4;
|
|
2
|
+
function isSectionInput(block) {
|
|
3
|
+
return typeof block === "object" && !Array.isArray(block);
|
|
4
|
+
}
|
|
5
|
+
function toRow(row) {
|
|
6
|
+
return Array.isArray(row) ? [...row] : [row];
|
|
7
|
+
}
|
|
8
|
+
function sectionsFromBlocks(blocks) {
|
|
9
|
+
const sections = [];
|
|
10
|
+
let loose = null;
|
|
11
|
+
for (const block of blocks ?? []) {
|
|
12
|
+
if (isSectionInput(block)) {
|
|
13
|
+
loose = null;
|
|
14
|
+
sections.push({
|
|
15
|
+
title: block.title,
|
|
16
|
+
...block.description ? { description: block.description } : {},
|
|
17
|
+
...block.collapsed ? { collapsed: true } : {},
|
|
18
|
+
rows: block.rows.map(toRow)
|
|
19
|
+
});
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (!loose) {
|
|
23
|
+
loose = { rows: [] };
|
|
24
|
+
sections.push(loose);
|
|
25
|
+
}
|
|
26
|
+
loose.rows.push(toRow(block));
|
|
27
|
+
}
|
|
28
|
+
return sections;
|
|
29
|
+
}
|
|
30
|
+
export function layoutFromConfig(input) {
|
|
31
|
+
return { main: sectionsFromBlocks(input) };
|
|
32
|
+
}
|
|
33
|
+
function cloneSection(section) {
|
|
34
|
+
return { ...section, rows: section.rows.map((row) => [...row]) };
|
|
35
|
+
}
|
|
36
|
+
export function resolveFormLayout(fields, layout) {
|
|
37
|
+
const known = new Set(Object.keys(fields));
|
|
38
|
+
const mobileOf = new Map(
|
|
39
|
+
Object.entries(fields).filter(([, field]) => field.mobileOf && known.has(field.mobileOf)).map(([key, field]) => [field.mobileOf, key])
|
|
40
|
+
);
|
|
41
|
+
const placed = /* @__PURE__ */ new Set();
|
|
42
|
+
const clean = (sections) => (sections ?? []).map(cloneSection).map((section) => ({
|
|
43
|
+
...section,
|
|
44
|
+
rows: section.rows.map(
|
|
45
|
+
(row) => row.filter((key) => {
|
|
46
|
+
if (!known.has(key) || placed.has(key)) return false;
|
|
47
|
+
placed.add(key);
|
|
48
|
+
return true;
|
|
49
|
+
})
|
|
50
|
+
).filter((row) => row.length)
|
|
51
|
+
}));
|
|
52
|
+
const main = clean(layout?.main);
|
|
53
|
+
for (const section of main) {
|
|
54
|
+
for (let r = 0; r < section.rows.length; r++) {
|
|
55
|
+
const row = section.rows[r];
|
|
56
|
+
for (const [parent, mobile] of mobileOf) {
|
|
57
|
+
const index = row.indexOf(parent);
|
|
58
|
+
if (index === -1 || placed.has(mobile)) continue;
|
|
59
|
+
if (row.length < MAX_LAYOUT_ROW_FIELDS) row.splice(index + 1, 0, mobile);
|
|
60
|
+
else section.rows.splice(r + 1, 0, [mobile]);
|
|
61
|
+
placed.add(mobile);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const missing = Object.keys(fields).filter((key) => !placed.has(key));
|
|
66
|
+
if (missing.length) {
|
|
67
|
+
const rows = [];
|
|
68
|
+
for (const key of missing) {
|
|
69
|
+
if (placed.has(key)) continue;
|
|
70
|
+
const mobile = mobileOf.get(key);
|
|
71
|
+
const row = mobile && !placed.has(mobile) ? [key, mobile] : [key];
|
|
72
|
+
row.forEach((k) => placed.add(k));
|
|
73
|
+
rows.push(row);
|
|
74
|
+
}
|
|
75
|
+
const last = main[main.length - 1];
|
|
76
|
+
if (last && !last.title) last.rows.push(...rows);
|
|
77
|
+
else main.push({ rows });
|
|
78
|
+
}
|
|
79
|
+
return { main };
|
|
80
|
+
}
|
|
81
|
+
export function defaultListColumns(fields, list, extra = []) {
|
|
82
|
+
const available = [...Object.keys(fields), ...extra];
|
|
83
|
+
const configured = list?.columns?.filter((key) => available.includes(key));
|
|
84
|
+
if (configured?.length) return configured;
|
|
85
|
+
return [...Object.keys(fields).slice(0, 4), ...extra];
|
|
86
|
+
}
|
|
87
|
+
export function resolveListColumns(fields, saved, list, extra = []) {
|
|
88
|
+
const available = /* @__PURE__ */ new Set([...Object.keys(fields), ...extra]);
|
|
89
|
+
const kept = saved?.filter((key) => available.has(key));
|
|
90
|
+
return kept?.length ? [...new Set(kept)] : defaultListColumns(fields, list, extra);
|
|
91
|
+
}
|
|
92
|
+
export function layoutErrors(at, fields, input) {
|
|
93
|
+
if (!input) return [];
|
|
94
|
+
const errors = [];
|
|
95
|
+
if (!Array.isArray(input)) return [`${at}: layout must be an array of rows and sections`];
|
|
96
|
+
const blocks = input;
|
|
97
|
+
const seen = /* @__PURE__ */ new Set();
|
|
98
|
+
const checkRow = (row) => {
|
|
99
|
+
const keys = toRow(row);
|
|
100
|
+
if (!keys.length) errors.push(`${at}: layout has an empty row`);
|
|
101
|
+
if (keys.length > MAX_LAYOUT_ROW_FIELDS)
|
|
102
|
+
errors.push(
|
|
103
|
+
`${at}: layout row [${keys.join(", ")}] has more than ${MAX_LAYOUT_ROW_FIELDS} fields`
|
|
104
|
+
);
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
if (!Object.hasOwn(fields, key))
|
|
107
|
+
errors.push(`${at}: layout names '${key}', which is not a declared field`);
|
|
108
|
+
else if (seen.has(key)) errors.push(`${at}: layout names '${key}' more than once`);
|
|
109
|
+
seen.add(key);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
for (const block of blocks) {
|
|
113
|
+
if (isSectionInput(block)) {
|
|
114
|
+
if (!block.title) errors.push(`${at}: every layout section needs a title`);
|
|
115
|
+
if (!Array.isArray(block.rows))
|
|
116
|
+
errors.push(`${at}: layout section '${block.title}' needs a rows array`);
|
|
117
|
+
else block.rows.forEach(checkRow);
|
|
118
|
+
} else {
|
|
119
|
+
checkRow(block);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return errors;
|
|
123
|
+
}
|
|
124
|
+
export function listErrors(at, fields, list, extra = []) {
|
|
125
|
+
if (!list?.columns) return [];
|
|
126
|
+
return list.columns.filter((key) => !Object.hasOwn(fields, key) && !extra.includes(key)).map((key) => `${at}: list.columns names '${key}', which is not a declared field`);
|
|
127
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type CmsUserRole = 'superadmin' | 'admin';
|
|
2
|
+
export interface CmsSessionUser {
|
|
3
|
+
id?: string;
|
|
4
|
+
email: string;
|
|
5
|
+
name?: string | null;
|
|
6
|
+
role?: CmsUserRole;
|
|
7
|
+
firstLogin?: boolean;
|
|
8
|
+
passwordStamp?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CmsAccount {
|
|
11
|
+
id: string;
|
|
12
|
+
email: string;
|
|
13
|
+
name: string | null;
|
|
14
|
+
role: CmsUserRole;
|
|
15
|
+
lastLoginAt: string | null;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const MIN_PASSWORD_LENGTH = 8;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MIN_PASSWORD_LENGTH = 8;
|
|
@@ -31,8 +31,9 @@ function scalarSchema(field, m) {
|
|
|
31
31
|
return field.required ? z.string().trim().min(1, m.required) : z.string();
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
function optionalize(field, base) {
|
|
35
|
-
|
|
34
|
+
function optionalize(field, base, m) {
|
|
35
|
+
if (!field.required) return base.nullish().transform((v) => v ?? null);
|
|
36
|
+
return z.any().refine((v) => v !== null && v !== void 0, m.required).pipe(base);
|
|
36
37
|
}
|
|
37
38
|
function translatableSchema(field, i18n, m) {
|
|
38
39
|
const { locales, defaultLocale } = i18n;
|
|
@@ -42,7 +43,7 @@ function translatableSchema(field, i18n, m) {
|
|
|
42
43
|
function blockFieldSchema(blockField, i18n, m) {
|
|
43
44
|
if (isTranslatableField(blockField) && i18n.locales.length)
|
|
44
45
|
return translatableSchema(blockField, i18n, m);
|
|
45
|
-
return optionalize(blockField, scalarSchema(blockField, m));
|
|
46
|
+
return optionalize(blockField, scalarSchema(blockField, m), m);
|
|
46
47
|
}
|
|
47
48
|
function blocksSchema(field, i18n, m) {
|
|
48
49
|
const variants = Object.entries(field.blocks ?? {}).map(
|
|
@@ -99,7 +100,7 @@ export function buildEntrySchema(entry, i18n, messages) {
|
|
|
99
100
|
shape[key] = field.required ? json.refine((v) => v !== void 0 && v !== null, m.required) : json.nullish().transform((v) => v ?? null);
|
|
100
101
|
continue;
|
|
101
102
|
}
|
|
102
|
-
shape[key] = optionalize(field, scalarSchema(field, m));
|
|
103
|
+
shape[key] = optionalize(field, scalarSchema(field, m), m);
|
|
103
104
|
}
|
|
104
105
|
if (entry.drafts) {
|
|
105
106
|
shape.status = z.enum(["draft", "published"]).nullish().transform((v) => v ?? void 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xleddyl/nuxt-cms",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Lightweight CMS that ships with your Nuxt app: runs on the Nitro server, content types defined in code, /cms admin panel, GraphQL API, SQLite or Postgres. No external CMS needed!",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Edoardo Alberti (https://github.com/xleddyl)",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"dist"
|
|
61
61
|
],
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@fontsource-variable/
|
|
64
|
-
"@fontsource/
|
|
63
|
+
"@fontsource-variable/geist": "^5.3.0",
|
|
64
|
+
"@fontsource-variable/geist-mono": "^5.3.0",
|
|
65
65
|
"@nuxt/kit": "^4.4.8",
|
|
66
66
|
"@tailwindcss/vite": "^4.3.2",
|
|
67
67
|
"@tiptap/starter-kit": "^3.0.0",
|