@timbenniks/contentstack-platform-app-scaffold 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/commands/scaffold.d.ts +19 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +83 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/services/prompts.d.ts +3 -0
- package/dist/services/prompts.d.ts.map +1 -0
- package/dist/services/prompts.js +154 -0
- package/dist/services/prompts.js.map +1 -0
- package/dist/services/scaffold.d.ts +3 -0
- package/dist/services/scaffold.d.ts.map +1 -0
- package/dist/services/scaffold.js +119 -0
- package/dist/services/scaffold.js.map +1 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/git.d.ts +5 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +33 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/package-manager.d.ts +8 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +40 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/dist/utils/template-engine.d.ts +13 -0
- package/dist/utils/template-engine.d.ts.map +1 -0
- package/dist/utils/template-engine.js +87 -0
- package/dist/utils/template-engine.js.map +1 -0
- package/oclif.manifest.json +101 -0
- package/package.json +40 -0
- package/templates/nextjs/.env.example.hbs +22 -0
- package/templates/nextjs/app/api/auth/[...nextauth]/route.ts.hbs +3 -0
- package/templates/nextjs/app/api/brandkit/[...path]/route.ts.hbs +35 -0
- package/templates/nextjs/app/api/cma/[...path]/route.ts.hbs +35 -0
- package/templates/nextjs/app/api/launch/[...path]/route.ts.hbs +34 -0
- package/templates/nextjs/app/assets/page.tsx.hbs +25 -0
- package/templates/nextjs/app/brandkit/page.tsx.hbs +43 -0
- package/templates/nextjs/app/entries/page.tsx.hbs +26 -0
- package/templates/nextjs/app/launch/page.tsx.hbs +43 -0
- package/templates/nextjs/app/layout.tsx.hbs +20 -0
- package/templates/nextjs/app/page.tsx.hbs +55 -0
- package/templates/nextjs/app/providers.tsx.hbs +11 -0
- package/templates/nextjs/lib/auth.ts.hbs +12 -0
- package/templates/nextjs/manifest.json +19 -0
- package/templates/nextjs/next.config.js +4 -0
- package/templates/nextjs/package.json.hbs +24 -0
- package/templates/nextjs/tsconfig.json +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-engine.d.ts","sourceRoot":"","sources":["../../src/utils/template-engine.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAY/C,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAGvF;AA4CD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,eAAe,GACvB,IAAI,CAgCN"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderTemplate = renderTemplate;
|
|
7
|
+
exports.renderDirectory = renderDirectory;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const handlebars_1 = __importDefault(require("handlebars"));
|
|
11
|
+
/** Render a single Handlebars template string with the given context */
|
|
12
|
+
function renderTemplate(templateString, context) {
|
|
13
|
+
const template = handlebars_1.default.compile(templateString, { noEscape: true });
|
|
14
|
+
return template(context);
|
|
15
|
+
}
|
|
16
|
+
/** Walk a directory recursively and return all file paths relative to root */
|
|
17
|
+
function walkDirectory(dir, root = dir) {
|
|
18
|
+
const results = [];
|
|
19
|
+
for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
|
|
20
|
+
const fullPath = (0, node_path_1.join)(dir, entry.name);
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
results.push(...walkDirectory(fullPath, root));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
results.push((0, node_path_1.relative)(root, fullPath));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return results;
|
|
29
|
+
}
|
|
30
|
+
/** Check whether a file should be included based on feature flags and manifest */
|
|
31
|
+
function shouldIncludeFile(relPath, manifest, context) {
|
|
32
|
+
if (!manifest)
|
|
33
|
+
return true;
|
|
34
|
+
const entry = manifest.find((m) => m.path === relPath);
|
|
35
|
+
if (!entry)
|
|
36
|
+
return true;
|
|
37
|
+
if (entry.requiredFeatures && entry.requiredFeatures.length > 0) {
|
|
38
|
+
const hasAllRequired = entry.requiredFeatures.every((feature) => context.features[feature] === true);
|
|
39
|
+
if (!hasAllRequired)
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (entry.anyFeatures && entry.anyFeatures.length > 0) {
|
|
43
|
+
const hasAnyRequired = entry.anyFeatures.some((feature) => context.features[feature] === true);
|
|
44
|
+
if (!hasAnyRequired)
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Render an entire template directory into an output directory.
|
|
51
|
+
*
|
|
52
|
+
* - Files ending in `.hbs` are rendered through Handlebars and the `.hbs` extension is stripped.
|
|
53
|
+
* - All other files are copied as-is.
|
|
54
|
+
* - Files listed in `manifest.json` with `requiredFeatures` are skipped if the features are not selected.
|
|
55
|
+
* - `manifest.json` itself is never copied to the output.
|
|
56
|
+
*/
|
|
57
|
+
function renderDirectory(templateDir, outputDir, context) {
|
|
58
|
+
let manifest;
|
|
59
|
+
try {
|
|
60
|
+
const manifestContent = (0, node_fs_1.readFileSync)((0, node_path_1.join)(templateDir, "manifest.json"), "utf-8");
|
|
61
|
+
manifest = JSON.parse(manifestContent);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
manifest = undefined;
|
|
65
|
+
}
|
|
66
|
+
const files = walkDirectory(templateDir);
|
|
67
|
+
for (const relPath of files) {
|
|
68
|
+
if (relPath === "manifest.json")
|
|
69
|
+
continue;
|
|
70
|
+
const outputRelPath = relPath.endsWith(".hbs") ? relPath.slice(0, -4) : relPath;
|
|
71
|
+
if (!shouldIncludeFile(outputRelPath, manifest, context))
|
|
72
|
+
continue;
|
|
73
|
+
const inputPath = (0, node_path_1.join)(templateDir, relPath);
|
|
74
|
+
const outputPath = (0, node_path_1.join)(outputDir, outputRelPath);
|
|
75
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(outputPath), { recursive: true });
|
|
76
|
+
if (relPath.endsWith(".hbs")) {
|
|
77
|
+
const templateContent = (0, node_fs_1.readFileSync)(inputPath, "utf-8");
|
|
78
|
+
const rendered = renderTemplate(templateContent, context);
|
|
79
|
+
(0, node_fs_1.writeFileSync)(outputPath, rendered, "utf-8");
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const content = (0, node_fs_1.readFileSync)(inputPath);
|
|
83
|
+
(0, node_fs_1.writeFileSync)(outputPath, content);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=template-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-engine.js","sourceRoot":"","sources":["../../src/utils/template-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"commands": {
|
|
3
|
+
"scaffold": {
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"args": {
|
|
6
|
+
"name": {
|
|
7
|
+
"description": "Project directory name",
|
|
8
|
+
"name": "name",
|
|
9
|
+
"required": false
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"description": "Scaffold a Contentstack app using the Platform SDK",
|
|
13
|
+
"examples": [
|
|
14
|
+
"<%= config.bin %> scaffold my-app",
|
|
15
|
+
"<%= config.bin %> scaffold my-app --framework nextjs --region eu",
|
|
16
|
+
"<%= config.bin %> scaffold my-app --features cma,oauth --no-install"
|
|
17
|
+
],
|
|
18
|
+
"flags": {
|
|
19
|
+
"framework": {
|
|
20
|
+
"description": "Framework (nextjs)",
|
|
21
|
+
"name": "framework",
|
|
22
|
+
"hasDynamicHelp": false,
|
|
23
|
+
"multiple": false,
|
|
24
|
+
"options": [
|
|
25
|
+
"nextjs"
|
|
26
|
+
],
|
|
27
|
+
"type": "option"
|
|
28
|
+
},
|
|
29
|
+
"region": {
|
|
30
|
+
"description": "Contentstack region (us|eu|au|azure-na|azure-eu|gcp-na|gcp-eu)",
|
|
31
|
+
"name": "region",
|
|
32
|
+
"hasDynamicHelp": false,
|
|
33
|
+
"multiple": false,
|
|
34
|
+
"options": [
|
|
35
|
+
"us",
|
|
36
|
+
"eu",
|
|
37
|
+
"au",
|
|
38
|
+
"azure-na",
|
|
39
|
+
"azure-eu",
|
|
40
|
+
"gcp-na",
|
|
41
|
+
"gcp-eu"
|
|
42
|
+
],
|
|
43
|
+
"type": "option"
|
|
44
|
+
},
|
|
45
|
+
"features": {
|
|
46
|
+
"description": "Comma-separated features: cma,oauth,launch,brandkit",
|
|
47
|
+
"name": "features",
|
|
48
|
+
"hasDynamicHelp": false,
|
|
49
|
+
"multiple": false,
|
|
50
|
+
"type": "option"
|
|
51
|
+
},
|
|
52
|
+
"scopes": {
|
|
53
|
+
"description": "Comma-separated OAuth scopes",
|
|
54
|
+
"name": "scopes",
|
|
55
|
+
"hasDynamicHelp": false,
|
|
56
|
+
"multiple": false,
|
|
57
|
+
"type": "option"
|
|
58
|
+
},
|
|
59
|
+
"package-manager": {
|
|
60
|
+
"description": "Package manager (npm|yarn|pnpm)",
|
|
61
|
+
"name": "package-manager",
|
|
62
|
+
"hasDynamicHelp": false,
|
|
63
|
+
"multiple": false,
|
|
64
|
+
"options": [
|
|
65
|
+
"npm",
|
|
66
|
+
"yarn",
|
|
67
|
+
"pnpm"
|
|
68
|
+
],
|
|
69
|
+
"type": "option"
|
|
70
|
+
},
|
|
71
|
+
"no-git": {
|
|
72
|
+
"description": "Skip git initialization",
|
|
73
|
+
"name": "no-git",
|
|
74
|
+
"allowNo": false,
|
|
75
|
+
"type": "boolean"
|
|
76
|
+
},
|
|
77
|
+
"no-install": {
|
|
78
|
+
"description": "Skip dependency installation",
|
|
79
|
+
"name": "no-install",
|
|
80
|
+
"allowNo": false,
|
|
81
|
+
"type": "boolean"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"hasDynamicHelp": false,
|
|
85
|
+
"hiddenAliases": [],
|
|
86
|
+
"id": "scaffold",
|
|
87
|
+
"pluginAlias": "@timbenniks/contentstack-platform-app-scaffold",
|
|
88
|
+
"pluginName": "@timbenniks/contentstack-platform-app-scaffold",
|
|
89
|
+
"pluginType": "core",
|
|
90
|
+
"strict": true,
|
|
91
|
+
"enableJsonFlag": false,
|
|
92
|
+
"isESM": false,
|
|
93
|
+
"relativePath": [
|
|
94
|
+
"dist",
|
|
95
|
+
"commands",
|
|
96
|
+
"scaffold.js"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"version": "0.1.0"
|
|
101
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@timbenniks/contentstack-platform-app-scaffold",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Contentstack CLI plugin to scaffold apps using the Platform SDK",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"oclif": {
|
|
8
|
+
"commands": {
|
|
9
|
+
"strategy": "pattern",
|
|
10
|
+
"target": "./dist/commands"
|
|
11
|
+
},
|
|
12
|
+
"bin": "csdx"
|
|
13
|
+
},
|
|
14
|
+
"files": ["dist", "templates", "oclif.manifest.json"],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"postbuild": "oclif manifest",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"test:coverage": "vitest run --coverage",
|
|
21
|
+
"lint": "biome check src/",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@oclif/core": "^4.8.0",
|
|
26
|
+
"@clack/prompts": "^0.9.0",
|
|
27
|
+
"picocolors": "^1.1.0",
|
|
28
|
+
"handlebars": "^4.7.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.0.0",
|
|
32
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
33
|
+
"oclif": "^4.0.0",
|
|
34
|
+
"typescript": "^5.5.0",
|
|
35
|
+
"vitest": "^2.1.0"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Contentstack Configuration
|
|
2
|
+
CONTENTSTACK_REGION={{region}}
|
|
3
|
+
CONTENTSTACK_API_KEY=your_api_key
|
|
4
|
+
CONTENTSTACK_ORGANIZATION_UID=your_org_uid
|
|
5
|
+
CONTENTSTACK_AUTHTOKEN=your_authtoken # Optional: for Launch/Brand Kit when OAuth is not enabled
|
|
6
|
+
{{#if features.cma}}
|
|
7
|
+
{{#unless features.oauth}}
|
|
8
|
+
# CMA Access Token
|
|
9
|
+
CONTENTSTACK_MANAGEMENT_TOKEN=your_management_token
|
|
10
|
+
{{/unless}}
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if features.oauth}}
|
|
13
|
+
# OAuth Credentials
|
|
14
|
+
CONTENTSTACK_APP_ID=your_app_id
|
|
15
|
+
CONTENTSTACK_CLIENT_ID=your_client_id
|
|
16
|
+
CONTENTSTACK_CLIENT_SECRET=your_client_secret
|
|
17
|
+
AUTH_SECRET=generate_a_random_secret
|
|
18
|
+
{{/if}}
|
|
19
|
+
{{#if features.brandkit}}
|
|
20
|
+
# Brand Kit
|
|
21
|
+
CONTENTSTACK_BRAND_KIT_UID=your_brand_kit_uid
|
|
22
|
+
{{/if}}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createBrandKitProxy } from "@timbenniks/contentstack-platform-sdk/server/proxy"
|
|
2
|
+
import type { ContentstackRegion } from "@timbenniks/contentstack-platform-sdk/regions"
|
|
3
|
+
{{#if features.oauth}}
|
|
4
|
+
import { getAccessToken } from "@timbenniks/contentstack-platform-sdk/server/middleware"
|
|
5
|
+
import { auth } from "@/lib/auth"
|
|
6
|
+
{{/if}}
|
|
7
|
+
|
|
8
|
+
const proxy = createBrandKitProxy({
|
|
9
|
+
region: (process.env.CONTENTSTACK_REGION ?? "{{region}}") as ContentstackRegion,
|
|
10
|
+
organizationUid: process.env.CONTENTSTACK_ORGANIZATION_UID!,
|
|
11
|
+
brandKitUid: process.env.CONTENTSTACK_BRAND_KIT_UID!,
|
|
12
|
+
getAccessToken: async () => {
|
|
13
|
+
{{#if features.oauth}}
|
|
14
|
+
return await getAccessToken(auth)
|
|
15
|
+
{{else}}
|
|
16
|
+
return process.env.CONTENTSTACK_AUTHTOKEN ?? ""
|
|
17
|
+
{{/if}}
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export async function GET(request: Request) {
|
|
22
|
+
return proxy(request)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function POST(request: Request) {
|
|
26
|
+
return proxy(request)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function PUT(request: Request) {
|
|
30
|
+
return proxy(request)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function DELETE(request: Request) {
|
|
34
|
+
return proxy(request)
|
|
35
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createCMAProxy } from "@timbenniks/contentstack-platform-sdk/server/proxy"
|
|
2
|
+
import type { ContentstackRegion } from "@timbenniks/contentstack-platform-sdk/regions"
|
|
3
|
+
{{#if features.oauth}}
|
|
4
|
+
import { getAccessToken } from "@timbenniks/contentstack-platform-sdk/server/middleware"
|
|
5
|
+
import { auth } from "@/lib/auth"
|
|
6
|
+
{{/if}}
|
|
7
|
+
|
|
8
|
+
const proxy = createCMAProxy({
|
|
9
|
+
region: (process.env.CONTENTSTACK_REGION ?? "{{region}}") as ContentstackRegion,
|
|
10
|
+
apiKey: process.env.CONTENTSTACK_API_KEY!,
|
|
11
|
+
getAccessToken: async () => {
|
|
12
|
+
{{#if features.oauth}}
|
|
13
|
+
return await getAccessToken(auth)
|
|
14
|
+
{{else}}
|
|
15
|
+
return process.env.CONTENTSTACK_MANAGEMENT_TOKEN ?? ""
|
|
16
|
+
{{/if}}
|
|
17
|
+
},
|
|
18
|
+
allowedScopes: ["entries:read", "entries:write", "assets:read", "content-types:read"],
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export async function GET(request: Request) {
|
|
22
|
+
return proxy(request)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function POST(request: Request) {
|
|
26
|
+
return proxy(request)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function PUT(request: Request) {
|
|
30
|
+
return proxy(request)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function DELETE(request: Request) {
|
|
34
|
+
return proxy(request)
|
|
35
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createLaunchProxy } from "@timbenniks/contentstack-platform-sdk/server/proxy"
|
|
2
|
+
import type { ContentstackRegion } from "@timbenniks/contentstack-platform-sdk/regions"
|
|
3
|
+
{{#if features.oauth}}
|
|
4
|
+
import { getAccessToken } from "@timbenniks/contentstack-platform-sdk/server/middleware"
|
|
5
|
+
import { auth } from "@/lib/auth"
|
|
6
|
+
{{/if}}
|
|
7
|
+
|
|
8
|
+
const proxy = createLaunchProxy({
|
|
9
|
+
region: (process.env.CONTENTSTACK_REGION ?? "{{region}}") as ContentstackRegion,
|
|
10
|
+
organizationUid: process.env.CONTENTSTACK_ORGANIZATION_UID!,
|
|
11
|
+
getAccessToken: async () => {
|
|
12
|
+
{{#if features.oauth}}
|
|
13
|
+
return await getAccessToken(auth)
|
|
14
|
+
{{else}}
|
|
15
|
+
return process.env.CONTENTSTACK_AUTHTOKEN ?? ""
|
|
16
|
+
{{/if}}
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export async function GET(request: Request) {
|
|
21
|
+
return proxy(request)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function POST(request: Request) {
|
|
25
|
+
return proxy(request)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function PUT(request: Request) {
|
|
29
|
+
return proxy(request)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function DELETE(request: Request) {
|
|
33
|
+
return proxy(request)
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useAssets } from "@timbenniks/contentstack-platform-sdk/react/hooks"
|
|
4
|
+
|
|
5
|
+
export default function AssetsPage() {
|
|
6
|
+
const { data: assets, loading, error } = useAssets()
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<main style=\{{ padding: "2rem", fontFamily: "system-ui, sans-serif" }}>
|
|
10
|
+
<h1>Assets</h1>
|
|
11
|
+
|
|
12
|
+
{loading && <p>Loading assets...</p>}
|
|
13
|
+
{error && <p>Error: {error.message}</p>}
|
|
14
|
+
{assets && (
|
|
15
|
+
<ul>
|
|
16
|
+
{assets.map((asset) => (
|
|
17
|
+
<li key={asset.uid}>{asset.title ?? asset.filename}</li>
|
|
18
|
+
))}
|
|
19
|
+
</ul>
|
|
20
|
+
)}
|
|
21
|
+
|
|
22
|
+
<p><a href="/">Back to home</a></p>
|
|
23
|
+
</main>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { BrandKit } from "@timbenniks/contentstack-platform-sdk/brandkit"
|
|
4
|
+
import { useBrandKit } from "@timbenniks/contentstack-platform-sdk/react/hooks"
|
|
5
|
+
import { useEffect, useState } from "react"
|
|
6
|
+
|
|
7
|
+
export default function BrandKitPage() {
|
|
8
|
+
const brandKit = useBrandKit()
|
|
9
|
+
const [brandKits, setBrandKits] = useState<BrandKit[]>([])
|
|
10
|
+
const [loading, setLoading] = useState(true)
|
|
11
|
+
const [error, setError] = useState<string | null>(null)
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
brandKit.brandKits
|
|
15
|
+
.listAll()
|
|
16
|
+
.then(setBrandKits)
|
|
17
|
+
.catch((err: Error) => setError(err.message))
|
|
18
|
+
.finally(() => setLoading(false))
|
|
19
|
+
}, [brandKit])
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<main style=\{{ padding: "2rem", fontFamily: "system-ui, sans-serif" }}>
|
|
23
|
+
<h1>Brand Kits</h1>
|
|
24
|
+
|
|
25
|
+
{loading && <p>Loading brand kits...</p>}
|
|
26
|
+
{error && <p>Error: {error}</p>}
|
|
27
|
+
|
|
28
|
+
{!loading && !error && (
|
|
29
|
+
<ul>
|
|
30
|
+
{brandKits.map((kit) => (
|
|
31
|
+
<li key={kit.uid}>{kit.name}</li>
|
|
32
|
+
))}
|
|
33
|
+
</ul>
|
|
34
|
+
)}
|
|
35
|
+
|
|
36
|
+
{!loading && !error && brandKits.length === 0 && (
|
|
37
|
+
<p>No brand kits found for this organization.</p>
|
|
38
|
+
)}
|
|
39
|
+
|
|
40
|
+
<p><a href="/">Back to home</a></p>
|
|
41
|
+
</main>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEntries } from "@timbenniks/contentstack-platform-sdk/react/hooks"
|
|
4
|
+
|
|
5
|
+
export default function EntriesPage() {
|
|
6
|
+
const { data: entries, loading, error } = useEntries("your_content_type")
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<main style=\{{ padding: "2rem", fontFamily: "system-ui, sans-serif" }}>
|
|
10
|
+
<h1>Entries</h1>
|
|
11
|
+
<p>Update <code>"your_content_type"</code> to a content type UID from your stack.</p>
|
|
12
|
+
|
|
13
|
+
{loading && <p>Loading entries...</p>}
|
|
14
|
+
{error && <p>Error: {error.message}</p>}
|
|
15
|
+
{entries && (
|
|
16
|
+
<ul>
|
|
17
|
+
{entries.map((entry) => (
|
|
18
|
+
<li key={entry.uid}>{entry.title ?? entry.uid}</li>
|
|
19
|
+
))}
|
|
20
|
+
</ul>
|
|
21
|
+
)}
|
|
22
|
+
|
|
23
|
+
<p><a href="/">Back to home</a></p>
|
|
24
|
+
</main>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { LaunchProject } from "@timbenniks/contentstack-platform-sdk/launch"
|
|
4
|
+
import { useLaunch } from "@timbenniks/contentstack-platform-sdk/react/hooks"
|
|
5
|
+
import { useEffect, useState } from "react"
|
|
6
|
+
|
|
7
|
+
export default function LaunchPage() {
|
|
8
|
+
const launch = useLaunch()
|
|
9
|
+
const [projects, setProjects] = useState<LaunchProject[]>([])
|
|
10
|
+
const [loading, setLoading] = useState(true)
|
|
11
|
+
const [error, setError] = useState<string | null>(null)
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
launch.projects
|
|
15
|
+
.listAll()
|
|
16
|
+
.then(setProjects)
|
|
17
|
+
.catch((err: Error) => setError(err.message))
|
|
18
|
+
.finally(() => setLoading(false))
|
|
19
|
+
}, [launch])
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<main style=\{{ padding: "2rem", fontFamily: "system-ui, sans-serif" }}>
|
|
23
|
+
<h1>Launch Projects</h1>
|
|
24
|
+
|
|
25
|
+
{loading && <p>Loading projects...</p>}
|
|
26
|
+
{error && <p>Error: {error}</p>}
|
|
27
|
+
|
|
28
|
+
{!loading && !error && (
|
|
29
|
+
<ul>
|
|
30
|
+
{projects.map((project) => (
|
|
31
|
+
<li key={project.uid}>{project.name}</li>
|
|
32
|
+
))}
|
|
33
|
+
</ul>
|
|
34
|
+
)}
|
|
35
|
+
|
|
36
|
+
{!loading && !error && projects.length === 0 && (
|
|
37
|
+
<p>No projects found in this organization.</p>
|
|
38
|
+
)}
|
|
39
|
+
|
|
40
|
+
<p><a href="/">Back to home</a></p>
|
|
41
|
+
</main>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Metadata } from "next"
|
|
2
|
+
import type { ReactNode } from "react"
|
|
3
|
+
{{#if hasClientFeatures}}
|
|
4
|
+
import { Providers } from "./providers"
|
|
5
|
+
{{/if}}
|
|
6
|
+
|
|
7
|
+
export const metadata: Metadata = {
|
|
8
|
+
title: "{{projectName}}",
|
|
9
|
+
description: "Built with Contentstack Platform SDK",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
13
|
+
return (
|
|
14
|
+
<html lang="en">
|
|
15
|
+
<body>
|
|
16
|
+
{{#if hasClientFeatures}}<Providers>{children}</Providers>{{else}}{children}{{/if}}
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{{#if features.oauth}}
|
|
2
|
+
import { auth } from "@/lib/auth"
|
|
3
|
+
{{/if}}
|
|
4
|
+
|
|
5
|
+
export default {{#if features.oauth}}async {{/if}}function Home() {
|
|
6
|
+
{{#if features.oauth}}
|
|
7
|
+
const session = await auth()
|
|
8
|
+
{{/if}}
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<main style=\{{ padding: "2rem", fontFamily: "system-ui, sans-serif" }}>
|
|
12
|
+
<h1>{{projectName}}</h1>
|
|
13
|
+
<p>Region: <code>{{region}}</code></p>
|
|
14
|
+
<p>Built with <a href="https://github.com/contentstack/platform-sdk">Contentstack Platform SDK</a></p>
|
|
15
|
+
|
|
16
|
+
<h2>Getting Started</h2>
|
|
17
|
+
<ol>
|
|
18
|
+
<li>Copy <code>.env.example</code> to <code>.env.local</code></li>
|
|
19
|
+
<li>Fill in your Contentstack credentials</li>
|
|
20
|
+
<li>Start building!</li>
|
|
21
|
+
</ol>
|
|
22
|
+
{{#if features.oauth}}
|
|
23
|
+
|
|
24
|
+
<h2>Authentication</h2>
|
|
25
|
+
{session ? (
|
|
26
|
+
<p>Signed in. <a href="/api/auth/signout">Sign out</a></p>
|
|
27
|
+
) : (
|
|
28
|
+
<p><a href="/api/auth/signin">Sign in with Contentstack</a></p>
|
|
29
|
+
)}
|
|
30
|
+
{{/if}}
|
|
31
|
+
{{#if features.cma}}
|
|
32
|
+
|
|
33
|
+
<h2>Content Management</h2>
|
|
34
|
+
<ul>
|
|
35
|
+
<li><a href="/entries">Entries</a></li>
|
|
36
|
+
<li><a href="/assets">Assets</a></li>
|
|
37
|
+
</ul>
|
|
38
|
+
{{/if}}
|
|
39
|
+
{{#if features.launch}}
|
|
40
|
+
|
|
41
|
+
<h2>Launch</h2>
|
|
42
|
+
<ul>
|
|
43
|
+
<li><a href="/launch">Projects</a></li>
|
|
44
|
+
</ul>
|
|
45
|
+
{{/if}}
|
|
46
|
+
{{#if features.brandkit}}
|
|
47
|
+
|
|
48
|
+
<h2>Brand Kit AI</h2>
|
|
49
|
+
<ul>
|
|
50
|
+
<li><a href="/brandkit">Brand Kits</a></li>
|
|
51
|
+
</ul>
|
|
52
|
+
{{/if}}
|
|
53
|
+
</main>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { ContentstackProvider } from "@timbenniks/contentstack-platform-sdk/react/provider"
|
|
4
|
+
|
|
5
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
6
|
+
return (
|
|
7
|
+
<ContentstackProvider region="{{region}}">
|
|
8
|
+
{children}
|
|
9
|
+
</ContentstackProvider>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContentstackAuth } from "@timbenniks/contentstack-platform-sdk/server/middleware"
|
|
2
|
+
import type { ContentstackRegion } from "@timbenniks/contentstack-platform-sdk/regions"
|
|
3
|
+
|
|
4
|
+
export const { handlers, auth } = await createContentstackAuth({
|
|
5
|
+
region: (process.env.CONTENTSTACK_REGION ?? "{{region}}") as ContentstackRegion,
|
|
6
|
+
appId: process.env.CONTENTSTACK_APP_ID!,
|
|
7
|
+
clientId: process.env.CONTENTSTACK_CLIENT_ID!,
|
|
8
|
+
clientSecret: process.env.CONTENTSTACK_CLIENT_SECRET!,
|
|
9
|
+
secret: process.env.AUTH_SECRET!,
|
|
10
|
+
scopes: [{{#each scopes}}"{{this}}"{{#unless @last}}, {{/unless}}{{/each}}],
|
|
11
|
+
trustHost: true,
|
|
12
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "path": "package.json" },
|
|
3
|
+
{ "path": ".env.example" },
|
|
4
|
+
{ "path": ".gitignore" },
|
|
5
|
+
{ "path": "tsconfig.json" },
|
|
6
|
+
{ "path": "next.config.js" },
|
|
7
|
+
{ "path": "lib/auth.ts", "requiredFeatures": ["oauth"] },
|
|
8
|
+
{ "path": "app/layout.tsx" },
|
|
9
|
+
{ "path": "app/page.tsx" },
|
|
10
|
+
{ "path": "app/providers.tsx", "anyFeatures": ["cma", "launch", "brandkit"] },
|
|
11
|
+
{ "path": "app/api/auth/[...nextauth]/route.ts", "requiredFeatures": ["oauth"] },
|
|
12
|
+
{ "path": "app/api/cma/[...path]/route.ts", "requiredFeatures": ["cma"] },
|
|
13
|
+
{ "path": "app/api/launch/[...path]/route.ts", "requiredFeatures": ["launch"] },
|
|
14
|
+
{ "path": "app/api/brandkit/[...path]/route.ts", "requiredFeatures": ["brandkit"] },
|
|
15
|
+
{ "path": "app/entries/page.tsx", "requiredFeatures": ["cma"] },
|
|
16
|
+
{ "path": "app/assets/page.tsx", "requiredFeatures": ["cma"] },
|
|
17
|
+
{ "path": "app/launch/page.tsx", "requiredFeatures": ["launch"] },
|
|
18
|
+
{ "path": "app/brandkit/page.tsx", "requiredFeatures": ["brandkit"] }
|
|
19
|
+
]
|