@donkeylabs/cli 2.0.15 ā 2.0.17
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/package.json +1 -1
- package/src/commands/config.ts +610 -0
- package/src/commands/deploy-enhanced.ts +354 -0
- package/src/commands/deploy.ts +204 -0
- package/src/commands/docs.ts +207 -0
- package/src/commands/init-enhanced.ts +1994 -0
- package/src/deployment/manager.ts +356 -0
- package/src/index.ts +58 -19
- package/templates/starter/.env.example +0 -44
- package/templates/starter/.gitignore.template +0 -4
- package/templates/starter/donkeylabs.config.ts +0 -6
- package/templates/starter/package.json +0 -21
- package/templates/starter/src/index.ts +0 -54
- package/templates/starter/src/plugins/stats/index.ts +0 -105
- package/templates/starter/src/routes/health/handlers/ping.ts +0 -22
- package/templates/starter/src/routes/health/index.ts +0 -19
- package/templates/starter/tsconfig.json +0 -27
- package/templates/sveltekit-app/.env.example +0 -59
- package/templates/sveltekit-app/README.md +0 -103
- package/templates/sveltekit-app/bun.lock +0 -683
- package/templates/sveltekit-app/donkeylabs.config.ts +0 -12
- package/templates/sveltekit-app/package.json +0 -38
- package/templates/sveltekit-app/src/app.css +0 -40
- package/templates/sveltekit-app/src/app.html +0 -12
- package/templates/sveltekit-app/src/hooks.server.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/badge/badge.svelte +0 -30
- package/templates/sveltekit-app/src/lib/components/ui/badge/index.ts +0 -3
- package/templates/sveltekit-app/src/lib/components/ui/button/button.svelte +0 -48
- package/templates/sveltekit-app/src/lib/components/ui/button/index.ts +0 -9
- package/templates/sveltekit-app/src/lib/components/ui/card/card-content.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-description.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-footer.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-header.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-title.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card.svelte +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/card/index.ts +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/index.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/input/index.ts +0 -2
- package/templates/sveltekit-app/src/lib/components/ui/input/input.svelte +0 -20
- package/templates/sveltekit-app/src/lib/permissions.ts +0 -213
- package/templates/sveltekit-app/src/lib/utils/index.ts +0 -6
- package/templates/sveltekit-app/src/routes/+layout.svelte +0 -8
- package/templates/sveltekit-app/src/routes/+page.server.ts +0 -25
- package/templates/sveltekit-app/src/routes/+page.svelte +0 -680
- package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +0 -23
- package/templates/sveltekit-app/src/routes/workflows/+page.svelte +0 -522
- package/templates/sveltekit-app/src/server/events.ts +0 -28
- package/templates/sveltekit-app/src/server/index.ts +0 -124
- package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +0 -377
- package/templates/sveltekit-app/src/server/plugins/auth/index.ts +0 -815
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/001_create_users.ts +0 -25
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/002_create_sessions.ts +0 -32
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/003_create_refresh_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/004_create_passkeys.ts +0 -60
- package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +0 -65
- package/templates/sveltekit-app/src/server/plugins/demo/index.ts +0 -262
- package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +0 -369
- package/templates/sveltekit-app/src/server/plugins/email/index.ts +0 -411
- package/templates/sveltekit-app/src/server/plugins/email/migrations/001_create_email_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/email/schema.ts +0 -24
- package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +0 -1048
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/001_create_tenants.ts +0 -63
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/002_create_roles.ts +0 -90
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/003_create_resource_grants.ts +0 -50
- package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +0 -566
- package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +0 -67
- package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +0 -198
- package/templates/sveltekit-app/src/server/routes/auth/auth.schemas.ts +0 -66
- package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +0 -18
- package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +0 -16
- package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +0 -20
- package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +0 -19
- package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/auth/index.ts +0 -73
- package/templates/sveltekit-app/src/server/routes/demo.ts +0 -464
- package/templates/sveltekit-app/src/server/routes/example/example.schemas.ts +0 -22
- package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/example/index.ts +0 -28
- package/templates/sveltekit-app/src/server/routes/permissions/index.ts +0 -248
- package/templates/sveltekit-app/src/server/routes/tenants/index.ts +0 -339
- package/templates/sveltekit-app/static/robots.txt +0 -3
- package/templates/sveltekit-app/svelte.config.ts +0 -17
- package/templates/sveltekit-app/tsconfig.json +0 -20
- package/templates/sveltekit-app/vite.config.ts +0 -12
package/package.json
CHANGED
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
// packages/cli/src/commands/config.ts
|
|
2
|
+
/**
|
|
3
|
+
* Interactive configuration management
|
|
4
|
+
* Modify plugin configs, deployment settings, etc.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import pc from "picocolors";
|
|
10
|
+
|
|
11
|
+
interface ConfigEntry {
|
|
12
|
+
key: string;
|
|
13
|
+
value: any;
|
|
14
|
+
type: "string" | "number" | "boolean" | "select";
|
|
15
|
+
options?: string[];
|
|
16
|
+
description: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function configCommand(args: string[]) {
|
|
20
|
+
const subcommand = args[0] || "interactive";
|
|
21
|
+
|
|
22
|
+
switch (subcommand) {
|
|
23
|
+
case "list":
|
|
24
|
+
case "ls":
|
|
25
|
+
await listConfig();
|
|
26
|
+
break;
|
|
27
|
+
case "get":
|
|
28
|
+
await getConfigValue(args[1]);
|
|
29
|
+
break;
|
|
30
|
+
case "set":
|
|
31
|
+
await setConfigValue(args[1], args[2]);
|
|
32
|
+
break;
|
|
33
|
+
case "plugins":
|
|
34
|
+
await configurePlugins();
|
|
35
|
+
break;
|
|
36
|
+
case "deployment":
|
|
37
|
+
await configureDeployment();
|
|
38
|
+
break;
|
|
39
|
+
case "database":
|
|
40
|
+
await configureDatabase();
|
|
41
|
+
break;
|
|
42
|
+
case "interactive":
|
|
43
|
+
default:
|
|
44
|
+
await interactiveConfig();
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function interactiveConfig() {
|
|
50
|
+
const prompts = await import("prompts");
|
|
51
|
+
|
|
52
|
+
console.log(pc.cyan(pc.bold("\nāļø Configuration Manager\n")));
|
|
53
|
+
|
|
54
|
+
const action = await prompts.default({
|
|
55
|
+
type: "select",
|
|
56
|
+
name: "value",
|
|
57
|
+
message: "What would you like to configure?",
|
|
58
|
+
choices: [
|
|
59
|
+
{ title: "Plugin settings", value: "plugins" },
|
|
60
|
+
{ title: "Deployment settings", value: "deployment" },
|
|
61
|
+
{ title: "Database configuration", value: "database" },
|
|
62
|
+
{ title: "Environment variables", value: "env" },
|
|
63
|
+
{ title: "View all config", value: "list" },
|
|
64
|
+
],
|
|
65
|
+
}).then((r: any) => r.value);
|
|
66
|
+
|
|
67
|
+
switch (action) {
|
|
68
|
+
case "plugins":
|
|
69
|
+
await configurePlugins();
|
|
70
|
+
break;
|
|
71
|
+
case "deployment":
|
|
72
|
+
await configureDeployment();
|
|
73
|
+
break;
|
|
74
|
+
case "database":
|
|
75
|
+
await configureDatabase();
|
|
76
|
+
break;
|
|
77
|
+
case "env":
|
|
78
|
+
await configureEnv();
|
|
79
|
+
break;
|
|
80
|
+
case "list":
|
|
81
|
+
await listConfig();
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function configurePlugins() {
|
|
87
|
+
const prompts = await import("prompts");
|
|
88
|
+
const pluginsDir = join(process.cwd(), "src/server/plugins");
|
|
89
|
+
|
|
90
|
+
if (!existsSync(pluginsDir)) {
|
|
91
|
+
console.log(pc.yellow("No plugins directory found"));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// List installed plugins
|
|
96
|
+
const plugins = require("fs")
|
|
97
|
+
.readdirSync(pluginsDir)
|
|
98
|
+
.filter((f: string) =>
|
|
99
|
+
require("fs").statSync(join(pluginsDir, f)).isDirectory()
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (plugins.length === 0) {
|
|
103
|
+
console.log(pc.yellow("No plugins installed"));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const pluginName = await prompts.default({
|
|
108
|
+
type: "select",
|
|
109
|
+
name: "value",
|
|
110
|
+
message: "Select plugin to configure:",
|
|
111
|
+
choices: plugins.map((p: string) => ({ title: p, value: p })),
|
|
112
|
+
}).then((r: any) => r.value);
|
|
113
|
+
|
|
114
|
+
// Load plugin config schema
|
|
115
|
+
const pluginConfigPath = join(pluginsDir, pluginName, "config.ts");
|
|
116
|
+
if (!existsSync(pluginConfigPath)) {
|
|
117
|
+
console.log(pc.yellow(`Plugin ${pluginName} has no configurable options`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Interactive config editor
|
|
122
|
+
console.log(pc.cyan(`\nConfiguring ${pluginName}...\n`));
|
|
123
|
+
|
|
124
|
+
// Parse and edit config file
|
|
125
|
+
const configContent = readFileSync(pluginConfigPath, "utf-8");
|
|
126
|
+
console.log(pc.gray("Current config:"));
|
|
127
|
+
console.log(configContent);
|
|
128
|
+
|
|
129
|
+
// Would implement full config editing here
|
|
130
|
+
console.log(pc.yellow("Config editing coming soon!"));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function configureDeployment() {
|
|
134
|
+
const prompts = await import("prompts");
|
|
135
|
+
|
|
136
|
+
console.log(pc.cyan(pc.bold("\nš Deployment Configuration\n")));
|
|
137
|
+
|
|
138
|
+
const platform = await prompts.default({
|
|
139
|
+
type: "select",
|
|
140
|
+
name: "value",
|
|
141
|
+
message: "Select platform:",
|
|
142
|
+
choices: [
|
|
143
|
+
{ title: "Vercel", value: "vercel" },
|
|
144
|
+
{ title: "Cloudflare Workers", value: "cloudflare" },
|
|
145
|
+
{ title: "AWS Lambda", value: "aws" },
|
|
146
|
+
{ title: "VPS (Docker)", value: "docker" },
|
|
147
|
+
],
|
|
148
|
+
}).then((r: any) => r.value);
|
|
149
|
+
|
|
150
|
+
switch (platform) {
|
|
151
|
+
case "vercel":
|
|
152
|
+
await configureVercel();
|
|
153
|
+
break;
|
|
154
|
+
case "cloudflare":
|
|
155
|
+
await configureCloudflare();
|
|
156
|
+
break;
|
|
157
|
+
case "aws":
|
|
158
|
+
await configureAWS();
|
|
159
|
+
break;
|
|
160
|
+
case "docker":
|
|
161
|
+
await configureDocker();
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function configureVercel() {
|
|
167
|
+
const prompts = await import("prompts");
|
|
168
|
+
|
|
169
|
+
console.log(pc.cyan("\nConfiguring Vercel deployment...\n"));
|
|
170
|
+
|
|
171
|
+
const settings = await prompts.default([
|
|
172
|
+
{
|
|
173
|
+
type: "confirm",
|
|
174
|
+
name: "enableAnalytics",
|
|
175
|
+
message: "Enable Vercel Analytics?",
|
|
176
|
+
initial: false,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
type: "confirm",
|
|
180
|
+
name: "enableSpeedInsights",
|
|
181
|
+
message: "Enable Speed Insights?",
|
|
182
|
+
initial: false,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: "select",
|
|
186
|
+
name: "region",
|
|
187
|
+
message: "Deployment region:",
|
|
188
|
+
choices: [
|
|
189
|
+
{ title: "Auto (default)", value: "auto" },
|
|
190
|
+
{ title: "US East", value: "iad1" },
|
|
191
|
+
{ title: "US West", value: "sfo1" },
|
|
192
|
+
{ title: "EU West", value: "fra1" },
|
|
193
|
+
{ title: "AP East", value: "hkg1" },
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
// Update vercel.json
|
|
199
|
+
const vercelConfig = {
|
|
200
|
+
version: 2,
|
|
201
|
+
builds: [
|
|
202
|
+
{
|
|
203
|
+
src: "api/index.ts",
|
|
204
|
+
use: "@vercel/node",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
regions: settings.region === "auto" ? undefined : [settings.region],
|
|
208
|
+
analytics: settings.enableAnalytics,
|
|
209
|
+
speedInsights: settings.enableSpeedInsights,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
writeFileSync(
|
|
213
|
+
join(process.cwd(), "vercel.json"),
|
|
214
|
+
JSON.stringify(vercelConfig, null, 2)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
console.log(pc.green("ā
Vercel configuration updated"));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function configureCloudflare() {
|
|
221
|
+
const prompts = await import("prompts");
|
|
222
|
+
|
|
223
|
+
console.log(pc.cyan("\nConfiguring Cloudflare Workers...\n"));
|
|
224
|
+
|
|
225
|
+
const settings = await prompts.default([
|
|
226
|
+
{
|
|
227
|
+
type: "text",
|
|
228
|
+
name: "name",
|
|
229
|
+
message: "Worker name:",
|
|
230
|
+
initial: "my-app",
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: "select",
|
|
234
|
+
name: "usageModel",
|
|
235
|
+
message: "Usage model:",
|
|
236
|
+
choices: [
|
|
237
|
+
{ title: "Bundled (default)", value: "bundled" },
|
|
238
|
+
{ title: "Unbound", value: "unbound" },
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
type: "confirm",
|
|
243
|
+
name: "enableAnalytics",
|
|
244
|
+
message: "Enable Cloudflare Analytics?",
|
|
245
|
+
initial: true,
|
|
246
|
+
},
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
// Update wrangler.toml
|
|
250
|
+
const wranglerConfig = `name = "${settings.name}"
|
|
251
|
+
main = "src/index.ts"
|
|
252
|
+
compatibility_date = "2024-01-01"
|
|
253
|
+
usage_model = "${settings.usageModel}"
|
|
254
|
+
analytics_engine_datasets = [{ binding = "ANALYTICS", dataset = "${settings.name}_analytics" }]
|
|
255
|
+
|
|
256
|
+
[env.production]
|
|
257
|
+
vars = { ENVIRONMENT = "production" }
|
|
258
|
+
|
|
259
|
+
[env.staging]
|
|
260
|
+
vars = { ENVIRONMENT = "staging" }
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
writeFileSync(join(process.cwd(), "wrangler.toml"), wranglerConfig);
|
|
264
|
+
console.log(pc.green("ā
Cloudflare Workers configuration updated"));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function configureAWS() {
|
|
268
|
+
const prompts = await import("prompts");
|
|
269
|
+
|
|
270
|
+
console.log(pc.cyan("\nConfiguring AWS Lambda...\n"));
|
|
271
|
+
|
|
272
|
+
const settings = await prompts.default([
|
|
273
|
+
{
|
|
274
|
+
type: "text",
|
|
275
|
+
name: "stackName",
|
|
276
|
+
message: "CloudFormation stack name:",
|
|
277
|
+
initial: "my-app",
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
type: "select",
|
|
281
|
+
name: "region",
|
|
282
|
+
message: "AWS Region:",
|
|
283
|
+
choices: [
|
|
284
|
+
{ title: "us-east-1 (N. Virginia)", value: "us-east-1" },
|
|
285
|
+
{ title: "us-west-2 (Oregon)", value: "us-west-2" },
|
|
286
|
+
{ title: "eu-west-1 (Ireland)", value: "eu-west-1" },
|
|
287
|
+
{ title: "ap-southeast-1 (Singapore)", value: "ap-southeast-1" },
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
type: "number",
|
|
292
|
+
name: "memorySize",
|
|
293
|
+
message: "Lambda memory (MB):",
|
|
294
|
+
initial: 512,
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
type: "number",
|
|
298
|
+
name: "timeout",
|
|
299
|
+
message: "Lambda timeout (seconds):",
|
|
300
|
+
initial: 30,
|
|
301
|
+
},
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
// Create SAM template
|
|
305
|
+
const samTemplate = {
|
|
306
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
307
|
+
Transform: "AWS::Serverless-2016-10-31",
|
|
308
|
+
Description: `${settings.stackName} API`,
|
|
309
|
+
Globals: {
|
|
310
|
+
Function: {
|
|
311
|
+
Timeout: settings.timeout,
|
|
312
|
+
MemorySize: settings.memorySize,
|
|
313
|
+
Runtime: "nodejs20.x",
|
|
314
|
+
Architectures: ["arm64"],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
Resources: {
|
|
318
|
+
ApiFunction: {
|
|
319
|
+
Type: "AWS::Serverless::Function",
|
|
320
|
+
Properties: {
|
|
321
|
+
FunctionName: settings.stackName,
|
|
322
|
+
Handler: "dist/index.handler",
|
|
323
|
+
CodeUri: "./",
|
|
324
|
+
Events: {
|
|
325
|
+
ApiEvent: {
|
|
326
|
+
Type: "Api",
|
|
327
|
+
Properties: {
|
|
328
|
+
Path: "/{proxy+}",
|
|
329
|
+
Method: "ANY",
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
Outputs: {
|
|
337
|
+
ApiUrl: {
|
|
338
|
+
Description: "API Gateway endpoint URL",
|
|
339
|
+
Value: { "Fn::Sub": "https://\${ServerlessRestApi}.execute-api.\${AWS::Region}.amazonaws.com/Prod/" },
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
writeFileSync(
|
|
345
|
+
join(process.cwd(), "template.yaml"),
|
|
346
|
+
JSON.stringify(samTemplate, null, 2)
|
|
347
|
+
);
|
|
348
|
+
console.log(pc.green("ā
AWS SAM template created"));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function configureDocker() {
|
|
352
|
+
const prompts = await import("prompts");
|
|
353
|
+
|
|
354
|
+
console.log(pc.cyan("\nConfiguring Docker deployment...\n"));
|
|
355
|
+
|
|
356
|
+
const settings = await prompts.default([
|
|
357
|
+
{
|
|
358
|
+
type: "confirm",
|
|
359
|
+
name: "useNginx",
|
|
360
|
+
message: "Include Nginx reverse proxy?",
|
|
361
|
+
initial: true,
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
type: "confirm",
|
|
365
|
+
name: "enableSSL",
|
|
366
|
+
message: "Enable SSL/Let's Encrypt?",
|
|
367
|
+
initial: true,
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
type: "confirm",
|
|
371
|
+
name: "enableWatchtower",
|
|
372
|
+
message: "Enable Watchtower for auto-updates?",
|
|
373
|
+
initial: true,
|
|
374
|
+
},
|
|
375
|
+
]);
|
|
376
|
+
|
|
377
|
+
console.log(pc.green("ā
Docker configuration updated"));
|
|
378
|
+
console.log(pc.gray("Run 'docker-compose up -d' to deploy"));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function configureDatabase() {
|
|
382
|
+
const prompts = await import("prompts");
|
|
383
|
+
|
|
384
|
+
console.log(pc.cyan(pc.bold("\nšļø Database Configuration\n")));
|
|
385
|
+
|
|
386
|
+
const dbType = await prompts.default({
|
|
387
|
+
type: "select",
|
|
388
|
+
name: "value",
|
|
389
|
+
message: "Database type:",
|
|
390
|
+
choices: [
|
|
391
|
+
{ title: "SQLite", value: "sqlite" },
|
|
392
|
+
{ title: "PostgreSQL", value: "postgres" },
|
|
393
|
+
{ title: "MySQL", value: "mysql" },
|
|
394
|
+
],
|
|
395
|
+
}).then((r: any) => r.value);
|
|
396
|
+
|
|
397
|
+
const connectionString = await prompts.default({
|
|
398
|
+
type: "text",
|
|
399
|
+
name: "value",
|
|
400
|
+
message: "Database connection string:",
|
|
401
|
+
initial:
|
|
402
|
+
dbType === "sqlite"
|
|
403
|
+
? "./data/app.db"
|
|
404
|
+
: dbType === "postgres"
|
|
405
|
+
? "postgresql://user:pass@localhost:5432/app"
|
|
406
|
+
: "mysql://user:pass@localhost:3306/app",
|
|
407
|
+
}).then((r: any) => r.value);
|
|
408
|
+
|
|
409
|
+
// Update .env
|
|
410
|
+
const envPath = join(process.cwd(), ".env");
|
|
411
|
+
let envContent = "";
|
|
412
|
+
|
|
413
|
+
if (existsSync(envPath)) {
|
|
414
|
+
envContent = readFileSync(envPath, "utf-8");
|
|
415
|
+
// Replace or add DATABASE_URL
|
|
416
|
+
if (envContent.includes("DATABASE_URL=")) {
|
|
417
|
+
envContent = envContent.replace(
|
|
418
|
+
/DATABASE_URL=.*/,
|
|
419
|
+
`DATABASE_URL=${connectionString}`
|
|
420
|
+
);
|
|
421
|
+
} else {
|
|
422
|
+
envContent += `\nDATABASE_URL=${connectionString}\n`;
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
envContent = `DATABASE_URL=${connectionString}\n`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
writeFileSync(envPath, envContent);
|
|
429
|
+
console.log(pc.green("ā
Database configuration updated in .env"));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async function configureEnv() {
|
|
433
|
+
const prompts = await import("prompts");
|
|
434
|
+
|
|
435
|
+
console.log(pc.cyan(pc.bold("\nš Environment Variables\n")));
|
|
436
|
+
|
|
437
|
+
const envPath = join(process.cwd(), ".env");
|
|
438
|
+
let envVars: Record<string, string> = {};
|
|
439
|
+
|
|
440
|
+
if (existsSync(envPath)) {
|
|
441
|
+
const content = readFileSync(envPath, "utf-8");
|
|
442
|
+
for (const line of content.split("\n")) {
|
|
443
|
+
const match = line.match(/^([A-Z_]+)=(.*)$/);
|
|
444
|
+
if (match) {
|
|
445
|
+
envVars[match[1]] = match[2];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const action = await prompts.default({
|
|
451
|
+
type: "select",
|
|
452
|
+
name: "value",
|
|
453
|
+
message: "Action:",
|
|
454
|
+
choices: [
|
|
455
|
+
{ title: "Add new variable", value: "add" },
|
|
456
|
+
{ title: "Edit existing", value: "edit" },
|
|
457
|
+
{ title: "Delete variable", value: "delete" },
|
|
458
|
+
{ title: "View all", value: "view" },
|
|
459
|
+
],
|
|
460
|
+
}).then((r: any) => r.value);
|
|
461
|
+
|
|
462
|
+
switch (action) {
|
|
463
|
+
case "add":
|
|
464
|
+
const newKey = await prompts.default({
|
|
465
|
+
type: "text",
|
|
466
|
+
name: "value",
|
|
467
|
+
message: "Variable name:",
|
|
468
|
+
validate: (v: string) => /^[A-Z_]+$/.test(v) || "Use UPPER_CASE with underscores",
|
|
469
|
+
}).then((r: any) => r.value);
|
|
470
|
+
|
|
471
|
+
const newValue = await prompts.default({
|
|
472
|
+
type: "text",
|
|
473
|
+
name: "value",
|
|
474
|
+
message: "Value:",
|
|
475
|
+
}).then((r: any) => r.value);
|
|
476
|
+
|
|
477
|
+
envVars[newKey] = newValue;
|
|
478
|
+
break;
|
|
479
|
+
|
|
480
|
+
case "edit":
|
|
481
|
+
const editKey = await prompts.default({
|
|
482
|
+
type: "select",
|
|
483
|
+
name: "value",
|
|
484
|
+
message: "Select variable to edit:",
|
|
485
|
+
choices: Object.keys(envVars).map((k) => ({ title: k, value: k })),
|
|
486
|
+
}).then((r: any) => r.value);
|
|
487
|
+
|
|
488
|
+
const editValue = await prompts.default({
|
|
489
|
+
type: "text",
|
|
490
|
+
name: "value",
|
|
491
|
+
message: "New value:",
|
|
492
|
+
initial: envVars[editKey],
|
|
493
|
+
}).then((r: any) => r.value);
|
|
494
|
+
|
|
495
|
+
envVars[editKey] = editValue;
|
|
496
|
+
break;
|
|
497
|
+
|
|
498
|
+
case "delete":
|
|
499
|
+
const deleteKey = await prompts.default({
|
|
500
|
+
type: "select",
|
|
501
|
+
name: "value",
|
|
502
|
+
message: "Select variable to delete:",
|
|
503
|
+
choices: Object.keys(envVars).map((k) => ({ title: k, value: k })),
|
|
504
|
+
}).then((r: any) => r.value);
|
|
505
|
+
|
|
506
|
+
delete envVars[deleteKey];
|
|
507
|
+
break;
|
|
508
|
+
|
|
509
|
+
case "view":
|
|
510
|
+
console.log(pc.cyan("\nCurrent environment variables:\n"));
|
|
511
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
512
|
+
const maskedValue = key.includes("SECRET") || key.includes("PASSWORD")
|
|
513
|
+
? "***"
|
|
514
|
+
: value;
|
|
515
|
+
console.log(` ${key}=${maskedValue}`);
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Save changes
|
|
521
|
+
const newContent = Object.entries(envVars)
|
|
522
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
523
|
+
.join("\n");
|
|
524
|
+
writeFileSync(envPath, newContent + "\n");
|
|
525
|
+
console.log(pc.green("ā
Environment variables updated"));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
async function listConfig() {
|
|
529
|
+
console.log(pc.cyan(pc.bold("\nš Current Configuration\n")));
|
|
530
|
+
|
|
531
|
+
// Show deployment config
|
|
532
|
+
const hasVercel = existsSync(join(process.cwd(), "vercel.json"));
|
|
533
|
+
const hasCloudflare = existsSync(join(process.cwd(), "wrangler.toml"));
|
|
534
|
+
const hasAWS = existsSync(join(process.cwd(), "template.yaml"));
|
|
535
|
+
const hasDocker = existsSync(join(process.cwd(), "Dockerfile"));
|
|
536
|
+
|
|
537
|
+
console.log(pc.bold("Deployment:"));
|
|
538
|
+
if (hasVercel) console.log(` ${pc.green("ā")} Vercel`);
|
|
539
|
+
if (hasCloudflare) console.log(` ${pc.green("ā")} Cloudflare Workers`);
|
|
540
|
+
if (hasAWS) console.log(` ${pc.green("ā")} AWS Lambda`);
|
|
541
|
+
if (hasDocker) console.log(` ${pc.green("ā")} Docker`);
|
|
542
|
+
|
|
543
|
+
// Show plugins
|
|
544
|
+
const pluginsDir = join(process.cwd(), "src/server/plugins");
|
|
545
|
+
if (existsSync(pluginsDir)) {
|
|
546
|
+
const plugins = require("fs")
|
|
547
|
+
.readdirSync(pluginsDir)
|
|
548
|
+
.filter((f: string) =>
|
|
549
|
+
require("fs").statSync(join(pluginsDir, f)).isDirectory()
|
|
550
|
+
);
|
|
551
|
+
console.log(pc.bold("\nPlugins:") + ` ${plugins.join(", ")}`);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Show env
|
|
555
|
+
const envPath = join(process.cwd(), ".env");
|
|
556
|
+
if (existsSync(envPath)) {
|
|
557
|
+
console.log(pc.bold("\nEnvironment:"));
|
|
558
|
+
const content = readFileSync(envPath, "utf-8");
|
|
559
|
+
const dbMatch = content.match(/DATABASE_URL=(.+)/);
|
|
560
|
+
if (dbMatch) {
|
|
561
|
+
console.log(` Database: ${dbMatch[1].split("://")[0]}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async function getConfigValue(key: string) {
|
|
567
|
+
if (!key) {
|
|
568
|
+
console.log(pc.red("Usage: donkeylabs config get <key>"));
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const envPath = join(process.cwd(), ".env");
|
|
573
|
+
if (!existsSync(envPath)) {
|
|
574
|
+
console.log(pc.yellow("No .env file found"));
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const content = readFileSync(envPath, "utf-8");
|
|
579
|
+
const match = content.match(new RegExp(`^${key}=(.+)$`, "m"));
|
|
580
|
+
|
|
581
|
+
if (match) {
|
|
582
|
+
console.log(`${key}=${match[1]}`);
|
|
583
|
+
} else {
|
|
584
|
+
console.log(pc.yellow(`Key '${key}' not found`));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async function setConfigValue(key: string, value: string) {
|
|
589
|
+
if (!key || !value) {
|
|
590
|
+
console.log(pc.red("Usage: donkeylabs config set <key> <value>"));
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const envPath = join(process.cwd(), ".env");
|
|
595
|
+
let content = "";
|
|
596
|
+
|
|
597
|
+
if (existsSync(envPath)) {
|
|
598
|
+
content = readFileSync(envPath, "utf-8");
|
|
599
|
+
if (content.includes(`${key}=`)) {
|
|
600
|
+
content = content.replace(new RegExp(`${key}=.+`), `${key}=${value}`);
|
|
601
|
+
} else {
|
|
602
|
+
content += `\n${key}=${value}\n`;
|
|
603
|
+
}
|
|
604
|
+
} else {
|
|
605
|
+
content = `${key}=${value}\n`;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
writeFileSync(envPath, content);
|
|
609
|
+
console.log(pc.green(`ā
Set ${key}=${value}`));
|
|
610
|
+
}
|