@dani-builder/strapi-plugin-builder 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/AGENTS.md +149 -0
  2. package/LICENSE +21 -0
  3. package/README.md +61 -0
  4. package/dist/server/src/bootstrap.d.ts +1 -1
  5. package/dist/server/src/bootstrap.d.ts.map +1 -1
  6. package/dist/server/src/bootstrap.js +8 -1
  7. package/dist/server/src/bootstrap.js.map +1 -1
  8. package/dist/server/src/bootstrap.test.d.ts +2 -0
  9. package/dist/server/src/bootstrap.test.d.ts.map +1 -0
  10. package/dist/server/src/bootstrap.test.js +45 -0
  11. package/dist/server/src/bootstrap.test.js.map +1 -0
  12. package/dist/server/src/components/blocks/cta.json +13 -5
  13. package/dist/server/src/components/blocks/faq.json +1 -1
  14. package/dist/server/src/components/blocks/features.json +10 -5
  15. package/dist/server/src/components/blocks/hero.json +15 -6
  16. package/dist/server/src/components/blocks/highlights.json +9 -5
  17. package/dist/server/src/components/blocks/inquiry-form.json +3 -0
  18. package/dist/server/src/components/blocks/logo-cloud.json +6 -1
  19. package/dist/server/src/components/blocks/stats.json +9 -5
  20. package/dist/server/src/components/blocks/testimonials.json +10 -5
  21. package/dist/server/src/components/shared/bento-item.json +4 -4
  22. package/dist/server/src/components/shared/highlight-item.json +5 -5
  23. package/dist/server/src/components/shared/image.json +24 -0
  24. package/dist/server/src/components/shared/slider-item.json +5 -5
  25. package/dist/server/src/content-types/article/index.d.ts +146 -0
  26. package/dist/server/src/content-types/article/index.d.ts.map +1 -0
  27. package/dist/server/src/content-types/article/index.js +8 -0
  28. package/dist/server/src/content-types/article/index.js.map +1 -0
  29. package/dist/server/src/content-types/article/schema.json +104 -0
  30. package/dist/server/src/content-types/author/index.d.ts +56 -0
  31. package/dist/server/src/content-types/author/index.d.ts.map +1 -0
  32. package/dist/server/src/content-types/author/index.js +8 -0
  33. package/dist/server/src/content-types/author/index.js.map +1 -0
  34. package/dist/server/src/content-types/author/schema.json +48 -0
  35. package/dist/server/src/content-types/category/index.d.ts +71 -0
  36. package/dist/server/src/content-types/category/index.d.ts.map +1 -0
  37. package/dist/server/src/content-types/category/index.js +8 -0
  38. package/dist/server/src/content-types/category/index.js.map +1 -0
  39. package/dist/server/src/content-types/category/schema.json +45 -0
  40. package/dist/server/src/content-types/faq/index.d.ts +60 -0
  41. package/dist/server/src/content-types/faq/index.d.ts.map +1 -0
  42. package/dist/server/src/content-types/faq/index.js +8 -0
  43. package/dist/server/src/content-types/faq/index.js.map +1 -0
  44. package/dist/server/src/content-types/faq/schema.json +38 -0
  45. package/dist/server/src/content-types/global/index.d.ts +91 -0
  46. package/dist/server/src/content-types/global/index.d.ts.map +1 -0
  47. package/dist/server/src/content-types/global/index.js +8 -0
  48. package/dist/server/src/content-types/global/index.js.map +1 -0
  49. package/dist/server/src/content-types/global/schema.json +66 -0
  50. package/dist/server/src/content-types/index.d.ts +795 -0
  51. package/dist/server/src/content-types/index.d.ts.map +1 -0
  52. package/dist/server/src/content-types/index.js +30 -0
  53. package/dist/server/src/content-types/index.js.map +1 -0
  54. package/dist/server/src/content-types/inquiry/index.d.ts +48 -0
  55. package/dist/server/src/content-types/inquiry/index.d.ts.map +1 -0
  56. package/dist/server/src/content-types/inquiry/index.js +8 -0
  57. package/dist/server/src/content-types/inquiry/index.js.map +1 -0
  58. package/dist/server/src/content-types/inquiry/schema.json +40 -0
  59. package/dist/server/src/content-types/navigation/index.d.ts +80 -0
  60. package/dist/server/src/content-types/navigation/index.d.ts.map +1 -0
  61. package/dist/server/src/content-types/navigation/index.js +8 -0
  62. package/dist/server/src/content-types/navigation/index.js.map +1 -0
  63. package/dist/server/src/content-types/navigation/schema.json +54 -0
  64. package/dist/server/src/content-types/page/index.d.ts +78 -0
  65. package/dist/server/src/content-types/page/index.d.ts.map +1 -0
  66. package/dist/server/src/content-types/page/index.js +8 -0
  67. package/dist/server/src/content-types/page/index.js.map +1 -0
  68. package/dist/server/src/content-types/page/schema.json +64 -0
  69. package/dist/server/src/content-types/series/index.d.ts +68 -0
  70. package/dist/server/src/content-types/series/index.d.ts.map +1 -0
  71. package/dist/server/src/content-types/series/index.js +8 -0
  72. package/dist/server/src/content-types/series/index.js.map +1 -0
  73. package/dist/server/src/content-types/series/schema.json +46 -0
  74. package/dist/server/src/content-types/site-setting/index.d.ts +47 -0
  75. package/dist/server/src/content-types/site-setting/index.d.ts.map +1 -0
  76. package/dist/server/src/content-types/site-setting/index.js +8 -0
  77. package/dist/server/src/content-types/site-setting/index.js.map +1 -0
  78. package/dist/server/src/content-types/site-setting/schema.json +39 -0
  79. package/dist/server/src/content-types/tag/index.d.ts +68 -0
  80. package/dist/server/src/content-types/tag/index.d.ts.map +1 -0
  81. package/dist/server/src/content-types/tag/index.js +8 -0
  82. package/dist/server/src/content-types/tag/index.js.map +1 -0
  83. package/dist/server/src/content-types/tag/schema.json +46 -0
  84. package/dist/server/src/controllers/global.d.ts +1 -10
  85. package/dist/server/src/controllers/global.d.ts.map +1 -1
  86. package/dist/server/src/controllers/global.js +11 -4
  87. package/dist/server/src/controllers/global.js.map +1 -1
  88. package/dist/server/src/controllers/index.d.ts.map +1 -1
  89. package/dist/server/src/controllers/index.js +10 -0
  90. package/dist/server/src/controllers/index.js.map +1 -1
  91. package/dist/server/src/controllers/page.d.ts +11 -14
  92. package/dist/server/src/controllers/page.d.ts.map +1 -1
  93. package/dist/server/src/controllers/page.js +78 -9
  94. package/dist/server/src/controllers/page.js.map +1 -1
  95. package/dist/server/src/index.d.ts.map +1 -1
  96. package/dist/server/src/index.js +4 -0
  97. package/dist/server/src/index.js.map +1 -1
  98. package/dist/server/src/policies/index.d.ts +3 -0
  99. package/dist/server/src/policies/index.d.ts.map +1 -0
  100. package/dist/server/src/policies/index.js +13 -0
  101. package/dist/server/src/policies/index.js.map +1 -0
  102. package/dist/server/src/policies/rate-limit.d.ts +15 -0
  103. package/dist/server/src/policies/rate-limit.d.ts.map +1 -0
  104. package/dist/server/src/policies/rate-limit.js +39 -0
  105. package/dist/server/src/policies/rate-limit.js.map +1 -0
  106. package/dist/server/src/register.d.ts.map +1 -1
  107. package/dist/server/src/register.js +16 -1
  108. package/dist/server/src/register.js.map +1 -1
  109. package/dist/server/src/routes/content-api.d.ts +10 -13
  110. package/dist/server/src/routes/content-api.d.ts.map +1 -1
  111. package/dist/server/src/routes/content-api.js +34 -30
  112. package/dist/server/src/routes/content-api.js.map +1 -1
  113. package/dist/server/src/routes/index.d.ts +10 -15
  114. package/dist/server/src/routes/index.d.ts.map +1 -1
  115. package/dist/server/src/routes/index.js +2 -1
  116. package/dist/server/src/routes/index.js.map +1 -1
  117. package/dist/server/src/services/index.d.ts.map +1 -1
  118. package/dist/server/src/services/index.js +12 -0
  119. package/dist/server/src/services/index.js.map +1 -1
  120. package/dist/server/src/services/populate.d.ts.map +1 -1
  121. package/dist/server/src/services/populate.js +15 -9
  122. package/dist/server/src/services/populate.js.map +1 -1
  123. package/dist/server/src/utils/query.d.ts +11 -0
  124. package/dist/server/src/utils/query.d.ts.map +1 -0
  125. package/dist/server/src/utils/query.js +112 -0
  126. package/dist/server/src/utils/query.js.map +1 -0
  127. package/package.json +30 -18
  128. package/bin/sync.js +0 -320
  129. package/schemas/api/article/content-types/article/schema.json +0 -102
  130. package/schemas/api/author/content-types/author/schema.json +0 -44
  131. package/schemas/api/category/content-types/category/schema.json +0 -43
  132. package/schemas/api/faq/content-types/faq/schema.json +0 -36
  133. package/schemas/api/global/content-types/global/schema.json +0 -64
  134. package/schemas/api/inquiry/content-types/inquiry/schema.json +0 -31
  135. package/schemas/api/navigation/content-types/navigation/schema.json +0 -52
  136. package/schemas/api/page/content-types/page/schema.json +0 -62
  137. package/schemas/api/series/content-types/series/schema.json +0 -44
  138. package/schemas/api/site-setting/content-types/site-setting/schema.json +0 -35
  139. package/schemas/api/tag/content-types/tag/schema.json +0 -44
  140. package/schemas/manifest.json +0 -16
