create-apiagex 0.6.2 → 0.6.3
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/README.md +8 -4
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +53 -0
- package/dist/create-apiagex.type.d.ts +15 -0
- package/dist/create-apiagex.type.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +32 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +65 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,18 +15,20 @@ Current CLI behavior:
|
|
|
15
15
|
- Supports `--dry-run` to show the scaffold plan without writing files.
|
|
16
16
|
- Creates a small starter scaffold when the target folder is missing or empty.
|
|
17
17
|
|
|
18
|
-
Interactive setup asks for setup mode, package manager, dependency install preference, git init preference, and owner bootstrap
|
|
18
|
+
Interactive setup asks for setup mode, SQLite database path, host, port, package manager, dependency install preference, git init preference, and optional first owner bootstrap credentials. Postgres and MySQL are planned providers, but SQLite is the supported provider today.
|
|
19
19
|
|
|
20
20
|
Generated starter files:
|
|
21
21
|
|
|
22
22
|
- `package.json`
|
|
23
23
|
- `.gitignore`
|
|
24
|
+
- `.env`
|
|
24
25
|
- `.env.example`
|
|
25
26
|
- `apiagex.config.json`
|
|
27
|
+
- `src/index.js`
|
|
26
28
|
- `README.md`
|
|
27
29
|
- `docs/README.md`
|
|
28
30
|
|
|
29
|
-
The generated `.env
|
|
31
|
+
The generated `.env` stores local setup values such as `APIAGEX_DATABASE_PROVIDER`, `APIAGEX_DATABASE_PATH`, `APIAGEX_UPLOADS_PATH`, `APIAGEX_SECRET`, `HOST`, and `PORT`. If owner bootstrap is enabled, `.env` also contains `APIAGEX_OWNER_EMAIL` and `APIAGEX_OWNER_PASSWORD`; remove the password after the first successful start.
|
|
30
32
|
|
|
31
33
|
The generated `package.json` depends on `@apiagex/server` and exposes `npm run dev`, `npm run start`, `npm run smoke`, and `npm run build`.
|
|
32
34
|
|
|
@@ -78,18 +80,20 @@ Current CLI behavior:
|
|
|
78
80
|
- `--dry-run` scaffold plan dikhata hai bina files likhe.
|
|
79
81
|
- Target folder missing ya empty ho to small starter scaffold create hota hai.
|
|
80
82
|
|
|
81
|
-
Interactive setup setup mode, package manager, dependency install preference, git init preference, aur owner bootstrap
|
|
83
|
+
Interactive setup setup mode, SQLite database path, host, port, package manager, dependency install preference, git init preference, aur optional first owner bootstrap credentials puchta hai. Postgres aur MySQL planned providers hain, lekin aaj SQLite supported provider hai.
|
|
82
84
|
|
|
83
85
|
Generated starter files:
|
|
84
86
|
|
|
85
87
|
- `package.json`
|
|
86
88
|
- `.gitignore`
|
|
89
|
+
- `.env`
|
|
87
90
|
- `.env.example`
|
|
88
91
|
- `apiagex.config.json`
|
|
92
|
+
- `src/index.js`
|
|
89
93
|
- `README.md`
|
|
90
94
|
- `docs/README.md`
|
|
91
95
|
|
|
92
|
-
Generated `.env
|
|
96
|
+
Generated `.env` local setup values store karta hai, jaise `APIAGEX_DATABASE_PROVIDER`, `APIAGEX_DATABASE_PATH`, `APIAGEX_UPLOADS_PATH`, `APIAGEX_SECRET`, `HOST`, aur `PORT`. Owner bootstrap enable ho to `.env` me `APIAGEX_OWNER_EMAIL` aur `APIAGEX_OWNER_PASSWORD` bhi hota hai; first successful start ke baad password hata do.
|
|
93
97
|
|
|
94
98
|
Generated `package.json` `@apiagex/server` par depend karta hai aur `npm run dev`, `npm run start`, `npm run smoke`, aur `npm run build` expose karta hai.
|
|
95
99
|
|
package/dist/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA+C,MAAM,0BAA0B,CAAC;AAExG,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,MAAM,CAiE7D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKtE"}
|
package/dist/args.js
CHANGED
|
@@ -22,6 +22,56 @@ export function parseArgs(args) {
|
|
|
22
22
|
options.bootstrapOwner = true;
|
|
23
23
|
else if (arg === "--no-owner")
|
|
24
24
|
options.bootstrapOwner = false;
|
|
25
|
+
else if (arg === "--database") {
|
|
26
|
+
const value = args[index + 1];
|
|
27
|
+
if (!isDatabaseProvider(value))
|
|
28
|
+
return "Use --database sqlite. Postgres and MySQL are planned but not available yet.";
|
|
29
|
+
options.databaseProvider = value;
|
|
30
|
+
index += 1;
|
|
31
|
+
}
|
|
32
|
+
else if (arg === "--database-path") {
|
|
33
|
+
const value = args[index + 1];
|
|
34
|
+
if (!value)
|
|
35
|
+
return "Use --database-path with a SQLite database path.";
|
|
36
|
+
options.databasePath = value;
|
|
37
|
+
index += 1;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--host") {
|
|
40
|
+
const value = args[index + 1];
|
|
41
|
+
if (!value)
|
|
42
|
+
return "Use --host with a host value.";
|
|
43
|
+
options.host = value;
|
|
44
|
+
index += 1;
|
|
45
|
+
}
|
|
46
|
+
else if (arg === "--port") {
|
|
47
|
+
const value = args[index + 1];
|
|
48
|
+
if (!value || !/^\d+$/.test(value))
|
|
49
|
+
return "Use --port with a numeric port.";
|
|
50
|
+
options.port = value;
|
|
51
|
+
index += 1;
|
|
52
|
+
}
|
|
53
|
+
else if (arg === "--app-secret") {
|
|
54
|
+
const value = args[index + 1];
|
|
55
|
+
if (!value)
|
|
56
|
+
return "Use --app-secret with a non-empty secret.";
|
|
57
|
+
options.appSecret = value;
|
|
58
|
+
index += 1;
|
|
59
|
+
}
|
|
60
|
+
else if (arg === "--owner-email") {
|
|
61
|
+
const value = args[index + 1];
|
|
62
|
+
if (!value)
|
|
63
|
+
return "Use --owner-email with an email address.";
|
|
64
|
+
options.ownerEmail = value;
|
|
65
|
+
index += 1;
|
|
66
|
+
}
|
|
67
|
+
else if (arg === "--owner-password") {
|
|
68
|
+
const value = args[index + 1];
|
|
69
|
+
if (!value)
|
|
70
|
+
return "Use --owner-password with a password.";
|
|
71
|
+
options.ownerPassword = value;
|
|
72
|
+
index += 1;
|
|
73
|
+
options.bootstrapOwner = true;
|
|
74
|
+
}
|
|
25
75
|
else if (arg === "--package-manager") {
|
|
26
76
|
const value = args[index + 1];
|
|
27
77
|
if (!isPackageManager(value))
|
|
@@ -57,6 +107,9 @@ export function validateProjectSlug(target) {
|
|
|
57
107
|
function isPackageManager(value) {
|
|
58
108
|
return value === "npm" || value === "pnpm" || value === "yarn";
|
|
59
109
|
}
|
|
110
|
+
function isDatabaseProvider(value) {
|
|
111
|
+
return value === "sqlite";
|
|
112
|
+
}
|
|
60
113
|
function isSetupMode(value) {
|
|
61
114
|
return value === "quickstart" || value === "custom";
|
|
62
115
|
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import type { Readable, Writable } from "node:stream";
|
|
2
2
|
export type PackageManager = "npm" | "pnpm" | "yarn";
|
|
3
3
|
export type SetupMode = "custom" | "quickstart";
|
|
4
|
+
export type DatabaseProvider = "sqlite";
|
|
4
5
|
export type CliOptions = {
|
|
6
|
+
appSecret?: string;
|
|
5
7
|
bootstrapOwner?: boolean;
|
|
8
|
+
databasePath?: string;
|
|
9
|
+
databaseProvider?: DatabaseProvider;
|
|
6
10
|
dryRun: boolean;
|
|
7
11
|
help: boolean;
|
|
12
|
+
host?: string;
|
|
8
13
|
initGit?: boolean;
|
|
9
14
|
installDependencies?: boolean;
|
|
15
|
+
ownerEmail?: string;
|
|
16
|
+
ownerPassword?: string;
|
|
10
17
|
packageManager?: PackageManager;
|
|
18
|
+
port?: string;
|
|
11
19
|
setupMode?: SetupMode;
|
|
12
20
|
target?: string;
|
|
13
21
|
version: boolean;
|
|
@@ -30,10 +38,17 @@ export type RunCliOptions = {
|
|
|
30
38
|
stdout?: Writable;
|
|
31
39
|
};
|
|
32
40
|
export type ScaffoldAnswers = {
|
|
41
|
+
appSecret: string;
|
|
33
42
|
bootstrapOwner: boolean;
|
|
43
|
+
databasePath: string;
|
|
44
|
+
databaseProvider: DatabaseProvider;
|
|
45
|
+
host: string;
|
|
34
46
|
initGit: boolean;
|
|
35
47
|
installDependencies: boolean;
|
|
48
|
+
ownerEmail?: string;
|
|
49
|
+
ownerPassword?: string;
|
|
36
50
|
packageManager: PackageManager;
|
|
51
|
+
port: string;
|
|
37
52
|
setupMode: SetupMode;
|
|
38
53
|
target: string;
|
|
39
54
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-apiagex.type.d.ts","sourceRoot":"","sources":["../src/create-apiagex.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AACrD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"create-apiagex.type.d.ts","sourceRoot":"","sources":["../src/create-apiagex.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AACrD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,YAAY,CAAC;AAChD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;AAExC,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAErE,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,cAAc,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { parseArgs, validateProjectSlug } from "./args.js";
|
|
8
8
|
import { resolveAnswers } from "./prompts.js";
|
|
9
9
|
import { createScaffoldFiles, renderPlan } from "./scaffold.js";
|
|
10
|
-
const packageVersion = "0.6.
|
|
10
|
+
const packageVersion = "0.6.3";
|
|
11
11
|
export async function runCli(args, cwd = process.cwd(), io = {}) {
|
|
12
12
|
const parsed = parseArgs(args);
|
|
13
13
|
if (typeof parsed === "string")
|
|
@@ -53,10 +53,17 @@ Usage:
|
|
|
53
53
|
|
|
54
54
|
Options:
|
|
55
55
|
--setup quickstart|custom Choose starter setup mode.
|
|
56
|
+
--database sqlite Choose database provider. Postgres and MySQL are planned.
|
|
57
|
+
--database-path <path> Set the SQLite database path.
|
|
58
|
+
--host <host> Set the server host.
|
|
59
|
+
--port <port> Set the server port.
|
|
60
|
+
--app-secret <secret> Set APIAGEX_SECRET instead of generating one.
|
|
61
|
+
--owner-email <email> Set first owner email when owner bootstrap is enabled.
|
|
62
|
+
--owner-password <password> Set first owner password and enable owner bootstrap.
|
|
56
63
|
--package-manager npm|pnpm|yarn
|
|
57
64
|
--install, --no-install Record whether dependencies should be installed after scaffold.
|
|
58
65
|
--git, --no-git Record whether git should be initialized after scaffold.
|
|
59
|
-
--owner, --no-owner
|
|
66
|
+
--owner, --no-owner Configure first owner bootstrap on first server start.
|
|
60
67
|
--dry-run Print the scaffold plan without writing files.
|
|
61
68
|
-y, --yes Use defaults for missing options.
|
|
62
69
|
-h, --help Show help.
|
package/dist/prompts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EAEV,aAAa,EACb,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,wBAAsB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,GAAE,aAAkB,GAAG,OAAO,CAAC,eAAe,GAAG,MAAM,CAAC,CAkCnH"}
|
package/dist/prompts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
2
3
|
export async function resolveAnswers(options, io = {}) {
|
|
3
4
|
const prompt = io.prompt ?? nodePrompt(io);
|
|
4
5
|
const canAsk = Boolean((io.interactive || io.prompt) && !options.yes);
|
|
@@ -6,12 +7,31 @@ export async function resolveAnswers(options, io = {}) {
|
|
|
6
7
|
if (!target)
|
|
7
8
|
return "Target folder is required. Run create-apiagex --help for usage.";
|
|
8
9
|
const setupMode = options.setupMode ?? await choiceAnswer(prompt, canAsk, "Setup mode", "quickstart", ["quickstart", "custom"]);
|
|
10
|
+
const databaseProvider = options.databaseProvider ?? "sqlite";
|
|
11
|
+
const databasePath = options.databasePath ?? await setupAnswer(prompt, canAsk, setupMode, "SQLite database path", ".apiagex/apiagex.sqlite");
|
|
12
|
+
const host = options.host ?? await setupAnswer(prompt, canAsk, setupMode, "Server host", "127.0.0.1");
|
|
13
|
+
const port = options.port ?? await setupAnswer(prompt, canAsk, setupMode, "Server port", "4000");
|
|
14
|
+
const appSecret = options.appSecret ?? generateSecret();
|
|
9
15
|
const packageManager = options.packageManager ?? await choiceAnswer(prompt, canAsk, "Package manager", "npm", ["npm", "pnpm", "yarn"]);
|
|
16
|
+
const bootstrapOwner = await boolAnswer(prompt, canAsk, "Create first owner during first server start?", options.bootstrapOwner, false);
|
|
17
|
+
const ownerEmail = bootstrapOwner
|
|
18
|
+
? options.ownerEmail ?? await setupAnswer(prompt, canAsk, setupMode, "Owner email", "owner@apiagex.local")
|
|
19
|
+
: undefined;
|
|
20
|
+
const ownerPassword = bootstrapOwner
|
|
21
|
+
? options.ownerPassword ?? await setupAnswer(prompt, canAsk, setupMode, "Owner password", generatePassword())
|
|
22
|
+
: undefined;
|
|
10
23
|
return {
|
|
11
|
-
|
|
24
|
+
appSecret,
|
|
25
|
+
bootstrapOwner,
|
|
26
|
+
databasePath,
|
|
27
|
+
databaseProvider,
|
|
28
|
+
host,
|
|
12
29
|
initGit: await boolAnswer(prompt, canAsk, "Initialize git repository?", options.initGit, true),
|
|
13
30
|
installDependencies: await boolAnswer(prompt, canAsk, "Install dependencies after scaffold?", options.installDependencies, false),
|
|
31
|
+
...(ownerEmail === undefined ? {} : { ownerEmail }),
|
|
32
|
+
...(ownerPassword === undefined ? {} : { ownerPassword }),
|
|
14
33
|
packageManager,
|
|
34
|
+
port,
|
|
15
35
|
setupMode,
|
|
16
36
|
target,
|
|
17
37
|
};
|
|
@@ -28,10 +48,21 @@ async function choiceAnswer(prompt, canAsk, message, defaultValue, allowed) {
|
|
|
28
48
|
const answer = await ask(prompt, `${message} (${allowed.join("/")})`, defaultValue);
|
|
29
49
|
return allowed.includes(answer) ? answer : defaultValue;
|
|
30
50
|
}
|
|
51
|
+
async function setupAnswer(prompt, canAsk, setupMode, message, defaultValue) {
|
|
52
|
+
if (!canAsk || setupMode !== "custom")
|
|
53
|
+
return defaultValue;
|
|
54
|
+
return ask(prompt, message, defaultValue);
|
|
55
|
+
}
|
|
31
56
|
async function ask(prompt, message, defaultValue) {
|
|
32
57
|
const answer = (await prompt({ defaultValue, message })).trim();
|
|
33
58
|
return answer || defaultValue;
|
|
34
59
|
}
|
|
60
|
+
function generateSecret() {
|
|
61
|
+
return randomBytes(32).toString("base64url");
|
|
62
|
+
}
|
|
63
|
+
function generatePassword() {
|
|
64
|
+
return `Apiagex-${randomBytes(9).toString("base64url")}1!`;
|
|
65
|
+
}
|
|
35
66
|
function nodePrompt(io) {
|
|
36
67
|
return async ({ defaultValue, message }) => {
|
|
37
68
|
const input = io.stdin ?? process.stdin;
|
package/dist/scaffold.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE9E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE9E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,YAAY,EAAE,CAiE5E;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CA0B3I"}
|
package/dist/scaffold.js
CHANGED
|
@@ -8,13 +8,13 @@ export function createScaffoldFiles(answers) {
|
|
|
8
8
|
private: true,
|
|
9
9
|
type: "module",
|
|
10
10
|
scripts: {
|
|
11
|
-
dev: "
|
|
12
|
-
start: "
|
|
11
|
+
dev: "node --env-file=.env src/index.js",
|
|
12
|
+
start: "node --env-file=.env src/index.js",
|
|
13
13
|
build: "apiagex build",
|
|
14
14
|
smoke: "apiagex smoke",
|
|
15
15
|
},
|
|
16
16
|
dependencies: {
|
|
17
|
-
"@apiagex/server": "^0.6.
|
|
17
|
+
"@apiagex/server": "^0.6.3",
|
|
18
18
|
},
|
|
19
19
|
}, null, 2)}\n`,
|
|
20
20
|
},
|
|
@@ -26,17 +26,30 @@ export function createScaffoldFiles(answers) {
|
|
|
26
26
|
path: ".gitignore",
|
|
27
27
|
content: "node_modules\n.env\ndist\n.apiagex\n",
|
|
28
28
|
},
|
|
29
|
+
{
|
|
30
|
+
path: ".env",
|
|
31
|
+
content: envFile(answers, true),
|
|
32
|
+
},
|
|
29
33
|
{
|
|
30
34
|
path: ".env.example",
|
|
31
|
-
content:
|
|
35
|
+
content: envFile(answers, false),
|
|
32
36
|
},
|
|
33
37
|
{
|
|
34
38
|
path: "apiagex.config.json",
|
|
35
39
|
content: `${JSON.stringify({
|
|
36
|
-
database: { provider:
|
|
37
|
-
project: {
|
|
40
|
+
database: { provider: answers.databaseProvider, path: answers.databasePath },
|
|
41
|
+
project: {
|
|
42
|
+
appSecretEnv: "APIAGEX_SECRET",
|
|
43
|
+
packageManager: answers.packageManager,
|
|
44
|
+
setupMode: answers.setupMode,
|
|
45
|
+
},
|
|
46
|
+
server: { host: answers.host, port: Number(answers.port) },
|
|
38
47
|
}, null, 2)}\n`,
|
|
39
48
|
},
|
|
49
|
+
{
|
|
50
|
+
path: "src/index.js",
|
|
51
|
+
content: serverEntryFile(),
|
|
52
|
+
},
|
|
40
53
|
{
|
|
41
54
|
path: "docs/README.md",
|
|
42
55
|
content: docsReadme(),
|
|
@@ -52,10 +65,13 @@ export function renderPlan(projectName, targetDir, files, answers, dryRun) {
|
|
|
52
65
|
"",
|
|
53
66
|
"Selected setup:",
|
|
54
67
|
`- Setup mode: ${answers.setupMode}`,
|
|
68
|
+
`- Database: ${answers.databaseProvider}`,
|
|
69
|
+
`- SQLite path: ${answers.databasePath}`,
|
|
70
|
+
`- Server: http://${answers.host}:${answers.port}`,
|
|
55
71
|
`- Package manager: ${answers.packageManager}`,
|
|
56
72
|
`- Install dependencies: ${answers.installDependencies ? "yes" : "no"}`,
|
|
57
73
|
`- Initialize git: ${answers.initGit ? "yes" : "no"}`,
|
|
58
|
-
`- Owner setup: ${answers.bootstrapOwner ? "create
|
|
74
|
+
`- Owner setup: ${answers.bootstrapOwner ? "create from .env on first start" : "create from Admin UI"}`,
|
|
59
75
|
"",
|
|
60
76
|
"Files:",
|
|
61
77
|
fileList,
|
|
@@ -86,28 +102,32 @@ ${runCommand(answers.packageManager, "dev")}
|
|
|
86
102
|
\`\`\`
|
|
87
103
|
|
|
88
104
|
Open http://127.0.0.1:4000/adminui to create the first owner. Open /doc for API docs and /readme for the readable project summary.
|
|
105
|
+
If .env contains APIAGEX_OWNER_EMAIL and APIAGEX_OWNER_PASSWORD, the first owner is created automatically on first server start. Remove APIAGEX_OWNER_PASSWORD from .env after the first successful start.
|
|
89
106
|
|
|
90
107
|
## Scripts
|
|
91
108
|
|
|
92
|
-
- \`${runCommand(answers.packageManager, "dev")}\`: start
|
|
93
|
-
- \`${runCommand(answers.packageManager, "start")}\`: start
|
|
109
|
+
- \`${runCommand(answers.packageManager, "dev")}\`: start src/index.js with .env.
|
|
110
|
+
- \`${runCommand(answers.packageManager, "start")}\`: start src/index.js with .env.
|
|
94
111
|
- \`${runCommand(answers.packageManager, "smoke")}\`: verify the runtime health route.
|
|
95
112
|
- \`${runCommand(answers.packageManager, "build")}\`: print runtime build guidance.
|
|
96
113
|
|
|
97
114
|
## Environment
|
|
98
115
|
|
|
99
|
-
Copy .env.example to .env if you need
|
|
116
|
+
The installer creates .env for local use. Copy .env.example to .env again if you need to reset local settings.
|
|
100
117
|
|
|
118
|
+
- APIAGEX_DATABASE_PROVIDER: sqlite today. Postgres and MySQL are planned.
|
|
101
119
|
- APIAGEX_DATABASE_PATH: SQLite database path. Default .apiagex/apiagex.sqlite.
|
|
102
120
|
- APIAGEX_UPLOADS_PATH: upload folder. Default .apiagex/uploads.
|
|
121
|
+
- APIAGEX_SECRET: app secret generated during setup.
|
|
103
122
|
- PORT: server port. Default 4000.
|
|
104
123
|
- HOST: server host. Default 127.0.0.1.
|
|
124
|
+
- APIAGEX_OWNER_EMAIL and APIAGEX_OWNER_PASSWORD: optional first owner bootstrap values. Remove the password after first start.
|
|
105
125
|
|
|
106
126
|
## Practical flow
|
|
107
127
|
|
|
108
128
|
English:
|
|
109
129
|
|
|
110
|
-
1.
|
|
130
|
+
1. Start the server. If owner env values exist, Apiagex creates the first owner automatically; otherwise create it from /adminui.
|
|
111
131
|
2. Create a schema, for example Article with a required title field.
|
|
112
132
|
3. Create entries from Entries or call POST /api/content/article.
|
|
113
133
|
4. Create Content Roles, save permissions, then create users or API tokens.
|
|
@@ -115,7 +135,7 @@ English:
|
|
|
115
135
|
|
|
116
136
|
Hinglish:
|
|
117
137
|
|
|
118
|
-
1.
|
|
138
|
+
1. Server start karo. Owner env values hain to Apiagex first owner automatic create karega; nahi hain to /adminui se create karo.
|
|
119
139
|
2. Schema banao, jaise required title field ke saath Article.
|
|
120
140
|
3. Entries screen se entry banao ya POST /api/content/article call karo.
|
|
121
141
|
4. Content Roles banao, permissions save karo, phir users ya API tokens create karo.
|
|
@@ -144,7 +164,7 @@ Use /doc for generated API docs and /readme for the project summary.
|
|
|
144
164
|
|
|
145
165
|
English: Open /adminui, create the first owner, then use the same page for later logins.
|
|
146
166
|
|
|
147
|
-
Hinglish: /adminui open karo, first owner create karo, phir later login ke liye same page use karo.
|
|
167
|
+
Hinglish: /adminui open karo, first owner create karo ya .env owner bootstrap use karo, phir later login ke liye same page use karo.
|
|
148
168
|
|
|
149
169
|
## Generated APIs
|
|
150
170
|
|
|
@@ -167,6 +187,38 @@ Hinglish: Webhooks content change ke baad external URLs call karte hain. Realtim
|
|
|
167
187
|
Relation docs: /doc explains relation field types, entry payloads, populate query options, Admin UI entry pickers, and common errors.
|
|
168
188
|
`;
|
|
169
189
|
}
|
|
190
|
+
function envFile(answers, includeSecrets) {
|
|
191
|
+
const lines = [
|
|
192
|
+
`APIAGEX_DATABASE_PROVIDER=${answers.databaseProvider}`,
|
|
193
|
+
`APIAGEX_DATABASE_PATH=${answers.databasePath}`,
|
|
194
|
+
"APIAGEX_UPLOADS_PATH=.apiagex/uploads",
|
|
195
|
+
`APIAGEX_SECRET=${includeSecrets ? answers.appSecret : "change-me"}`,
|
|
196
|
+
`HOST=${answers.host}`,
|
|
197
|
+
`PORT=${answers.port}`,
|
|
198
|
+
];
|
|
199
|
+
if (answers.bootstrapOwner) {
|
|
200
|
+
lines.push(`APIAGEX_OWNER_EMAIL=${answers.ownerEmail ?? "owner@apiagex.local"}`, `APIAGEX_OWNER_PASSWORD=${includeSecrets ? answers.ownerPassword ?? "" : "change-me-after-first-start"}`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
lines.push("# APIAGEX_OWNER_EMAIL=owner@apiagex.local", "# APIAGEX_OWNER_PASSWORD=change-me-after-first-start");
|
|
204
|
+
}
|
|
205
|
+
return `${lines.join("\n")}\n`;
|
|
206
|
+
}
|
|
207
|
+
function serverEntryFile() {
|
|
208
|
+
return `import { startApiagex } from "@apiagex/server";
|
|
209
|
+
|
|
210
|
+
const ownerEmail = process.env.APIAGEX_OWNER_EMAIL;
|
|
211
|
+
const ownerPassword = process.env.APIAGEX_OWNER_PASSWORD;
|
|
212
|
+
|
|
213
|
+
await startApiagex({
|
|
214
|
+
host: process.env.HOST ?? "127.0.0.1",
|
|
215
|
+
port: Number(process.env.PORT ?? 4000),
|
|
216
|
+
initialOwner: ownerEmail && ownerPassword
|
|
217
|
+
? { email: ownerEmail, password: ownerPassword }
|
|
218
|
+
: undefined,
|
|
219
|
+
});
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
170
222
|
function installCommand(packageManager) {
|
|
171
223
|
return packageManager === "yarn" ? "yarn install" : `${packageManager} install`;
|
|
172
224
|
}
|