create-wakii-stack 0.1.2 → 0.1.4
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/index.js +59 -12
- package/package.json +1 -1
- package/stack-template/stack-manifest.json +11 -0
package/dist/index.js
CHANGED
|
@@ -81,7 +81,7 @@ function resolveOptions(options, ctx) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// src/scaffold.ts
|
|
84
|
-
import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
|
|
84
|
+
import { chmod, mkdir, readFile, readdir, rm, stat, writeFile } from "fs/promises";
|
|
85
85
|
import { existsSync } from "fs";
|
|
86
86
|
import { dirname, join, resolve } from "path";
|
|
87
87
|
import { fileURLToPath } from "url";
|
|
@@ -186,7 +186,20 @@ function pruneEnv(text, keys) {
|
|
|
186
186
|
}).join("\n").replace(/\n{3,}/g, "\n\n");
|
|
187
187
|
}
|
|
188
188
|
function renderReadme(manifest, selection) {
|
|
189
|
-
const
|
|
189
|
+
const gatewayUrl = manifest.ports?.gateway ?? "http://localhost:8080";
|
|
190
|
+
const chosen = new Set(selection.services);
|
|
191
|
+
const ports = Object.entries(manifest.ports ?? {}).filter(([name]) => chosen.has(name)).map(([name, url]) => {
|
|
192
|
+
const note = name === "gateway" ? " \u2014 entry duy nh\u1EA5t: `/` = FE shell, `/api/*` = services" : name === "shell" ? " \u2014 static-only (login \u1EDF \u0111\xE2y b\xE1o 405 \u2014 d\xF9ng gateway)" : "";
|
|
193
|
+
return `- ${name}: ${url}${note}`;
|
|
194
|
+
}).join("\n");
|
|
195
|
+
const access = chosen.has("gateway") ? `## Truy c\u1EADp (sau khi up)
|
|
196
|
+
|
|
197
|
+
M\u1EDF **${gatewayUrl}** (gateway) \u2014 entry duy nh\u1EA5t: \`/\` l\xE0 FE shell, \`/api/*\` l\xE0 c\xE1c service.
|
|
198
|
+
|
|
199
|
+
- Ch\u01B0a c\xF3 t\xE0i kho\u1EA3n? auth-svc KH\xD4NG seed user \u2014 d\xF9ng tab **\u0110\u0103ng k\xFD** tr\u01B0\u1EDBc, r\u1ED3i \u0110\u0103ng nh\u1EADp.
|
|
200
|
+
- KH\xD4NG m\u1EDF port FE tr\u1EF1c ti\u1EBFp (vd \`:3000\`): \u0111\xF3 l\xE0 static server, m\u1ECDi \`POST /api/*\` tr\u1EA3 **405**.
|
|
201
|
+
|
|
202
|
+
` : "";
|
|
190
203
|
return `# ${selection.appName}
|
|
191
204
|
|
|
192
205
|
Stack generated by \`create-wakii-stack\` (Hybrid C \u2014 full template pruned to your selection).
|
|
@@ -195,7 +208,7 @@ Stack generated by \`create-wakii-stack\` (Hybrid C \u2014 full template pruned
|
|
|
195
208
|
|
|
196
209
|
${selection.services.map((s) => `- ${s} _(${manifest.services[s]?.layer ?? "?"})_`).join("\n")}
|
|
197
210
|
|
|
198
|
-
## Run
|
|
211
|
+
${access}## Run
|
|
199
212
|
|
|
200
213
|
\`\`\`bash
|
|
201
214
|
cd ${selection.appName}
|
|
@@ -209,7 +222,7 @@ ${manifest.smoke ? `## Smoke
|
|
|
209
222
|
${manifest.smoke}
|
|
210
223
|
\`\`\`
|
|
211
224
|
` : ""}
|
|
212
|
-
${ports ? `##
|
|
225
|
+
${ports ? `## Ports
|
|
213
226
|
|
|
214
227
|
${ports}
|
|
215
228
|
` : ""}
|
|
@@ -228,6 +241,14 @@ async function scaffold(input) {
|
|
|
228
241
|
await copyTree(templateRoot, dest, { stackName: selection.appName, files });
|
|
229
242
|
const allServices = Object.keys(manifest.services);
|
|
230
243
|
const pruned = allServices.filter((s) => !selection.services.includes(s));
|
|
244
|
+
const ALWAYS_PRUNE = ["app/create-wakii-app", "app/create-wakii-stack", "app/starter-templates", "app/stack-template", "app/spike"];
|
|
245
|
+
for (const relPath of ALWAYS_PRUNE) {
|
|
246
|
+
const target = join(dest, relPath);
|
|
247
|
+
if (existsSync(target)) {
|
|
248
|
+
await rm(target, { recursive: true, force: true });
|
|
249
|
+
log2?.(`Pruned tooling: ${relPath}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
231
252
|
for (const name of pruned) {
|
|
232
253
|
for (const rawPath of manifest.services[name].paths ?? []) {
|
|
233
254
|
const relPath = rawPath.endsWith("/**") ? rawPath.slice(0, -3) : rawPath;
|
|
@@ -263,8 +284,32 @@ async function scaffold(input) {
|
|
|
263
284
|
}
|
|
264
285
|
}
|
|
265
286
|
await writeFile(join(dest, "README.md"), renderReadme(manifest, selection));
|
|
287
|
+
const execFixed = await fixExecutableBits(dest);
|
|
288
|
+
if (execFixed > 0) log2?.(`chmod +x: ${execFixed} script`);
|
|
266
289
|
return { files: [...files.map((f) => f.slice(dest.length + 1)), "README.md"], prunedServices: pruned };
|
|
267
290
|
}
|
|
291
|
+
async function fixExecutableBits(root) {
|
|
292
|
+
let count = 0;
|
|
293
|
+
const walk = async (dir) => {
|
|
294
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
295
|
+
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
296
|
+
const full = join(dir, entry.name);
|
|
297
|
+
if (entry.isDirectory()) {
|
|
298
|
+
await walk(full);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (/\.(sh)$/.test(entry.name) || entry.name === "mvnw" || entry.name === "gradlew") {
|
|
302
|
+
const st = await stat(full);
|
|
303
|
+
if (!(st.mode & 73)) {
|
|
304
|
+
await chmod(full, st.mode | 493);
|
|
305
|
+
count += 1;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
await walk(root);
|
|
311
|
+
return count;
|
|
312
|
+
}
|
|
268
313
|
|
|
269
314
|
// src/index.ts
|
|
270
315
|
var USAGE = `create-wakii-stack \u2014 scaffold full-stack FE+BE (Hybrid C: full-copy + prune)
|
|
@@ -280,16 +325,18 @@ Flags:
|
|
|
280
325
|
|
|
281
326
|
Rules t\u1EA7ng 1: \u22651 BE t\u1EF1 k\xE8m postgres+kafka+gateway+auth-svc; \u22651 FE t\u1EF1 k\xE8m gateway.
|
|
282
327
|
`;
|
|
283
|
-
function printNextSteps(selection,
|
|
284
|
-
const steps = [
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
328
|
+
function printNextSteps(selection, gatewayUrl) {
|
|
329
|
+
const steps = [
|
|
330
|
+
`cd ${selection.appName}`,
|
|
331
|
+
"cp platform/.env.example platform/.env # \u0111i\u1EC1n secret tr\u01B0\u1EDBc khi up",
|
|
332
|
+
"docker compose --env-file platform/.env up -d"
|
|
333
|
+
];
|
|
289
334
|
if (!selection.skipInstall) steps.push("# FE apps: pnpm install trong t\u1EEBng app mu\u1ED1n ch\u1EA1y build c\u1EE5c b\u1ED9");
|
|
290
335
|
p.log.info("Next steps:");
|
|
291
336
|
for (const step of steps) p.log.step(` ${step}`);
|
|
292
|
-
|
|
337
|
+
const entry = gatewayUrl ?? "http://localhost:8080";
|
|
338
|
+
p.log.info(`M\u1EDF ${entry} (gateway \u2014 entry duy nh\u1EA5t: / l\xE0 FE shell, /api/* l\xE0 services). \u0110\u0103ng k\xFD t\xE0i kho\u1EA3n tr\u01B0\u1EDBc khi login (auth-svc kh\xF4ng seed user).`);
|
|
339
|
+
p.log.warn("Port FE tr\u1EF1c ti\u1EBFp (vd :3000) ch\u1EC9 l\xE0 static server \u2014 login \u1EDF \u0111\xF3 b\xE1o HTTP 405.");
|
|
293
340
|
p.log.info("FE-only SPA ngo\xE0i stack? pnpm create wakii-app");
|
|
294
341
|
}
|
|
295
342
|
async function main() {
|
|
@@ -383,7 +430,7 @@ async function main() {
|
|
|
383
430
|
return;
|
|
384
431
|
}
|
|
385
432
|
p.outro("Stack s\u1EB5n s\xE0ng! \u{1F389}");
|
|
386
|
-
printNextSteps(selection,
|
|
433
|
+
printNextSteps(selection, manifest.ports?.gateway);
|
|
387
434
|
}
|
|
388
435
|
main().catch((err) => {
|
|
389
436
|
p.log.error(err instanceof Error ? err.message : String(err));
|
package/package.json
CHANGED
|
@@ -279,5 +279,16 @@
|
|
|
279
279
|
"when": "không chọn service be nào và không chọn option fe nào",
|
|
280
280
|
"action": "engine phải dừng với lỗi rõ ràng — stack trống không hợp lệ"
|
|
281
281
|
}
|
|
282
|
+
},
|
|
283
|
+
"ports": {
|
|
284
|
+
"gateway": "http://localhost:8080",
|
|
285
|
+
"shell": "http://localhost:3000",
|
|
286
|
+
"remote-tasks": "http://localhost:4001",
|
|
287
|
+
"remote-activity": "http://localhost:4002",
|
|
288
|
+
"auth-svc": "http://localhost:8090",
|
|
289
|
+
"node-svc": "http://localhost:3001",
|
|
290
|
+
"spring-svc": "http://localhost:8081",
|
|
291
|
+
"go-svc": "http://localhost:8082",
|
|
292
|
+
"postgres": "localhost:5432"
|
|
282
293
|
}
|
|
283
294
|
}
|