package/bin/sync.js DELETED
@@ -1,320 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- 'use strict';
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const crypto = require('crypto');
8
-
9
- // ─── Helpers ──────────────────────────────────────────────
10
-
11
- function hash(content) {
12
- return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
13
- }
14
-
15
- function ensureDir(dir) {
16
- if (!fs.existsSync(dir)) {
17
- fs.mkdirSync(dir, { recursive: true });
18
- }
19
- }
20
-
21
- function parseArgs(argv) {
22
- const args = argv.slice(2);
23
- const opts = {
24
- command: null,
25
- dryRun: false,
26
- force: false,
27
- only: null,
28
- };
29
-
30
- for (let i = 0; i < args.length; i++) {
31
- const arg = args[i];
32
- if (arg === '--dry-run') {
33
- opts.dryRun = true;
34
- } else if (arg === '--force') {
35
- opts.force = true;
36
- } else if (arg === '--only' && args[i + 1]) {
37
- opts.only = args[++i].split(',').map((s) => s.trim());
38
- } else if (!arg.startsWith('-')) {
39
- opts.command = arg;
40
- }
41
- }
42
-
43
- return opts;
44
- }
45
-
46
- // ─── Colors (minimal, no dependency) ─────────────────────
47
-
48
- const c = {
49
- green: (s) => `\x1b[32m${s}\x1b[0m`,
50
- yellow: (s) => `\x1b[33m${s}\x1b[0m`,
51
- red: (s) => `\x1b[31m${s}\x1b[0m`,
52
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
53
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
54
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
55
- };
56
-
57
- // ─── Strapi Boilerplate Generation ───────────────────────
58
-
59
- function generateBoilerplate(ctName, uid, targetApiDir, opts) {
60
- const files = [
61
- {
62
- rel: `routes/${ctName}.ts`,
63
- content: `import { factories } from '@strapi/strapi';\nexport default factories.createCoreRouter('${uid}');\n`,
64
- },
65
- {
66
- rel: `controllers/${ctName}.ts`,
67
- content: `import { factories } from '@strapi/strapi';\nexport default factories.createCoreController('${uid}');\n`,
68
- },
69
- {
70
- rel: `services/${ctName}.ts`,
71
- content: `import { factories } from '@strapi/strapi';\nexport default factories.createCoreService('${uid}');\n`,
72
- },
73
- ];
74
-
75
- const created = [];
76
- for (const file of files) {
77
- const filePath = path.join(targetApiDir, file.rel);
78
- if (!fs.existsSync(filePath)) {
79
- if (!opts.dryRun) {
80
- ensureDir(path.dirname(filePath));
81
- fs.writeFileSync(filePath, file.content, 'utf-8');
82
- }
83
- created.push(file.rel);
84
- }
85
- }
86
- return created;
87
- }
88
-
89
- function extractUid(schemaPath) {
90
- try {
91
- const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf-8'));
92
- const info = schema.info || {};
93
- const singularName = info.singularName;
94
- if (singularName) {
95
- return `api::${singularName}.${singularName}`;
96
- }
97
- } catch {
98
- // fallback below
99
- }
100
- // Fallback: derive from path (api/{name}/content-types/{name}/schema.json)
101
- const parts = schemaPath.split(path.sep);
102
- const apiIdx = parts.indexOf('api');
103
- if (apiIdx >= 0 && parts[apiIdx + 1]) {
104
- const name = parts[apiIdx + 1];
105
- return `api::${name}.${name}`;
106
- }
107
- return null;
108
- }
109
-
110
- // ─── Sync Logic ──────────────────────────────────────────
111
-
112
- function findProjectRoot() {
113
- let dir = process.cwd();
114
- while (dir !== path.dirname(dir)) {
115
- if (fs.existsSync(path.join(dir, 'package.json'))) {
116
- const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
117
- // Look for Strapi project indicators
118
- if (
119
- pkg.dependencies?.['@strapi/strapi'] ||
120
- pkg.devDependencies?.['@strapi/strapi'] ||
121
- fs.existsSync(path.join(dir, 'config', 'database.js')) ||
122
- fs.existsSync(path.join(dir, 'config', 'database.ts'))
123
- ) {
124
- return dir;
125
- }
126
- }
127
- dir = path.dirname(dir);
128
- }
129
- return process.cwd();
130
- }
131
-
132
- function findPluginSchemasDir() {
133
- // When installed as a dependency, schemas/ is at the package root
134
- const packageRoot = path.resolve(__dirname, '..');
135
- const schemasDir = path.join(packageRoot, 'schemas');
136
- if (fs.existsSync(schemasDir)) {
137
- return schemasDir;
138
- }
139
- throw new Error('Could not find plugin schemas directory');
140
- }
141
-
142
- function sync(opts) {
143
- const projectRoot = findProjectRoot();
144
- const schemasDir = findPluginSchemasDir();
145
- const manifestPath = path.join(schemasDir, 'manifest.json');
146
- const localManifestPath = path.join(projectRoot, '.daniworks-sync-manifest.json');
147
-
148
- if (!fs.existsSync(manifestPath)) {
149
- console.error(c.red('Error: manifest.json not found in plugin schemas directory'));
150
- process.exit(1);
151
- }
152
-
153
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
154
- const localManifest = fs.existsSync(localManifestPath)
155
- ? JSON.parse(fs.readFileSync(localManifestPath, 'utf-8'))
156
- : { version: '0.0.0', schemas: {} };
157
-
158
- const srcDir = path.join(projectRoot, 'src');
159
- const results = { synced: [], skipped: [], created: [], boilerplate: [] };
160
-
161
- console.log('');
162
- console.log(c.bold(` @dani-builder/strapi-plugin-builder sync v${manifest.version}`));
163
- console.log(c.dim(` Project: ${projectRoot}`));
164
- console.log(c.dim(` Schemas: ${schemasDir}`));
165
- if (opts.dryRun) {
166
- console.log(c.yellow(' Mode: DRY RUN (no files will be modified)'));
167
- }
168
- console.log('');
169
-
170
- for (const [relativePath, sourceHash] of Object.entries(manifest.schemas)) {
171
- const ctName = relativePath.split('/')[1]; // e.g. "page"
172
-
173
- // Filter by --only
174
- if (opts.only && !opts.only.includes(ctName)) {
175
- continue;
176
- }
177
-
178
- const sourcePath = path.join(schemasDir, relativePath);
179
- const targetPath = path.join(srcDir, relativePath);
180
-
181
- if (!fs.existsSync(sourcePath)) {
182
- console.log(c.red(` ✗ ${relativePath} — source file missing`));
183
- continue;
184
- }
185
-
186
- const sourceContent = fs.readFileSync(sourcePath, 'utf-8');
187
-
188
- // Check if target already exists
189
- if (fs.existsSync(targetPath)) {
190
- const targetContent = fs.readFileSync(targetPath, 'utf-8');
191
- const targetHash = hash(targetContent);
192
-
193
- // Compare against the last synced version (from local manifest)
194
- const lastSyncedHash = localManifest.schemas[relativePath];
195
-
196
- if (lastSyncedHash && targetHash !== lastSyncedHash && !opts.force) {
197
- // Local modification detected
198
- console.log(
199
- c.yellow(` ⚠ ${relativePath}`) + c.dim(' — local modifications detected, skipping')
200
- );
201
- console.log(c.dim(` Use --force to overwrite`));
202
- results.skipped.push(relativePath);
203
- continue;
204
- }
205
-
206
- if (targetHash === sourceHash) {
207
- // Already up to date
208
- console.log(c.dim(` ✓ ${relativePath} — already up to date`));
209
- continue;
210
- }
211
-
212
- // Update needed
213
- if (!opts.dryRun) {
214
- fs.writeFileSync(targetPath, sourceContent, 'utf-8');
215
- }
216
- console.log(c.green(` ↑ ${relativePath}`) + c.dim(' — updated'));
217
- results.synced.push(relativePath);
218
- } else {
219
- // New file
220
- if (!opts.dryRun) {
221
- ensureDir(path.dirname(targetPath));
222
- fs.writeFileSync(targetPath, sourceContent, 'utf-8');
223
- }
224
- console.log(c.cyan(` + ${relativePath}`) + c.dim(' — created'));
225
- results.created.push(relativePath);
226
- }
227
- }
228
-
229
- // Generate boilerplate (routes/controllers/services) for each CT
230
- const processedCts = new Set();
231
- for (const relativePath of Object.keys(manifest.schemas)) {
232
- const ctName = relativePath.split('/')[1];
233
- if (processedCts.has(ctName)) continue;
234
- if (opts.only && !opts.only.includes(ctName)) continue;
235
- processedCts.add(ctName);
236
-
237
- const targetApiDir = path.join(srcDir, 'api', ctName);
238
- const schemaFile = path.join(targetApiDir, 'content-types', ctName, 'schema.json');
239
- const uid = extractUid(schemaFile);
240
- if (!uid) continue;
241
-
242
- const created = generateBoilerplate(ctName, uid, targetApiDir, opts);
243
- for (const file of created) {
244
- console.log(c.cyan(` + api/${ctName}/${file}`) + c.dim(' — generated'));
245
- results.boilerplate.push(`api/${ctName}/${file}`);
246
- }
247
- }
248
-
249
- // Update local manifest (unless dry run)
250
- if (!opts.dryRun) {
251
- const newLocalManifest = {
252
- version: manifest.version,
253
- schemas: { ...manifest.schemas },
254
- };
255
- fs.writeFileSync(localManifestPath, JSON.stringify(newLocalManifest, null, 2) + '\n');
256
- }
257
-
258
- // Summary
259
- console.log('');
260
- console.log(c.bold(' Summary:'));
261
- console.log(` Created: ${c.cyan(results.created.length)}`);
262
- console.log(` Updated: ${c.green(results.synced.length)}`);
263
- console.log(` Skipped: ${c.yellow(results.skipped.length)}`);
264
- console.log(` Boilerplate: ${c.cyan(results.boilerplate.length)}`);
265
- console.log('');
266
-
267
- if (opts.dryRun) {
268
- console.log(c.yellow(' This was a dry run. No files were modified.'));
269
- console.log(c.dim(' Run without --dry-run to apply changes.'));
270
- console.log('');
271
- }
272
- }
273
-
274
- // ─── Help ────────────────────────────────────────────────
275
-
276
- function showHelp() {
277
- console.log(`
278
- ${c.bold('@dani-builder/strapi-plugin-builder')} — Content Type sync CLI
279
-
280
- ${c.bold('Usage:')}
281
- npx @dani-builder/strapi-plugin-builder sync [options]
282
-
283
- ${c.bold('Commands:')}
284
- sync Synchronize content type schemas to your Strapi project
285
-
286
- ${c.bold('Options:')}
287
- --dry-run Show what would change without modifying files
288
- --force Overwrite local modifications
289
- --only <names> Sync only specific content types (comma-separated)
290
- Example: --only page,article,global
291
-
292
- ${c.bold('Examples:')}
293
- npx @dani-builder/strapi-plugin-builder sync
294
- npx @dani-builder/strapi-plugin-builder sync --dry-run
295
- npx @dani-builder/strapi-plugin-builder sync --force
296
- npx @dani-builder/strapi-plugin-builder sync --only page,global
297
- `);
298
- }
299
-
300
- // ─── Main ────────────────────────────────────────────────
301
-
302
- function main() {
303
- const opts = parseArgs(process.argv);
304
-
305
- if (!opts.command || opts.command === 'help' || opts.command === '--help') {
306
- showHelp();
307
- return;
308
- }
309
-
310
- if (opts.command === 'sync') {
311
- sync(opts);
312
- return;
313
- }
314
-
315
- console.error(c.red(`Unknown command: ${opts.command}`));
316
- showHelp();
317
- process.exit(1);
318
- }
319
-
320
- main();
@@ -1,102 +0,0 @@
1
- {
2
- "kind": "collectionType",
3
- "collectionName": "articles",
4
- "info": {
5
- "singularName": "article",
6
- "pluralName": "articles",
7
- "displayName": "Article",
8
- "description": "Blog/news post with rich text body and relations"
9
- },
10
- "options": {
11
- "draftAndPublish": true
12
- },
13
- "pluginOptions": {
14
- "i18n": { "localized": true }
15
- },
16
- "attributes": {
17
- "title": {
18
- "type": "string",
19
- "required": true,
20
- "pluginOptions": { "i18n": { "localized": true } }
21
- },
22
- "slug": {
23
- "type": "uid",
24
- "targetField": "title",
25
- "required": true,
26
- "pluginOptions": { "i18n": { "localized": true } }
27
- },
28
- "body": {
29
- "type": "blocks",
30
- "required": true,
31
- "pluginOptions": { "i18n": { "localized": true } }
32
- },
33
- "summary": {
34
- "type": "text",
35
- "pluginOptions": { "i18n": { "localized": true } }
36
- },
37
- "excerpt": {
38
- "type": "text",
39
- "pluginOptions": { "i18n": { "localized": true } }
40
- },
41
- "cover": {
42
- "type": "media",
43
- "multiple": false,
44
- "allowedTypes": ["images"],
45
- "pluginOptions": { "i18n": { "localized": true } }
46
- },
47
- "gallery": {
48
- "type": "media",
49
- "multiple": true,
50
- "allowedTypes": ["images"],
51
- "pluginOptions": { "i18n": { "localized": true } }
52
- },
53
- "reading_time": {
54
- "type": "integer",
55
- "min": 1,
56
- "default": 5
57
- },
58
- "firstPublishedAt": {
59
- "type": "datetime"
60
- },
61
- "seo": {
62
- "type": "component",
63
- "component": "shared.seo",
64
- "repeatable": false,
65
- "pluginOptions": { "i18n": { "localized": true } }
66
- },
67
- "categories": {
68
- "type": "relation",
69
- "relation": "manyToMany",
70
- "target": "api::category.category",
71
- "inversedBy": "articles"
72
- },
73
- "tags": {
74
- "type": "relation",
75
- "relation": "manyToMany",
76
- "target": "api::tag.tag",
77
- "inversedBy": "articles"
78
- },
79
- "author": {
80
- "type": "relation",
81
- "relation": "manyToOne",
82
- "target": "api::author.author",
83
- "inversedBy": "articles"
84
- },
85
- "series": {
86
- "type": "relation",
87
- "relation": "manyToOne",
88
- "target": "api::series.series",
89
- "inversedBy": "articles"
90
- },
91
- "series_order": {
92
- "type": "integer",
93
- "min": 1,
94
- "default": 1
95
- },
96
- "related_posts": {
97
- "type": "relation",
98
- "relation": "manyToMany",
99
- "target": "api::article.article"
100
- }
101
- }
102
- }
@@ -1,44 +0,0 @@
1
- {
2
- "kind": "collectionType",
3
- "collectionName": "authors",
4
- "info": {
5
- "singularName": "author",
6
- "pluralName": "authors",
7
- "displayName": "Author",
8
- "description": "Article author profile"
9
- },
10
- "options": {
11
- "draftAndPublish": false
12
- },
13
- "attributes": {
14
- "name": {
15
- "type": "string",
16
- "required": true
17
- },
18
- "slug": {
19
- "type": "uid",
20
- "targetField": "name",
21
- "required": true
22
- },
23
- "role": {
24
- "type": "string"
25
- },
26
- "bio": {
27
- "type": "richtext"
28
- },
29
- "avatar": {
30
- "type": "media",
31
- "multiple": false,
32
- "allowedTypes": ["images"]
33
- },
34
- "email": {
35
- "type": "email"
36
- },
37
- "articles": {
38
- "type": "relation",
39
- "relation": "oneToMany",
40
- "target": "api::article.article",
41
- "mappedBy": "author"
42
- }
43
- }
44
- }
@@ -1,43 +0,0 @@
1
- {
2
- "kind": "collectionType",
3
- "collectionName": "categories",
4
- "info": {
5
- "singularName": "category",
6
- "pluralName": "categories",
7
- "displayName": "Category",
8
- "description": "Unified category for articles and FAQs"
9
- },
10
- "options": {
11
- "draftAndPublish": false
12
- },
13
- "pluginOptions": {
14
- "i18n": { "localized": true }
15
- },
16
- "attributes": {
17
- "name": {
18
- "type": "string",
19
- "required": true,
20
- "pluginOptions": { "i18n": { "localized": true } }
21
- },
22
- "slug": {
23
- "type": "uid",
24
- "targetField": "name",
25
- "required": true,
26
- "pluginOptions": { "i18n": { "localized": true } }
27
- },
28
- "description": {
29
- "type": "text",
30
- "pluginOptions": { "i18n": { "localized": true } }
31
- },
32
- "sortOrder": {
33
- "type": "integer",
34
- "pluginOptions": { "i18n": { "localized": true } }
35
- },
36
- "articles": {
37
- "type": "relation",
38
- "relation": "manyToMany",
39
- "target": "api::article.article",
40
- "mappedBy": "categories"
41
- }
42
- }
43
- }
@@ -1,36 +0,0 @@
1
- {
2
- "kind": "collectionType",
3
- "collectionName": "faqs",
4
- "info": {
5
- "singularName": "faq",
6
- "pluralName": "faqs",
7
- "displayName": "FAQ",
8
- "description": "Frequently asked question entry"
9
- },
10
- "options": {
11
- "draftAndPublish": true
12
- },
13
- "pluginOptions": {
14
- "i18n": { "localized": true }
15
- },
16
- "attributes": {
17
- "question": {
18
- "type": "string",
19
- "maxLength": 255,
20
- "pluginOptions": { "i18n": { "localized": true } }
21
- },
22
- "answer": {
23
- "type": "blocks",
24
- "pluginOptions": { "i18n": { "localized": true } }
25
- },
26
- "sortOrder": {
27
- "type": "integer",
28
- "pluginOptions": { "i18n": { "localized": true } }
29
- },
30
- "category": {
31
- "type": "relation",
32
- "relation": "oneToOne",
33
- "target": "api::category.category"
34
- }
35
- }
36
- }
@@ -1,64 +0,0 @@
1
- {
2
- "kind": "singleType",
3
- "collectionName": "globals",
4
- "info": {
5
- "singularName": "global",
6
- "pluralName": "globals",
7
- "displayName": "Global",
8
- "description": "Site-wide content, theme settings, footer, and common blocks"
9
- },
10
- "options": {
11
- "draftAndPublish": true
12
- },
13
- "pluginOptions": {
14
- "i18n": { "localized": true }
15
- },
16
- "attributes": {
17
- "sitename": {
18
- "type": "string",
19
- "required": true,
20
- "pluginOptions": { "i18n": { "localized": true } }
21
- },
22
- "seo": {
23
- "type": "component",
24
- "component": "shared.seo",
25
- "repeatable": false,
26
- "pluginOptions": { "i18n": { "localized": true } }
27
- },
28
- "home": {
29
- "type": "relation",
30
- "relation": "oneToOne",
31
- "target": "api::page.page"
32
- },
33
- "footer": {
34
- "type": "component",
35
- "component": "elements.footer",
36
- "repeatable": false,
37
- "pluginOptions": { "i18n": { "localized": true } }
38
- },
39
- "siteWideBlocks": {
40
- "type": "dynamiczone",
41
- "components": [
42
- "blocks.slider",
43
- "blocks.inquiry-form",
44
- "blocks.hero",
45
- "blocks.features"
46
- ],
47
- "pluginOptions": { "i18n": { "localized": true } }
48
- },
49
- "darkModeEnabled": {
50
- "type": "boolean",
51
- "default": false
52
- },
53
- "defaultTheme": {
54
- "type": "enumeration",
55
- "enum": ["light", "dark"],
56
- "pluginOptions": { "i18n": { "localized": true } }
57
- },
58
- "favicon": {
59
- "type": "media",
60
- "multiple": false,
61
- "allowedTypes": ["images"]
62
- }
63
- }
64
- }
@@ -1,31 +0,0 @@
1
- {
2
- "kind": "collectionType",
3
- "collectionName": "inquiries",
4
- "info": {
5
- "singularName": "inquiry",
6
- "pluralName": "inquiries",
7
- "displayName": "Inquiry",
8
- "description": "Contact form submission data"
9
- },
10
- "options": {
11
- "draftAndPublish": false
12
- },
13
- "attributes": {
14
- "name": {
15
- "type": "string",
16
- "required": true
17
- },
18
- "email": {
19
- "type": "string"
20
- },
21
- "phone": {
22
- "type": "string"
23
- },
24
- "subject": {
25
- "type": "string"
26
- },
27
- "message": {
28
- "type": "text"
29
- }
30
- }
31
- }
@@ -1,52 +0,0 @@
1
- {
2
- "kind": "singleType",
3
- "collectionName": "navigations",
4
- "info": {
5
- "singularName": "navigation",
6
- "pluralName": "navigations",
7
- "displayName": "Navigation",
8
- "description": "Global navigation with logo, menu groups, and CTA links"
9
- },
10
- "options": {
11
- "draftAndPublish": true
12
- },
13
- "pluginOptions": {
14
- "i18n": { "localized": true }
15
- },
16
- "attributes": {
17
- "mainLogo": {
18
- "type": "media",
19
- "multiple": false,
20
- "allowedTypes": ["images"]
21
- },
22
- "darkLogo": {
23
- "type": "media",
24
- "multiple": false,
25
- "allowedTypes": ["images"]
26
- },
27
- "globalNavigation": {
28
- "type": "component",
29
- "component": "shared.navigation-group",
30
- "repeatable": true,
31
- "pluginOptions": { "i18n": { "localized": true } }
32
- },
33
- "primaryLink": {
34
- "type": "component",
35
- "component": "shared.link",
36
- "repeatable": false,
37
- "pluginOptions": { "i18n": { "localized": true } }
38
- },
39
- "secondaryLink": {
40
- "type": "component",
41
- "component": "shared.link",
42
- "repeatable": false,
43
- "pluginOptions": { "i18n": { "localized": true } }
44
- },
45
- "align": {
46
- "type": "enumeration",
47
- "enum": ["left", "center", "right"],
48
- "default": "left",
49
- "pluginOptions": { "i18n": { "localized": true } }
50
- }
51
- }
52
- }