create-sprint 0.0.2 ā 0.0.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/bin/cli.js +489 -92
- package/bin/cli.ts +637 -219
- package/package.json +35 -35
package/bin/cli.js
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { select, input } from "@inquirer/prompts";
|
|
2
|
+
import { select, input, confirm } from "@inquirer/prompts";
|
|
3
3
|
import { mkdir, writeFile } from "fs/promises";
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
5
|
import { join } from "path";
|
|
6
|
+
import { execSync } from "child_process";
|
|
6
7
|
const args = process.argv.slice(2);
|
|
7
|
-
const
|
|
8
|
-
const isInitCommand = initIndex !== -1;
|
|
9
|
-
if (!isInitCommand) {
|
|
10
|
-
console.log("\nš Sprint - Quickly API Framework\n");
|
|
11
|
-
console.log("Usage: sprint init [options]");
|
|
12
|
-
console.log("\nOptions:");
|
|
13
|
-
console.log(" --ts, --typescript Create TypeScript project");
|
|
14
|
-
console.log(" --js, --javascript Create JavaScript project");
|
|
15
|
-
console.log(" --name <name> Project name");
|
|
16
|
-
console.log(" --current Create in current directory");
|
|
17
|
-
console.log("\nExamples:");
|
|
18
|
-
console.log(" sprint init Interactive mode");
|
|
19
|
-
console.log(" sprint init --ts Create TypeScript project");
|
|
20
|
-
console.log(" sprint init --ts --name my-api");
|
|
21
|
-
console.log(" sprint init --js --current\n");
|
|
22
|
-
process.exit(0);
|
|
23
|
-
}
|
|
8
|
+
const hasHelp = args.includes("--help") || args.includes("-h");
|
|
24
9
|
const hasTs = args.includes("--ts") || args.includes("--typescript");
|
|
25
10
|
const hasJs = args.includes("--js") || args.includes("--javascript");
|
|
26
11
|
const hasName = args.indexOf("--name");
|
|
27
12
|
const projectNameArg = hasName !== -1 ? args[hasName + 1] : null;
|
|
28
13
|
const useCurrentDirArg = args.includes("--current");
|
|
14
|
+
const skipInstallArg = args.includes("--no-install");
|
|
15
|
+
const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
|
|
16
|
+
const useDockerArg = args.includes("--docker");
|
|
17
|
+
if (hasHelp) {
|
|
18
|
+
console.log("\nš Sprint - Quickly API Framework\n");
|
|
19
|
+
console.log("Usage: sprint [options]");
|
|
20
|
+
console.log("\nOptions:");
|
|
21
|
+
console.log(" --ts, --typescript Create TypeScript project");
|
|
22
|
+
console.log(" --js, --javascript Create JavaScript project");
|
|
23
|
+
console.log(" --name <name> Project name (use '.' for current directory)");
|
|
24
|
+
console.log(" --no-install Skip automatic dependency installation");
|
|
25
|
+
console.log(" --telemetry <type> Telemetry: none, sentry, glitchtip, discord");
|
|
26
|
+
console.log(" --docker Add Docker support");
|
|
27
|
+
console.log(" --help, -h Show this help message");
|
|
28
|
+
console.log("\nExamples:");
|
|
29
|
+
console.log(" sprint Interactive mode");
|
|
30
|
+
console.log(" sprint --ts Create TypeScript project");
|
|
31
|
+
console.log(" sprint --ts --name my-api");
|
|
32
|
+
console.log(" sprint --js --name . --telemetry sentry --docker\n");
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
29
35
|
async function main() {
|
|
30
36
|
console.log("\nš Welcome to Sprint - Quickly API Framework\n");
|
|
31
37
|
let projectName;
|
|
@@ -51,29 +57,33 @@ async function main() {
|
|
|
51
57
|
console.log(`\nā
Creating Sprint project: ${projectName === "." ? "current directory" : projectName} with ${language === "typescript" ? "TypeScript" : "JavaScript"}\n`);
|
|
52
58
|
await createProject(projectName, language);
|
|
53
59
|
console.log("\nā
Project created successfully!");
|
|
60
|
+
let installDeps = true;
|
|
61
|
+
if (!skipInstallArg) {
|
|
62
|
+
installDeps = await confirm({
|
|
63
|
+
message: "Do you want to install dependencies now?",
|
|
64
|
+
default: true,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (installDeps) {
|
|
68
|
+
console.log("\nš¦ Installing dependencies...\n");
|
|
69
|
+
const targetDir = projectName === "." ? process.cwd() : join(process.cwd(), projectName);
|
|
70
|
+
try {
|
|
71
|
+
execSync("npm install", { cwd: targetDir, stdio: "inherit" });
|
|
72
|
+
console.log("\nā
Dependencies installed successfully!");
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error("\nā Error installing dependencies. Please run 'npm install' manually.");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
54
78
|
console.log("\nš¦ Next steps:");
|
|
55
79
|
const cdCmd = projectName === "." ? "" : `cd ${projectName} && `;
|
|
56
|
-
|
|
80
|
+
if (!installDeps) {
|
|
81
|
+
console.log(` ${cdCmd}npm install`);
|
|
82
|
+
}
|
|
57
83
|
console.log(` ${cdCmd}npm run dev`);
|
|
58
84
|
console.log("\n");
|
|
59
85
|
}
|
|
60
86
|
async function getProjectName() {
|
|
61
|
-
const useCurrentDir = await select({
|
|
62
|
-
message: "Where would you like to create your project?",
|
|
63
|
-
choices: [
|
|
64
|
-
{
|
|
65
|
-
name: "Current directory",
|
|
66
|
-
value: "current",
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "New directory",
|
|
70
|
-
value: "new",
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
});
|
|
74
|
-
if (useCurrentDir === "current") {
|
|
75
|
-
return ".";
|
|
76
|
-
}
|
|
77
87
|
const name = await input({
|
|
78
88
|
message: "Enter project name:",
|
|
79
89
|
validate: (value) => {
|
|
@@ -102,6 +112,34 @@ async function selectLanguage() {
|
|
|
102
112
|
});
|
|
103
113
|
return language;
|
|
104
114
|
}
|
|
115
|
+
async function selectTelemetry() {
|
|
116
|
+
const telemetry = await select({
|
|
117
|
+
message: "Select error tracking/telemetry solution:",
|
|
118
|
+
choices: [
|
|
119
|
+
{
|
|
120
|
+
name: "None",
|
|
121
|
+
value: "none",
|
|
122
|
+
description: "No error tracking integration",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "Sentry",
|
|
126
|
+
value: "sentry",
|
|
127
|
+
description: "Full-featured error tracking (free tier available)",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "GlitchTip",
|
|
131
|
+
value: "glitchtip",
|
|
132
|
+
description: "Simple error tracking, can be self-hosted",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "Discord Webhook",
|
|
136
|
+
value: "discord",
|
|
137
|
+
description: "Send error notifications to Discord channel",
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
return telemetry;
|
|
142
|
+
}
|
|
105
143
|
async function createProject(projectName, language) {
|
|
106
144
|
const isCurrentDir = projectName === ".";
|
|
107
145
|
const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
|
|
@@ -112,44 +150,104 @@ async function createProject(projectName, language) {
|
|
|
112
150
|
if (!isCurrentDir) {
|
|
113
151
|
await mkdir(targetDir, { recursive: true });
|
|
114
152
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
153
|
+
let telemetry = "none";
|
|
154
|
+
if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) {
|
|
155
|
+
telemetry = telemetryArg;
|
|
156
|
+
}
|
|
157
|
+
else if (!hasTs && !hasJs) {
|
|
158
|
+
telemetry = await selectTelemetry();
|
|
159
|
+
}
|
|
160
|
+
let useDocker = useDockerArg;
|
|
161
|
+
if (!useDocker && !hasTs && !hasJs) {
|
|
162
|
+
useDocker = await confirm({
|
|
163
|
+
message: "Do you want to add Docker support?",
|
|
164
|
+
default: false,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
let pkgJson;
|
|
168
|
+
if (language === "typescript") {
|
|
169
|
+
pkgJson = getTypeScriptPackageJson(projectName, telemetry);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
pkgJson = getJavaScriptPackageJson(projectName, telemetry);
|
|
173
|
+
}
|
|
118
174
|
await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
|
|
119
175
|
if (language === "typescript") {
|
|
120
176
|
await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
|
|
177
|
+
await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
|
|
121
178
|
}
|
|
122
179
|
const srcDir = join(targetDir, "src");
|
|
123
180
|
await mkdir(srcDir, { recursive: true });
|
|
124
|
-
await
|
|
181
|
+
await mkdir(join(srcDir, "middlewares"), { recursive: true });
|
|
182
|
+
await mkdir(join(srcDir, "routes"), { recursive: true });
|
|
183
|
+
await mkdir(join(srcDir, "controllers"), { recursive: true });
|
|
184
|
+
await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
|
|
185
|
+
await writeFile(join(srcDir, "controllers", ".gitkeep"), "");
|
|
186
|
+
await writeFile(join(srcDir, "index." + (language === "typescript" ? "ts" : "js")), getMainFile(language, telemetry));
|
|
187
|
+
await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
|
|
125
188
|
if (language === "typescript") {
|
|
126
|
-
await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getAppFile(language));
|
|
189
|
+
await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getAppFile(language, telemetry));
|
|
190
|
+
}
|
|
191
|
+
await writeFile(join(targetDir, ".env.example"), getEnvExample(telemetry));
|
|
192
|
+
await writeFile(join(targetDir, ".env"), "");
|
|
193
|
+
if (telemetry !== "none") {
|
|
194
|
+
await writeFile(join(targetDir, "sprint.config.js"), getSprintConfig(telemetry));
|
|
195
|
+
}
|
|
196
|
+
await writeFile(join(targetDir, ".gitignore"), getGitignore(language));
|
|
197
|
+
if (useDocker) {
|
|
198
|
+
await writeFile(join(targetDir, "Dockerfile"), getDockerfile(language));
|
|
199
|
+
await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(language));
|
|
200
|
+
await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
|
|
127
201
|
}
|
|
128
|
-
await writeFile(join(targetDir, ".env.example"), getEnvExample());
|
|
129
202
|
}
|
|
130
|
-
function getTypeScriptPackageJson(name) {
|
|
203
|
+
function getTypeScriptPackageJson(name, telemetry) {
|
|
204
|
+
const deps = {
|
|
205
|
+
"sprint-es": "^0.0.24",
|
|
206
|
+
dotenv: "^17.0.0",
|
|
207
|
+
};
|
|
208
|
+
const devDeps = {
|
|
209
|
+
"@types/node": "^22.0.0",
|
|
210
|
+
"tsx": "^4.19.0",
|
|
211
|
+
typescript: "^5.6.0",
|
|
212
|
+
vite: "^6.0.0",
|
|
213
|
+
};
|
|
214
|
+
if (telemetry === "sentry") {
|
|
215
|
+
deps["@sentry/node"] = "^8.0.0";
|
|
216
|
+
}
|
|
217
|
+
else if (telemetry === "glitchtip") {
|
|
218
|
+
deps["@sentry/node"] = "^8.0.0";
|
|
219
|
+
}
|
|
220
|
+
else if (telemetry === "discord") {
|
|
221
|
+
deps["axios"] = "^1.6.0";
|
|
222
|
+
}
|
|
131
223
|
return {
|
|
132
224
|
name: name === "." ? "sprint-app" : name,
|
|
133
225
|
version: "0.0.1",
|
|
134
226
|
description: "Sprint API",
|
|
135
227
|
main: "dist/index.js",
|
|
136
228
|
scripts: {
|
|
137
|
-
build: "
|
|
138
|
-
start: "node dist/index.js",
|
|
139
|
-
dev: "
|
|
140
|
-
},
|
|
141
|
-
dependencies: {
|
|
142
|
-
"sprint-es": "^0.0.24",
|
|
143
|
-
dotenv: "^17.0.0",
|
|
144
|
-
},
|
|
145
|
-
devDependencies: {
|
|
146
|
-
"@types/node": "^22.0.0",
|
|
147
|
-
"tsx": "^4.19.0",
|
|
148
|
-
typescript: "^5.6.0",
|
|
229
|
+
build: "vite build",
|
|
230
|
+
start: "node dist/index.js --prod",
|
|
231
|
+
dev: "vite --dev",
|
|
149
232
|
},
|
|
233
|
+
dependencies: deps,
|
|
234
|
+
devDependencies: devDeps,
|
|
150
235
|
};
|
|
151
236
|
}
|
|
152
|
-
function getJavaScriptPackageJson(name) {
|
|
237
|
+
function getJavaScriptPackageJson(name, telemetry) {
|
|
238
|
+
const deps = {
|
|
239
|
+
"sprint-es": "^0.0.24",
|
|
240
|
+
dotenv: "^17.0.0",
|
|
241
|
+
};
|
|
242
|
+
if (telemetry === "sentry") {
|
|
243
|
+
deps["@sentry/node"] = "^8.0.0";
|
|
244
|
+
}
|
|
245
|
+
else if (telemetry === "glitchtip") {
|
|
246
|
+
deps["@sentry/node"] = "^8.0.0";
|
|
247
|
+
}
|
|
248
|
+
else if (telemetry === "discord") {
|
|
249
|
+
deps["axios"] = "^1.6.0";
|
|
250
|
+
}
|
|
153
251
|
return {
|
|
154
252
|
name: name === "." ? "sprint-app" : name,
|
|
155
253
|
version: "0.0.1",
|
|
@@ -157,13 +255,10 @@ function getJavaScriptPackageJson(name) {
|
|
|
157
255
|
main: "src/index.js",
|
|
158
256
|
type: "module",
|
|
159
257
|
scripts: {
|
|
160
|
-
start: "node src/index.js",
|
|
161
|
-
dev: "node --watch src/index.js",
|
|
162
|
-
},
|
|
163
|
-
dependencies: {
|
|
164
|
-
"sprint-es": "^0.0.24",
|
|
165
|
-
dotenv: "^17.0.0",
|
|
258
|
+
start: "node src/index.js --prod",
|
|
259
|
+
dev: "node --watch src/index.js --dev",
|
|
166
260
|
},
|
|
261
|
+
dependencies: deps,
|
|
167
262
|
};
|
|
168
263
|
}
|
|
169
264
|
function getTsConfig() {
|
|
@@ -183,71 +278,373 @@ function getTsConfig() {
|
|
|
183
278
|
declaration: true,
|
|
184
279
|
declarationMap: true,
|
|
185
280
|
sourceMap: true,
|
|
281
|
+
tabWidth: 4,
|
|
186
282
|
},
|
|
187
283
|
include: ["src/**/*"],
|
|
188
284
|
exclude: ["node_modules", "dist"],
|
|
189
285
|
}, null, 2);
|
|
190
286
|
}
|
|
191
|
-
function
|
|
287
|
+
function getViteConfig() {
|
|
288
|
+
return `import { defineConfig } from "vite";
|
|
289
|
+
import { resolve } from "path";
|
|
290
|
+
|
|
291
|
+
export default defineConfig({
|
|
292
|
+
build: {
|
|
293
|
+
lib: {
|
|
294
|
+
entry: resolve(__dirname, "src/index.ts"),
|
|
295
|
+
formats: ["es"],
|
|
296
|
+
fileName: "index",
|
|
297
|
+
},
|
|
298
|
+
outDir: "dist",
|
|
299
|
+
rollupOptions: {
|
|
300
|
+
external: ["sprint-es", "express", "cors", "morgan", "serve-favicon", "dotenv"],
|
|
301
|
+
},
|
|
302
|
+
target: "ES2020",
|
|
303
|
+
},
|
|
304
|
+
resolve: {
|
|
305
|
+
alias: {
|
|
306
|
+
"@": resolve(__dirname, "src"),
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
`;
|
|
311
|
+
}
|
|
312
|
+
function getMainFile(language, telemetry) {
|
|
313
|
+
const hasTelemetry = telemetry !== "none";
|
|
192
314
|
if (language === "typescript") {
|
|
193
|
-
|
|
194
|
-
import dotenv from
|
|
315
|
+
let code = `import Sprint from "sprint-es";
|
|
316
|
+
import dotenv from "dotenv";
|
|
317
|
+
import homeRouter from "./routes/home";
|
|
195
318
|
|
|
196
319
|
dotenv.config();
|
|
197
320
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
321
|
+
`;
|
|
322
|
+
if (hasTelemetry) {
|
|
323
|
+
code += getTelemetryImport(language, telemetry);
|
|
324
|
+
}
|
|
325
|
+
code += `const app = new Sprint({
|
|
326
|
+
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
|
|
327
|
+
favicon: process.env.FAVICON || undefined,
|
|
328
|
+
bodyParser: {
|
|
329
|
+
limit: "10mb"
|
|
330
|
+
}
|
|
204
331
|
});
|
|
205
332
|
|
|
206
|
-
app.
|
|
207
|
-
res.send('Hello from Sprint!');
|
|
208
|
-
});
|
|
333
|
+
app.use(homeRouter);
|
|
209
334
|
|
|
210
335
|
app.listen();
|
|
211
336
|
`;
|
|
337
|
+
return code;
|
|
212
338
|
}
|
|
213
|
-
|
|
214
|
-
import dotenv from
|
|
339
|
+
let code = `import Sprint from "sprint-es";
|
|
340
|
+
import dotenv from "dotenv";
|
|
341
|
+
import homeRouter from "./routes/home";
|
|
215
342
|
|
|
216
343
|
dotenv.config();
|
|
217
344
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
345
|
+
`;
|
|
346
|
+
if (hasTelemetry) {
|
|
347
|
+
code += getTelemetryImport(language, telemetry);
|
|
348
|
+
}
|
|
349
|
+
code += `const app = new Sprint({
|
|
350
|
+
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
|
|
351
|
+
favicon: process.env.FAVICON || undefined,
|
|
352
|
+
bodyParser: {
|
|
353
|
+
limit: "10mb"
|
|
354
|
+
}
|
|
224
355
|
});
|
|
225
356
|
|
|
226
|
-
app.
|
|
227
|
-
res.send('Hello from Sprint!');
|
|
228
|
-
});
|
|
357
|
+
app.use(homeRouter);
|
|
229
358
|
|
|
230
359
|
app.listen();
|
|
231
360
|
`;
|
|
361
|
+
return code;
|
|
362
|
+
}
|
|
363
|
+
function getTelemetryImport(language, telemetry) {
|
|
364
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
365
|
+
return `import * as Sentry from "@sentry/node";
|
|
366
|
+
|
|
367
|
+
Sentry.init({
|
|
368
|
+
dsn: process.env.SENTRY_DSN,
|
|
369
|
+
integrations: [
|
|
370
|
+
Sentry.httpIntegration(),
|
|
371
|
+
],
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
`;
|
|
375
|
+
}
|
|
376
|
+
else if (telemetry === "discord") {
|
|
377
|
+
return `import axios from "axios";
|
|
378
|
+
|
|
379
|
+
const discordWebhook = process.env.DISCORD_WEBHOOK_URL;
|
|
380
|
+
|
|
381
|
+
async function sendDiscordError(error, req) {
|
|
382
|
+
if (!discordWebhook) return;
|
|
383
|
+
|
|
384
|
+
const embed = {
|
|
385
|
+
title: "šØ Error in Sprint API",
|
|
386
|
+
description: \`\${error.message}\`,
|
|
387
|
+
color: 16711680,
|
|
388
|
+
fields: [
|
|
389
|
+
{
|
|
390
|
+
name: "Method",
|
|
391
|
+
value: req.method,
|
|
392
|
+
inline: true,
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: "URL",
|
|
396
|
+
value: req.url,
|
|
397
|
+
inline: true,
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: "Stack",
|
|
401
|
+
value: error.stack?.split("\\n").slice(0, 5).join("\\n") || "No stack trace",
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
timestamp: new Date().toISOString(),
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
await axios.post(discordWebhook, {
|
|
409
|
+
embeds: [embed],
|
|
410
|
+
});
|
|
411
|
+
} catch (e) {
|
|
412
|
+
console.error("Failed to send Discord webhook:", e.message);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
`;
|
|
417
|
+
}
|
|
418
|
+
return "";
|
|
232
419
|
}
|
|
233
|
-
function getAppFile(language) {
|
|
420
|
+
function getAppFile(language, telemetry) {
|
|
234
421
|
if (language === "typescript") {
|
|
235
|
-
return `import type { SprintOptions } from
|
|
422
|
+
return `import type { SprintOptions } from "sprint-es";
|
|
236
423
|
|
|
237
424
|
export const options: SprintOptions = {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
425
|
+
port: 3000,
|
|
426
|
+
bodyParser: {
|
|
427
|
+
limit: "10mb"
|
|
428
|
+
}
|
|
242
429
|
};
|
|
243
430
|
`;
|
|
244
431
|
}
|
|
245
432
|
return "";
|
|
246
433
|
}
|
|
247
|
-
function getEnvExample() {
|
|
248
|
-
|
|
434
|
+
function getEnvExample(telemetry) {
|
|
435
|
+
let env = `PORT=3000
|
|
249
436
|
FAVICON=./public/favicon.ico
|
|
250
437
|
NODE_ENV=development
|
|
438
|
+
`;
|
|
439
|
+
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
440
|
+
env += `
|
|
441
|
+
# Sentry / GlitchTip (use GlitchTip DSN for self-hosted)
|
|
442
|
+
SENTRY_DSN=
|
|
443
|
+
`;
|
|
444
|
+
}
|
|
445
|
+
else if (telemetry === "discord") {
|
|
446
|
+
env += `
|
|
447
|
+
# Discord Webhook URL for error notifications
|
|
448
|
+
DISCORD_WEBHOOK_URL=
|
|
449
|
+
`;
|
|
450
|
+
}
|
|
451
|
+
return env;
|
|
452
|
+
}
|
|
453
|
+
function getSprintConfig(telemetry) {
|
|
454
|
+
if (telemetry === "discord") {
|
|
455
|
+
return `export default {
|
|
456
|
+
errorHandler: async (error, req, res) => {
|
|
457
|
+
console.error(error);
|
|
458
|
+
|
|
459
|
+
if (process.env.DISCORD_WEBHOOK_URL && res.socket.server) {
|
|
460
|
+
const axios = await import("axios");
|
|
461
|
+
const embed = {
|
|
462
|
+
title: "šØ Error in Sprint API",
|
|
463
|
+
description: error.message,
|
|
464
|
+
color: 16711680,
|
|
465
|
+
fields: [
|
|
466
|
+
{
|
|
467
|
+
name: "Method",
|
|
468
|
+
value: req.method,
|
|
469
|
+
inline: true,
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: "URL",
|
|
473
|
+
value: req.url,
|
|
474
|
+
inline: true,
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: "Stack",
|
|
478
|
+
value: error.stack?.split("\\n").slice(0, 5).join("\\n") || "No stack",
|
|
479
|
+
},
|
|
480
|
+
],
|
|
481
|
+
timestamp: new Date().toISOString(),
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
await axios.default.post(process.env.DISCORD_WEBHOOK_URL, {
|
|
486
|
+
embeds: [embed],
|
|
487
|
+
});
|
|
488
|
+
} catch (e) {
|
|
489
|
+
console.error("Discord webhook error:", e.message);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
res.status(500).json({ error: "Internal server error" });
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
`;
|
|
497
|
+
}
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
function getHomeRoute(language) {
|
|
501
|
+
if (language === "typescript") {
|
|
502
|
+
return `import { Router } from "sprint-es";
|
|
503
|
+
|
|
504
|
+
const router = Router();
|
|
505
|
+
|
|
506
|
+
router.get("/", (req, res) => {
|
|
507
|
+
res.json({
|
|
508
|
+
message: "Hello World",
|
|
509
|
+
status: "ok"
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
export default router;
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
516
|
+
return `import { Router } from "sprint-es";
|
|
517
|
+
|
|
518
|
+
const router = Router();
|
|
519
|
+
|
|
520
|
+
router.get("/", (req, res) => {
|
|
521
|
+
res.json({
|
|
522
|
+
message: "Hello World",
|
|
523
|
+
status: "ok"
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
export default router;
|
|
528
|
+
`;
|
|
529
|
+
}
|
|
530
|
+
function getDockerfile(language) {
|
|
531
|
+
if (language === "typescript") {
|
|
532
|
+
return `FROM node:20-alpine
|
|
533
|
+
|
|
534
|
+
WORKDIR /app
|
|
535
|
+
|
|
536
|
+
COPY package*.json ./
|
|
537
|
+
|
|
538
|
+
RUN npm install
|
|
539
|
+
|
|
540
|
+
COPY . .
|
|
541
|
+
|
|
542
|
+
RUN npm run build
|
|
543
|
+
|
|
544
|
+
EXPOSE 3000
|
|
545
|
+
|
|
546
|
+
CMD ["npm", "start"]
|
|
547
|
+
`;
|
|
548
|
+
}
|
|
549
|
+
return `FROM node:20-alpine
|
|
550
|
+
|
|
551
|
+
WORKDIR /app
|
|
552
|
+
|
|
553
|
+
COPY package*.json ./
|
|
554
|
+
|
|
555
|
+
RUN npm install
|
|
556
|
+
|
|
557
|
+
COPY . .
|
|
558
|
+
|
|
559
|
+
EXPOSE 3000
|
|
560
|
+
|
|
561
|
+
CMD ["npm", "start"]
|
|
562
|
+
`;
|
|
563
|
+
}
|
|
564
|
+
function getDockerCompose(language) {
|
|
565
|
+
if (language === "typescript") {
|
|
566
|
+
return `version: "3.8"
|
|
567
|
+
|
|
568
|
+
services:
|
|
569
|
+
app:
|
|
570
|
+
build: .
|
|
571
|
+
ports:
|
|
572
|
+
- "3000:3000"
|
|
573
|
+
environment:
|
|
574
|
+
- NODE_ENV=production
|
|
575
|
+
- PORT=3000
|
|
576
|
+
restart: unless-stopped
|
|
577
|
+
`;
|
|
578
|
+
}
|
|
579
|
+
return `version: "3.8"
|
|
580
|
+
|
|
581
|
+
services:
|
|
582
|
+
app:
|
|
583
|
+
build: .
|
|
584
|
+
ports:
|
|
585
|
+
- "3000:3000"
|
|
586
|
+
environment:
|
|
587
|
+
- NODE_ENV=production
|
|
588
|
+
- PORT=3000
|
|
589
|
+
restart: unless-stopped
|
|
590
|
+
`;
|
|
591
|
+
}
|
|
592
|
+
function getGitignore(language) {
|
|
593
|
+
return `# Dependencies
|
|
594
|
+
node_modules/
|
|
595
|
+
npm-debug.log*
|
|
596
|
+
yarn-debug.log*
|
|
597
|
+
yarn-error.log*
|
|
598
|
+
|
|
599
|
+
# Build
|
|
600
|
+
dist/
|
|
601
|
+
build/
|
|
602
|
+
*.tsbuildinfo
|
|
603
|
+
|
|
604
|
+
# Environment
|
|
605
|
+
.env
|
|
606
|
+
.env.local
|
|
607
|
+
.env.*.local
|
|
608
|
+
|
|
609
|
+
# IDE
|
|
610
|
+
.vscode/
|
|
611
|
+
.idea/
|
|
612
|
+
*.swp
|
|
613
|
+
*.swo
|
|
614
|
+
*~
|
|
615
|
+
|
|
616
|
+
# OS
|
|
617
|
+
.DS_Store
|
|
618
|
+
Thumbs.db
|
|
619
|
+
|
|
620
|
+
# Logs
|
|
621
|
+
logs/
|
|
622
|
+
*.log
|
|
623
|
+
|
|
624
|
+
# Test
|
|
625
|
+
coverage/
|
|
626
|
+
|
|
627
|
+
# Temporary
|
|
628
|
+
tmp/
|
|
629
|
+
temp/
|
|
630
|
+
`;
|
|
631
|
+
}
|
|
632
|
+
function getDockerIgnore() {
|
|
633
|
+
return `node_modules
|
|
634
|
+
npm-debug.log
|
|
635
|
+
.env
|
|
636
|
+
.env.local
|
|
637
|
+
.git
|
|
638
|
+
.gitignore
|
|
639
|
+
README.md
|
|
640
|
+
dist
|
|
641
|
+
build
|
|
642
|
+
coverage
|
|
643
|
+
.vscode
|
|
644
|
+
.idea
|
|
645
|
+
*.log
|
|
646
|
+
tmp
|
|
647
|
+
temp
|
|
251
648
|
`;
|
|
252
649
|
}
|
|
253
650
|
main().catch(console.error);
|