create-sprint 0.0.134 → 0.0.136
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 +10 -11
- package/dist/templates/controllers.js +109 -0
- package/dist/templates/index.js +3 -3
- package/dist/templates/packageJson.js +5 -4
- package/dist/templates/routes.js +29 -0
- package/dist/templates/schemas.js +45 -0
- package/package.json +1 -1
- package/src/index.ts +10 -15
- package/src/templates/controllers.ts +111 -1
- package/src/templates/index.ts +3 -3
- package/src/templates/packageJson.ts +7 -4
- package/src/templates/routes.ts +29 -0
- package/src/templates/schemas.ts +45 -0
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { join } from "path";
|
|
|
5
5
|
import color from "picocolors";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
import { validateProjectName } from "./validators.js";
|
|
8
|
-
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getEnvExample, getInternalAuthMiddleware, getUserAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob, getGraphQLFiles } from "./generators.js";
|
|
8
|
+
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getMainFile, getHomeRoute, getAdminRoute, getUploadRoute, getHomeController, getAdminController, getUploadController, getEnvExample, getInternalAuthMiddleware, getUserAuthMiddleware, getHomeSchema, getAdminSchema, getUploadSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob, getGraphQLFiles } from "./generators.js";
|
|
9
9
|
export async function writeFile(path, content, options) {
|
|
10
10
|
if (typeof content === "string")
|
|
11
11
|
content = content.trim();
|
|
@@ -24,7 +24,7 @@ export async function runCLI(args) {
|
|
|
24
24
|
telemetry: options.telemetry ?? "none",
|
|
25
25
|
swagger: options.swagger ?? true,
|
|
26
26
|
graphql: options.graphql ?? false,
|
|
27
|
-
docker: options.docker || false
|
|
27
|
+
docker: options.docker || false
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
else {
|
|
@@ -32,7 +32,7 @@ export async function runCLI(args) {
|
|
|
32
32
|
projectName: () => p.text({
|
|
33
33
|
message: "Project name:",
|
|
34
34
|
placeholder: "my-api",
|
|
35
|
-
validate: (v) => validateProjectName(v || "sprint-app") || undefined
|
|
35
|
+
validate: (v) => validateProjectName(v || "sprint-app") || undefined
|
|
36
36
|
}),
|
|
37
37
|
language: () => p.select({
|
|
38
38
|
message: "Language:",
|
|
@@ -55,12 +55,12 @@ export async function runCLI(args) {
|
|
|
55
55
|
}),
|
|
56
56
|
swagger: () => p.confirm({ message: "Add Swagger UI & OpenAPI?", initialValue: true }),
|
|
57
57
|
graphql: () => p.confirm({ message: "Add GraphQL support?", initialValue: false }),
|
|
58
|
-
docker: () => p.confirm({ message: "Add Docker support?", initialValue: false })
|
|
58
|
+
docker: () => p.confirm({ message: "Add Docker support?", initialValue: false })
|
|
59
59
|
}, {
|
|
60
60
|
onCancel: () => {
|
|
61
61
|
p.cancel("Cancelled.");
|
|
62
62
|
process.exit(0);
|
|
63
|
-
}
|
|
63
|
+
}
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
const targetDir = config.projectName === "." ? process.cwd() : join(process.cwd(), config.projectName);
|
|
@@ -102,10 +102,7 @@ export async function runCLI(args) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
const cdCmd = config.projectName === "." ? "" : `cd ${config.projectName} && `;
|
|
105
|
-
p.note([
|
|
106
|
-
!installDeps ? `${cdCmd}npm install --include=dev` : "",
|
|
107
|
-
`${cdCmd}npm run dev`
|
|
108
|
-
].filter(Boolean).join("\n"), "Next steps");
|
|
105
|
+
p.note([!installDeps ? `${cdCmd}npm install --include=dev` : "", `${cdCmd}npm run dev`].filter(Boolean).join("\n"), "Next steps");
|
|
109
106
|
p.outro("Ready. Happy shipping.");
|
|
110
107
|
}
|
|
111
108
|
;
|
|
@@ -181,9 +178,8 @@ async function createProject(projectName, language, telemetry, swagger, graphql,
|
|
|
181
178
|
await mkdir(join(srcDir, "cronjobs"), { recursive: true });
|
|
182
179
|
await mkdir(join(srcDir, "config"), { recursive: true });
|
|
183
180
|
await mkdir(join(srcDir, "services"), { recursive: true });
|
|
184
|
-
if (graphql)
|
|
181
|
+
if (graphql)
|
|
185
182
|
await mkdir(join(srcDir, "graphql"), { recursive: true });
|
|
186
|
-
}
|
|
187
183
|
if (language === "typescript") {
|
|
188
184
|
await writeFile(join(srcDir, "config", "index.ts"), "");
|
|
189
185
|
await writeFile(join(srcDir, "config", "clients.ts"), "");
|
|
@@ -196,12 +192,15 @@ async function createProject(projectName, language, telemetry, swagger, graphql,
|
|
|
196
192
|
await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getMainFile(language, graphql));
|
|
197
193
|
await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
|
|
198
194
|
await writeFile(join(srcDir, "routes", "admin." + (language === "typescript" ? "ts" : "js")), getAdminRoute(language));
|
|
195
|
+
await writeFile(join(srcDir, "routes", "upload." + (language === "typescript" ? "ts" : "js")), getUploadRoute(language));
|
|
199
196
|
await writeFile(join(srcDir, "controllers", "home." + (language === "typescript" ? "ts" : "js")), getHomeController(language));
|
|
200
197
|
await writeFile(join(srcDir, "controllers", "admin." + (language === "typescript" ? "ts" : "js")), getAdminController(language));
|
|
198
|
+
await writeFile(join(srcDir, "controllers", "upload." + (language === "typescript" ? "ts" : "js")), getUploadController(language));
|
|
201
199
|
await writeFile(join(srcDir, "middlewares", "auth.internal." + (language === "typescript" ? "ts" : "js")), getInternalAuthMiddleware(language));
|
|
202
200
|
await writeFile(join(srcDir, "middlewares", "auth.user." + (language === "typescript" ? "ts" : "js")), getUserAuthMiddleware(language));
|
|
203
201
|
await writeFile(join(srcDir, "schemas", "home." + (language === "typescript" ? "ts" : "js")), getHomeSchema(language));
|
|
204
202
|
await writeFile(join(srcDir, "schemas", "admin." + (language === "typescript" ? "ts" : "js")), getAdminSchema(language));
|
|
203
|
+
await writeFile(join(srcDir, "schemas", "upload." + (language === "typescript" ? "ts" : "js")), getUploadSchema(language));
|
|
205
204
|
await writeFile(join(srcDir, "cronjobs", "example." + (language === "typescript" ? "ts" : "js")), getExampleCronJob(language));
|
|
206
205
|
if (graphql) {
|
|
207
206
|
const graphqlFiles = getGraphQLFiles(language);
|
|
@@ -101,3 +101,112 @@ export const jwtGenerateController = (req, res) => {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
;
|
|
104
|
+
export function getUploadController(language) {
|
|
105
|
+
if (language === "typescript") {
|
|
106
|
+
return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
|
|
107
|
+
|
|
108
|
+
export const uploadPdfController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
109
|
+
const file = req.file;
|
|
110
|
+
|
|
111
|
+
if (!file) return res.status(400).json({ error: "No file uploaded" });
|
|
112
|
+
|
|
113
|
+
res.json({
|
|
114
|
+
message: "PDF uploaded successfully",
|
|
115
|
+
file: {
|
|
116
|
+
name: file.originalname,
|
|
117
|
+
size: file.size,
|
|
118
|
+
type: file.mimetype
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const uploadMultiplePdfsController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
124
|
+
const files = req.files?.documents || [];
|
|
125
|
+
|
|
126
|
+
if (!files || files.length === 0) return res.status(400).json({ error: "No files uploaded" });
|
|
127
|
+
|
|
128
|
+
res.json({
|
|
129
|
+
message: \`\${files.length} PDFs uploaded successfully\`,
|
|
130
|
+
files: files.map((f) => ({
|
|
131
|
+
name: f.originalname,
|
|
132
|
+
size: f.size,
|
|
133
|
+
type: f.mimetype
|
|
134
|
+
}))
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const streamUploadController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
139
|
+
const files = (req as any).files?.file || [];
|
|
140
|
+
|
|
141
|
+
if (!files || files.length === 0) return res.status(400).json({ error: "No files uploaded" });
|
|
142
|
+
|
|
143
|
+
res.json({
|
|
144
|
+
message: \`\${files.length} file(s) processed via streaming\`,
|
|
145
|
+
files: files.map((f: any) => ({
|
|
146
|
+
fieldName: f.fieldname,
|
|
147
|
+
filename: f.originalname,
|
|
148
|
+
mimeType: f.mimetype,
|
|
149
|
+
encoding: f.encoding
|
|
150
|
+
}))
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
return `export const uploadPdfController = (req, res) => {
|
|
156
|
+
const file = req.file;
|
|
157
|
+
|
|
158
|
+
if (!file) return res.status(400).json({ error: "No file uploaded" });
|
|
159
|
+
|
|
160
|
+
res.json({
|
|
161
|
+
message: "PDF uploaded successfully",
|
|
162
|
+
file: {
|
|
163
|
+
name: file.originalname,
|
|
164
|
+
size: file.size,
|
|
165
|
+
type: file.mimetype
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const uploadMultiplePdfsController = (req, res) => {
|
|
171
|
+
const files = req.files?.documents || [];
|
|
172
|
+
|
|
173
|
+
if (!files || files.length === 0) return res.status(400).json({ error: "No files uploaded" });
|
|
174
|
+
|
|
175
|
+
res.json({
|
|
176
|
+
message: \`\${files.length} PDFs uploaded successfully\`,
|
|
177
|
+
files: files.map((f) => ({
|
|
178
|
+
name: f.originalname,
|
|
179
|
+
size: f.size,
|
|
180
|
+
type: f.mimetype
|
|
181
|
+
}))
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
export const streamUploadController = async (req, res, next) => {
|
|
186
|
+
const sprint = req.sprintInstance;
|
|
187
|
+
|
|
188
|
+
if (!sprint) return res.status(500).json({ error: "Sprint instance not available" });
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const streamParser = sprint.streamUpload({
|
|
192
|
+
limits: { fileSize: 10 * 1024 * 1024 }
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const { files, fields } = await streamParser(req, res);
|
|
196
|
+
|
|
197
|
+
res.json({
|
|
198
|
+
message: \`\${files.length} file(s) processed via streaming\`,
|
|
199
|
+
files: files.map((f) => ({
|
|
200
|
+
fieldName: f.fieldName,
|
|
201
|
+
filename: f.filename,
|
|
202
|
+
mimeType: f.mimeType,
|
|
203
|
+
encoding: f.encoding
|
|
204
|
+
})),
|
|
205
|
+
fields
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
return res.status(400).json({ error: "Stream upload failed", details: error });
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
`;
|
|
212
|
+
}
|
package/dist/templates/index.js
CHANGED
|
@@ -5,13 +5,13 @@ export { getTsConfig, getSprintConfigFile } from "./configFiles.js";
|
|
|
5
5
|
// Environment Files
|
|
6
6
|
export { getEnvExample, getEnvDevelopment, getEnvProduction } from "./env.js";
|
|
7
7
|
// Routes
|
|
8
|
-
export { getMainFile, getHomeRoute, getAdminRoute } from "./routes.js";
|
|
8
|
+
export { getMainFile, getHomeRoute, getAdminRoute, getUploadRoute } from "./routes.js";
|
|
9
9
|
// Controllers
|
|
10
|
-
export { getHomeController, getAdminController } from "./controllers.js";
|
|
10
|
+
export { getHomeController, getAdminController, getUploadController } from "./controllers.js";
|
|
11
11
|
// Middlewares
|
|
12
12
|
export { getInternalAuthMiddleware, getUserAuthMiddleware } from "./middlewares.js";
|
|
13
13
|
// Schemas
|
|
14
|
-
export { getHomeSchema, getAdminSchema } from "./schemas.js";
|
|
14
|
+
export { getHomeSchema, getAdminSchema, getUploadSchema } from "./schemas.js";
|
|
15
15
|
// Cronjobs
|
|
16
16
|
export { getExampleCronJob } from "./cronjobs.js";
|
|
17
17
|
// Docker
|
|
@@ -10,7 +10,7 @@ export function generateJWTKeys() {
|
|
|
10
10
|
;
|
|
11
11
|
export function getTypeScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
12
12
|
const deps = {
|
|
13
|
-
"sprint-es": "^0.0.
|
|
13
|
+
"sprint-es": "^0.0.165"
|
|
14
14
|
};
|
|
15
15
|
const devDeps = {
|
|
16
16
|
"@types/node": "^22.0.0",
|
|
@@ -22,14 +22,15 @@ export function getTypeScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
|
22
22
|
deps["@sentry/node"] = "^8.0.0";
|
|
23
23
|
else if (telemetry === "discord")
|
|
24
24
|
deps["axios"] = "^1.6.0";
|
|
25
|
-
if (swagger)
|
|
25
|
+
if (swagger) {
|
|
26
26
|
deps["swagger-ui-express"] = "^5.0.0";
|
|
27
|
-
if (swagger)
|
|
28
27
|
devDeps["@types/swagger-ui-express"] = "^4.1.8";
|
|
28
|
+
}
|
|
29
29
|
if (graphql) {
|
|
30
30
|
deps["graphql"] = "^16.13.0";
|
|
31
31
|
deps["graphql-http"] = "^1.22.4";
|
|
32
32
|
deps["ruru"] = "^2.0.0-rc.6";
|
|
33
|
+
devDeps["@types/swagger-ui-express"] = "^4.1.8";
|
|
33
34
|
}
|
|
34
35
|
return {
|
|
35
36
|
name: name === "." ? "sprint-app" : name,
|
|
@@ -66,7 +67,7 @@ export function getTypeScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
|
66
67
|
;
|
|
67
68
|
export function getJavaScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
68
69
|
const deps = {
|
|
69
|
-
"sprint-es": "^0.0.
|
|
70
|
+
"sprint-es": "^0.0.165"
|
|
70
71
|
};
|
|
71
72
|
if (telemetry === "sentry" || telemetry === "glitchtip")
|
|
72
73
|
deps["@sentry/node"] = "^8.0.0";
|
package/dist/templates/routes.js
CHANGED
|
@@ -84,3 +84,32 @@ export default router;
|
|
|
84
84
|
`;
|
|
85
85
|
}
|
|
86
86
|
;
|
|
87
|
+
export function getUploadRoute(language) {
|
|
88
|
+
if (language === "typescript") {
|
|
89
|
+
return `import { Router } from "sprint-es";
|
|
90
|
+
import { uploadPdfSchema, uploadMultiplePdfsSchema, streamUploadSchema } from "@/schemas/upload";
|
|
91
|
+
import { uploadPdfController, uploadMultiplePdfsController, streamUploadController } from "@/controllers/upload";
|
|
92
|
+
|
|
93
|
+
const router = Router();
|
|
94
|
+
|
|
95
|
+
router.post("/pdf", uploadPdfSchema, uploadPdfController);
|
|
96
|
+
router.post("/pdfs", uploadMultiplePdfsSchema, uploadMultiplePdfsController);
|
|
97
|
+
router.post("/stream", streamUploadSchema, streamUploadController);
|
|
98
|
+
|
|
99
|
+
export default router;
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
return `import { Router } from "sprint-es";
|
|
103
|
+
import { uploadPdfSchema, uploadMultiplePdfsSchema, streamUploadSchema } from "../schemas/upload.js";
|
|
104
|
+
import { uploadPdfController, uploadMultiplePdfsController, streamUploadController } from "../controllers/upload.js";
|
|
105
|
+
|
|
106
|
+
const router = Router();
|
|
107
|
+
|
|
108
|
+
router.post("/pdf", uploadPdfSchema, uploadPdfController);
|
|
109
|
+
router.post("/pdfs", uploadMultiplePdfsSchema, uploadMultiplePdfsController);
|
|
110
|
+
router.post("/stream", streamUploadSchema, streamUploadController);
|
|
111
|
+
|
|
112
|
+
export default router;
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
;
|
|
@@ -54,3 +54,48 @@ export const jwtGenerateSchema = defineRouteSchema({
|
|
|
54
54
|
`;
|
|
55
55
|
}
|
|
56
56
|
;
|
|
57
|
+
export function getUploadSchema(language) {
|
|
58
|
+
if (language === "typescript") {
|
|
59
|
+
return `import { z, defineRouteSchema } from "sprint-es/schemas";
|
|
60
|
+
|
|
61
|
+
export const uploadPdfSchema = defineRouteSchema({
|
|
62
|
+
files: {
|
|
63
|
+
document: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const uploadMultiplePdfsSchema = defineRouteSchema({
|
|
68
|
+
files: {
|
|
69
|
+
documents: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const streamUploadSchema = defineRouteSchema({
|
|
74
|
+
files: {
|
|
75
|
+
file: z.files.stream().maxSize(10 * 1024 * 1024)
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
return `import { z, defineRouteSchema } from "sprint-es/schemas";
|
|
81
|
+
|
|
82
|
+
export const uploadPdfSchema = defineRouteSchema({
|
|
83
|
+
files: {
|
|
84
|
+
document: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export const uploadMultiplePdfsSchema = defineRouteSchema({
|
|
89
|
+
files: {
|
|
90
|
+
documents: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const streamUploadSchema = defineRouteSchema({
|
|
95
|
+
files: {
|
|
96
|
+
file: z.files.stream().maxSize(10 * 1024 * 1024)
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { join } from "path";
|
|
|
5
5
|
import color from "picocolors";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
import { validateProjectName } from "./validators.js";
|
|
8
|
-
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getMainFile, getHomeRoute, getAdminRoute, getHomeController, getAdminController, getEnvExample, getInternalAuthMiddleware, getUserAuthMiddleware, getHomeSchema, getAdminSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob, getGraphQLFiles } from "./generators.js";
|
|
8
|
+
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getMainFile, getHomeRoute, getAdminRoute, getUploadRoute, getHomeController, getAdminController, getUploadController, getEnvExample, getInternalAuthMiddleware, getUserAuthMiddleware, getHomeSchema, getAdminSchema, getUploadSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob, getGraphQLFiles } from "./generators.js";
|
|
9
9
|
|
|
10
10
|
type TelemetryProviders = "none" | "sentry" | "glitchtip" | "discord" | "open-telemetry" | "telegram" | "nodemailer";
|
|
11
11
|
|
|
@@ -48,7 +48,7 @@ export async function runCLI(args: string[]) {
|
|
|
48
48
|
telemetry: options.telemetry ?? "none",
|
|
49
49
|
swagger: options.swagger ?? true,
|
|
50
50
|
graphql: options.graphql ?? false,
|
|
51
|
-
docker: options.docker || false
|
|
51
|
+
docker: options.docker || false
|
|
52
52
|
};
|
|
53
53
|
} else {
|
|
54
54
|
config = await p.group(
|
|
@@ -57,7 +57,7 @@ export async function runCLI(args: string[]) {
|
|
|
57
57
|
p.text({
|
|
58
58
|
message: "Project name:",
|
|
59
59
|
placeholder: "my-api",
|
|
60
|
-
validate: (v) => validateProjectName(v || "sprint-app") || undefined
|
|
60
|
+
validate: (v) => validateProjectName(v || "sprint-app") || undefined
|
|
61
61
|
}),
|
|
62
62
|
|
|
63
63
|
language: () =>
|
|
@@ -87,13 +87,13 @@ export async function runCLI(args: string[]) {
|
|
|
87
87
|
|
|
88
88
|
graphql: () => p.confirm({ message: "Add GraphQL support?", initialValue: false }),
|
|
89
89
|
|
|
90
|
-
docker: () => p.confirm({ message: "Add Docker support?", initialValue: false })
|
|
90
|
+
docker: () => p.confirm({ message: "Add Docker support?", initialValue: false })
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
onCancel: () => {
|
|
94
94
|
p.cancel("Cancelled.");
|
|
95
95
|
process.exit(0);
|
|
96
|
-
}
|
|
96
|
+
}
|
|
97
97
|
}
|
|
98
98
|
);
|
|
99
99
|
}
|
|
@@ -143,13 +143,7 @@ export async function runCLI(args: string[]) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
const cdCmd = config.projectName === "." ? "" : `cd ${config.projectName} && `;
|
|
146
|
-
p.note(
|
|
147
|
-
[
|
|
148
|
-
!installDeps ? `${cdCmd}npm install --include=dev` : "",
|
|
149
|
-
`${cdCmd}npm run dev`
|
|
150
|
-
].filter(Boolean).join("\n"),
|
|
151
|
-
"Next steps"
|
|
152
|
-
);
|
|
146
|
+
p.note([!installDeps ? `${cdCmd}npm install --include=dev` : "", `${cdCmd}npm run dev` ].filter(Boolean).join("\n"), "Next steps");
|
|
153
147
|
|
|
154
148
|
p.outro("Ready. Happy shipping.");
|
|
155
149
|
};
|
|
@@ -233,9 +227,7 @@ async function createProject(
|
|
|
233
227
|
await mkdir(join(srcDir, "config"), { recursive: true });
|
|
234
228
|
await mkdir(join(srcDir, "services"), { recursive: true });
|
|
235
229
|
|
|
236
|
-
if (graphql) {
|
|
237
|
-
await mkdir(join(srcDir, "graphql"), { recursive: true });
|
|
238
|
-
}
|
|
230
|
+
if (graphql) await mkdir(join(srcDir, "graphql"), { recursive: true });
|
|
239
231
|
|
|
240
232
|
if (language === "typescript") {
|
|
241
233
|
await writeFile(join(srcDir, "config", "index.ts"), "");
|
|
@@ -251,15 +243,18 @@ async function createProject(
|
|
|
251
243
|
|
|
252
244
|
await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
|
|
253
245
|
await writeFile(join(srcDir, "routes", "admin." + (language === "typescript" ? "ts" : "js")), getAdminRoute(language));
|
|
246
|
+
await writeFile(join(srcDir, "routes", "upload." + (language === "typescript" ? "ts" : "js")), getUploadRoute(language));
|
|
254
247
|
|
|
255
248
|
await writeFile(join(srcDir, "controllers", "home." + (language === "typescript" ? "ts" : "js")), getHomeController(language));
|
|
256
249
|
await writeFile(join(srcDir, "controllers", "admin." + (language === "typescript" ? "ts" : "js")), getAdminController(language));
|
|
250
|
+
await writeFile(join(srcDir, "controllers", "upload." + (language === "typescript" ? "ts" : "js")), getUploadController(language));
|
|
257
251
|
|
|
258
252
|
await writeFile(join(srcDir, "middlewares", "auth.internal." + (language === "typescript" ? "ts" : "js")), getInternalAuthMiddleware(language));
|
|
259
253
|
await writeFile(join(srcDir, "middlewares", "auth.user." + (language === "typescript" ? "ts" : "js")), getUserAuthMiddleware(language));
|
|
260
254
|
|
|
261
255
|
await writeFile(join(srcDir, "schemas", "home." + (language === "typescript" ? "ts" : "js")), getHomeSchema(language));
|
|
262
256
|
await writeFile(join(srcDir, "schemas", "admin." + (language === "typescript" ? "ts" : "js")), getAdminSchema(language));
|
|
257
|
+
await writeFile(join(srcDir, "schemas", "upload." + (language === "typescript" ? "ts" : "js")), getUploadSchema(language));
|
|
263
258
|
|
|
264
259
|
await writeFile(join(srcDir, "cronjobs", "example." + (language === "typescript" ? "ts" : "js")), getExampleCronJob(language));
|
|
265
260
|
|
|
@@ -98,4 +98,114 @@ export const jwtGenerateController = (req, res) => {
|
|
|
98
98
|
};
|
|
99
99
|
`;
|
|
100
100
|
}
|
|
101
|
-
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function getUploadController(language: string) {
|
|
104
|
+
if (language === "typescript") {
|
|
105
|
+
return `import { Handler, SprintRequest, SprintResponse } from "sprint-es";
|
|
106
|
+
|
|
107
|
+
export const uploadPdfController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
108
|
+
const file = req.file;
|
|
109
|
+
|
|
110
|
+
if (!file) return res.status(400).json({ error: "No file uploaded" });
|
|
111
|
+
|
|
112
|
+
res.json({
|
|
113
|
+
message: "PDF uploaded successfully",
|
|
114
|
+
file: {
|
|
115
|
+
name: file.originalname,
|
|
116
|
+
size: file.size,
|
|
117
|
+
type: file.mimetype
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const uploadMultiplePdfsController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
123
|
+
const files = req.files?.documents || [];
|
|
124
|
+
|
|
125
|
+
if (!files || files.length === 0) return res.status(400).json({ error: "No files uploaded" });
|
|
126
|
+
|
|
127
|
+
res.json({
|
|
128
|
+
message: \`\${files.length} PDFs uploaded successfully\`,
|
|
129
|
+
files: files.map((f) => ({
|
|
130
|
+
name: f.originalname,
|
|
131
|
+
size: f.size,
|
|
132
|
+
type: f.mimetype
|
|
133
|
+
}))
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const streamUploadController: Handler = (req: SprintRequest, res: SprintResponse) => {
|
|
138
|
+
const files = (req as any).files?.file || [];
|
|
139
|
+
|
|
140
|
+
if (!files || files.length === 0) return res.status(400).json({ error: "No files uploaded" });
|
|
141
|
+
|
|
142
|
+
res.json({
|
|
143
|
+
message: \`\${files.length} file(s) processed via streaming\`,
|
|
144
|
+
files: files.map((f: any) => ({
|
|
145
|
+
fieldName: f.fieldname,
|
|
146
|
+
filename: f.originalname,
|
|
147
|
+
mimeType: f.mimetype,
|
|
148
|
+
encoding: f.encoding
|
|
149
|
+
}))
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
return `export const uploadPdfController = (req, res) => {
|
|
155
|
+
const file = req.file;
|
|
156
|
+
|
|
157
|
+
if (!file) return res.status(400).json({ error: "No file uploaded" });
|
|
158
|
+
|
|
159
|
+
res.json({
|
|
160
|
+
message: "PDF uploaded successfully",
|
|
161
|
+
file: {
|
|
162
|
+
name: file.originalname,
|
|
163
|
+
size: file.size,
|
|
164
|
+
type: file.mimetype
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const uploadMultiplePdfsController = (req, res) => {
|
|
170
|
+
const files = req.files?.documents || [];
|
|
171
|
+
|
|
172
|
+
if (!files || files.length === 0) return res.status(400).json({ error: "No files uploaded" });
|
|
173
|
+
|
|
174
|
+
res.json({
|
|
175
|
+
message: \`\${files.length} PDFs uploaded successfully\`,
|
|
176
|
+
files: files.map((f) => ({
|
|
177
|
+
name: f.originalname,
|
|
178
|
+
size: f.size,
|
|
179
|
+
type: f.mimetype
|
|
180
|
+
}))
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export const streamUploadController = async (req, res, next) => {
|
|
185
|
+
const sprint = req.sprintInstance;
|
|
186
|
+
|
|
187
|
+
if (!sprint) return res.status(500).json({ error: "Sprint instance not available" });
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
const streamParser = sprint.streamUpload({
|
|
191
|
+
limits: { fileSize: 10 * 1024 * 1024 }
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const { files, fields } = await streamParser(req, res);
|
|
195
|
+
|
|
196
|
+
res.json({
|
|
197
|
+
message: \`\${files.length} file(s) processed via streaming\`,
|
|
198
|
+
files: files.map((f) => ({
|
|
199
|
+
fieldName: f.fieldName,
|
|
200
|
+
filename: f.filename,
|
|
201
|
+
mimeType: f.mimeType,
|
|
202
|
+
encoding: f.encoding
|
|
203
|
+
})),
|
|
204
|
+
fields
|
|
205
|
+
});
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return res.status(400).json({ error: "Stream upload failed", details: error });
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
`;
|
|
211
|
+
}
|
package/src/templates/index.ts
CHANGED
|
@@ -8,16 +8,16 @@ export { getTsConfig, getSprintConfigFile } from "./configFiles.js";
|
|
|
8
8
|
export { getEnvExample, getEnvDevelopment, getEnvProduction } from "./env.js";
|
|
9
9
|
|
|
10
10
|
// Routes
|
|
11
|
-
export { getMainFile, getHomeRoute, getAdminRoute } from "./routes.js";
|
|
11
|
+
export { getMainFile, getHomeRoute, getAdminRoute, getUploadRoute } from "./routes.js";
|
|
12
12
|
|
|
13
13
|
// Controllers
|
|
14
|
-
export { getHomeController, getAdminController } from "./controllers.js";
|
|
14
|
+
export { getHomeController, getAdminController, getUploadController } from "./controllers.js";
|
|
15
15
|
|
|
16
16
|
// Middlewares
|
|
17
17
|
export { getInternalAuthMiddleware, getUserAuthMiddleware } from "./middlewares.js";
|
|
18
18
|
|
|
19
19
|
// Schemas
|
|
20
|
-
export { getHomeSchema, getAdminSchema } from "./schemas.js";
|
|
20
|
+
export { getHomeSchema, getAdminSchema, getUploadSchema } from "./schemas.js";
|
|
21
21
|
|
|
22
22
|
// Cronjobs
|
|
23
23
|
export { getExampleCronJob } from "./cronjobs.js";
|
|
@@ -16,7 +16,7 @@ export function generateJWTKeys(): JWTKeys {
|
|
|
16
16
|
|
|
17
17
|
export function getTypeScriptPackageJson(name: string, telemetry: string, swagger: boolean, graphql: boolean) {
|
|
18
18
|
const deps: Record<string, string> = {
|
|
19
|
-
"sprint-es": "^0.0.
|
|
19
|
+
"sprint-es": "^0.0.165"
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
const devDeps: Record<string, string> = {
|
|
@@ -29,13 +29,16 @@ export function getTypeScriptPackageJson(name: string, telemetry: string, swagge
|
|
|
29
29
|
if (telemetry === "sentry" || telemetry === "glitchtip") deps["@sentry/node"] = "^8.0.0";
|
|
30
30
|
else if (telemetry === "discord") deps["axios"] = "^1.6.0";
|
|
31
31
|
|
|
32
|
-
if (swagger)
|
|
33
|
-
|
|
32
|
+
if (swagger) {
|
|
33
|
+
deps["swagger-ui-express"] = "^5.0.0";
|
|
34
|
+
devDeps["@types/swagger-ui-express"] = "^4.1.8";
|
|
35
|
+
}
|
|
34
36
|
|
|
35
37
|
if (graphql) {
|
|
36
38
|
deps["graphql"] = "^16.13.0";
|
|
37
39
|
deps["graphql-http"] = "^1.22.4";
|
|
38
40
|
deps["ruru"] = "^2.0.0-rc.6";
|
|
41
|
+
devDeps["@types/swagger-ui-express"] = "^4.1.8";
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
return {
|
|
@@ -73,7 +76,7 @@ export function getTypeScriptPackageJson(name: string, telemetry: string, swagge
|
|
|
73
76
|
|
|
74
77
|
export function getJavaScriptPackageJson(name: string, telemetry: string, swagger: boolean, graphql: boolean) {
|
|
75
78
|
const deps: Record<string, string> = {
|
|
76
|
-
"sprint-es": "^0.0.
|
|
79
|
+
"sprint-es": "^0.0.165"
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
if (telemetry === "sentry" || telemetry === "glitchtip") deps["@sentry/node"] = "^8.0.0";
|
package/src/templates/routes.ts
CHANGED
|
@@ -83,6 +83,35 @@ router.get("/", adminSchema, adminController);
|
|
|
83
83
|
router.get("/users", adminSchema, adminUsersController);
|
|
84
84
|
router.post("/jwt/generate", jwtGenerateSchema, jwtGenerateController);
|
|
85
85
|
|
|
86
|
+
export default router;
|
|
87
|
+
`;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export function getUploadRoute(language: string) {
|
|
91
|
+
if (language === "typescript") {
|
|
92
|
+
return `import { Router } from "sprint-es";
|
|
93
|
+
import { uploadPdfSchema, uploadMultiplePdfsSchema, streamUploadSchema } from "@/schemas/upload";
|
|
94
|
+
import { uploadPdfController, uploadMultiplePdfsController, streamUploadController } from "@/controllers/upload";
|
|
95
|
+
|
|
96
|
+
const router = Router();
|
|
97
|
+
|
|
98
|
+
router.post("/pdf", uploadPdfSchema, uploadPdfController);
|
|
99
|
+
router.post("/pdfs", uploadMultiplePdfsSchema, uploadMultiplePdfsController);
|
|
100
|
+
router.post("/stream", streamUploadSchema, streamUploadController);
|
|
101
|
+
|
|
102
|
+
export default router;
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
return `import { Router } from "sprint-es";
|
|
106
|
+
import { uploadPdfSchema, uploadMultiplePdfsSchema, streamUploadSchema } from "../schemas/upload.js";
|
|
107
|
+
import { uploadPdfController, uploadMultiplePdfsController, streamUploadController } from "../controllers/upload.js";
|
|
108
|
+
|
|
109
|
+
const router = Router();
|
|
110
|
+
|
|
111
|
+
router.post("/pdf", uploadPdfSchema, uploadPdfController);
|
|
112
|
+
router.post("/pdfs", uploadMultiplePdfsSchema, uploadMultiplePdfsController);
|
|
113
|
+
router.post("/stream", streamUploadSchema, streamUploadController);
|
|
114
|
+
|
|
86
115
|
export default router;
|
|
87
116
|
`;
|
|
88
117
|
};
|
package/src/templates/schemas.ts
CHANGED
|
@@ -52,4 +52,49 @@ export const jwtGenerateSchema = defineRouteSchema({
|
|
|
52
52
|
})
|
|
53
53
|
});
|
|
54
54
|
`;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export function getUploadSchema(language: string) {
|
|
58
|
+
if (language === "typescript") {
|
|
59
|
+
return `import { z, defineRouteSchema } from "sprint-es/schemas";
|
|
60
|
+
|
|
61
|
+
export const uploadPdfSchema = defineRouteSchema({
|
|
62
|
+
files: {
|
|
63
|
+
document: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const uploadMultiplePdfsSchema = defineRouteSchema({
|
|
68
|
+
files: {
|
|
69
|
+
documents: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const streamUploadSchema = defineRouteSchema({
|
|
74
|
+
files: {
|
|
75
|
+
file: z.files.stream().maxSize(10 * 1024 * 1024)
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
return `import { z, defineRouteSchema } from "sprint-es/schemas";
|
|
81
|
+
|
|
82
|
+
export const uploadPdfSchema = defineRouteSchema({
|
|
83
|
+
files: {
|
|
84
|
+
document: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export const uploadMultiplePdfsSchema = defineRouteSchema({
|
|
89
|
+
files: {
|
|
90
|
+
documents: z.files.format("pdf").maxSize(10 * 1024 * 1024)
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
export const streamUploadSchema = defineRouteSchema({
|
|
95
|
+
files: {
|
|
96
|
+
file: z.files.stream().maxSize(10 * 1024 * 1024)
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
`;
|
|
55
100
|
};
|