Skip to content

Commit f230922

Browse files
Complete BackForge v1.7.0 - Enterprise Backend Generator
1 parent 85d625b commit f230922

File tree

82 files changed

+6716
-340
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+6716
-340
lines changed

README.md

Lines changed: 655 additions & 239 deletions
Large diffs are not rendered by default.

packages/backforge-cli/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "backforge-cli",
3-
"version": "1.0.9",
3+
"version": "1.7.0",
44
"description": "BackForge CLI - Production-ready backend scaffolder (Global CLI wrapper)",
55
"bin": {
66
"backforge": "bin/backforge.js"
@@ -25,7 +25,7 @@
2525
"node": ">=18.0.0"
2626
},
2727
"dependencies": {
28-
"backforge-core": "^1.0.12"
28+
"backforge-core": "^1.7.0"
2929
},
3030
"scripts": {
3131
"build": "echo skip"

packages/backforge-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "backforge-core",
3-
"version": "1.0.12",
3+
"version": "1.7.0",
44
"description": "Production-ready backend scaffolder for Node.js and Bun - Core library",
55
"main": "./dist/index.js",
66
"module": "./dist/index.mjs",

packages/backforge-core/src/cli/index.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as p from '@clack/prompts';
44
import pc from 'picocolors';
55
import type { CLIOptions } from '../types';
66
import { normalizeGenerateOptions } from '../scaffold/normalize';
7-
import { resolveDeps } from '../scaffold/deps';
7+
import { resolveDeps, getFeatureDependencies } from '../scaffold/deps';
88
import { generateProjectFromTemplate } from '../scaffold/writer';
99
import { installDependencies, detectPackageManager } from '../utils/pm';
1010
import { log, setSilent } from '../utils/log';
@@ -22,10 +22,21 @@ export async function createProject(
2222
const orm = (inOpts.orm ?? 'mongoose') as 'mongoose' | 'prisma';
2323
const framework = (inOpts.framework ?? 'express') as 'express' | 'fastify';
2424
const stackDeps = resolveDeps({ runtime, language, orm, framework });
25+
const projectName = String(inOpts.projectName ?? normalized.context?.projectName ?? path.basename(String(normalized.targetRoot)));
26+
const features = inOpts.features || [];
27+
28+
// Add feature-specific dependencies
29+
const featureDeps = getFeatureDependencies(features, language);
30+
const allDeps = { ...stackDeps.dependencies, ...featureDeps.dependencies };
31+
const allDevDeps = { ...stackDeps.devDependencies, ...featureDeps.devDependencies };
32+
const allScripts = { ...stackDeps.scripts, ...featureDeps.scripts };
33+
2534
const ctx = Object.assign({}, normalized.context ?? {}, {
26-
deps: stackDeps.dependencies,
27-
devDependencies: stackDeps.devDependencies,
28-
scripts: stackDeps.scripts ?? {},
35+
deps: allDeps,
36+
devDependencies: allDevDeps,
37+
scripts: allScripts ?? {},
38+
appName: projectName,
39+
features,
2940
});
3041
const opts = {
3142
templatesRoot: String(normalized.templatesRoot),
@@ -36,8 +47,6 @@ export async function createProject(
3647
};
3748
setSilent(false);
3849

39-
const projectName = String(inOpts.projectName ?? ctx.projectName ?? path.basename(opts.targetRoot));
40-
4150
p.note(
4251
`Creating project ${pc.cyan(projectName)}
4352
Runtime: ${pc.green(runtime)}
@@ -107,6 +116,7 @@ export async function main() {
107116
language: userAnswers.language,
108117
orm: userAnswers.orm,
109118
framework: userAnswers.framework,
119+
features: userAnswers.features,
110120
...options
111121
};
112122

packages/backforge-core/src/cli/questions.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,62 @@ export async function askQuestions(): Promise<CLIOptions> {
5555
process.exit(1);
5656
}
5757

58+
// Additional features
59+
const features = await p.multiselect({
60+
message: 'Choose additional features (use SPACEBAR to select/deselect, ARROW keys to navigate, ENTER to confirm)',
61+
options: [
62+
{ value: 'oauth', label: 'OAuth 2.0 (Google, GitHub)', hint: 'Social login integration' },
63+
{ value: 'clerk-auth', label: 'Clerk Authentication', hint: 'Modern auth service' },
64+
{ value: 'graphql', label: 'GraphQL (Apollo Server)', hint: 'GraphQL API with Apollo' },
65+
{ value: 'microservices', label: 'Microservices Template', hint: 'Service discovery & communication' },
66+
{ value: 'websocket', label: 'WebSocket (Socket.io)', hint: 'Real-time communication' },
67+
{ value: 'message-queue', label: 'Message Queue (RabbitMQ)', hint: 'Async messaging' },
68+
{ value: 's3-upload', label: 'S3 File Upload', hint: 'AWS S3 integration' },
69+
{ value: 'email', label: 'Email Service (SendGrid)', hint: 'Transactional emails' },
70+
{ value: 'payment-stripe', label: 'Payment (Stripe)', hint: 'Global payment processing' },
71+
{ value: 'payment-razorpay', label: 'Payment (Razorpay)', hint: 'Indian payment gateway' },
72+
{ value: 'admin-dashboard', label: 'Admin Dashboard', hint: 'Basic admin interface' },
73+
{ value: 'migrations-ui', label: 'Database Migrations UI', hint: 'Web-based migrations' },
74+
{ value: 'load-testing', label: 'Load Testing (k6)', hint: 'Performance testing scripts' },
75+
],
76+
required: false,
77+
}) as string[];
78+
79+
// Show selected features
80+
if (features && features.length > 0) {
81+
console.log('\n📋 Selected features:');
82+
features.forEach((feature: string) => {
83+
const option = [
84+
{ value: 'oauth', label: 'OAuth 2.0 (Google, GitHub)' },
85+
{ value: 'clerk-auth', label: 'Clerk Authentication' },
86+
{ value: 'graphql', label: 'GraphQL (Apollo Server)' },
87+
{ value: 'microservices', label: 'Microservices Template' },
88+
{ value: 'websocket', label: 'WebSocket (Socket.io)' },
89+
{ value: 'message-queue', label: 'Message Queue (RabbitMQ)' },
90+
{ value: 's3-upload', label: 'S3 File Upload' },
91+
{ value: 'email', label: 'Email Service (SendGrid)' },
92+
{ value: 'payment-stripe', label: 'Payment (Stripe)' },
93+
{ value: 'payment-razorpay', label: 'Payment (Razorpay)' },
94+
{ value: 'admin-dashboard', label: 'Admin Dashboard' },
95+
{ value: 'migrations-ui', label: 'Database Migrations UI' },
96+
{ value: 'load-testing', label: 'Load Testing (k6)' },
97+
].find(opt => opt.value === feature);
98+
console.log(` ✅ ${option?.label}`);
99+
});
100+
console.log('');
101+
}
102+
if (p.isCancel(features)) {
103+
p.cancel('Operation aborted');
104+
process.exit(1);
105+
}
106+
58107
p.log.info(pc.green('Configuration complete'));
59108

60109
return {
61-
62110
language: language as CLIOptions['language'],
63111
orm: orm as CLIOptions['orm'],
64112
framework: framework as CLIOptions['framework'],
65113
projectName: String(projectName),
114+
features: features as string[],
66115
};
67116
}

packages/backforge-core/src/scaffold/deps.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ const versions = {
6060
typescript_eslint: '^8.0.0',
6161
globals: '^15.14.0',
6262
eslint_js: '^9.0.0',
63+
bcrypt: '^5.1.0',
64+
jsonwebtoken: '^9.0.0',
65+
swagger_jsdoc: '^6.2.0',
66+
swagger_ui_express: '^5.0.0',
67+
jest: '^29.0.0',
68+
ts_jest: '^29.0.0',
69+
mongodb_memory_server: '^10.0.0',
70+
supertest: '^7.0.0',
71+
types_bcrypt: '^5.0.0',
72+
types_jsonwebtoken: '^9.0.0',
73+
types_supertest: '^6.0.0',
6374
};
6475

6576
export function resolveDeps(input: StackInput): StackDeps {
@@ -73,6 +84,10 @@ export function resolveDeps(input: StackInput): StackDeps {
7384
deps.uuid = versions.uuid;
7485
deps.winston = versions.winston;
7586
deps['winston-daily-rotate-file'] = versions.winston_daily_rotate_file;
87+
deps.bcrypt = versions.bcrypt;
88+
deps.jsonwebtoken = versions.jsonwebtoken;
89+
deps['swagger-jsdoc'] = versions.swagger_jsdoc;
90+
deps['swagger-ui-express'] = versions.swagger_ui_express;
7691

7792
if (input.framework === 'express') {
7893
deps.express = versions.express;
@@ -138,11 +153,21 @@ export function resolveDeps(input: StackInput): StackDeps {
138153
dev['@types/hpp'] = '^0.2.5';
139154
dev['@types/compression'] = '^1.7.5';
140155
dev['@types/morgan'] = '^1.9.9';
156+
dev.jest = versions.jest;
157+
dev['ts-jest'] = versions.ts_jest;
158+
dev['mongodb-memory-server'] = versions.mongodb_memory_server;
159+
dev.supertest = versions.supertest;
160+
dev['@types/bcrypt'] = versions.types_bcrypt;
161+
dev['@types/jsonwebtoken'] = versions.types_jsonwebtoken;
162+
dev['@types/supertest'] = versions.types_supertest;
141163
}
142164

143165
scripts.build = 'rimraf dist && tsc';
144166
scripts.format = 'prettier --write "src/**/*.ts"';
145167
scripts.lint = 'eslint "src/**/*.ts" --fix';
168+
scripts.test = 'jest';
169+
scripts['test:watch'] = 'jest --watch';
170+
scripts['test:coverage'] = 'jest --coverage';
146171

147172
if (input.runtime === 'bun') {
148173
scripts.dev = 'bun run --watch src/server.ts';
@@ -183,3 +208,83 @@ export function resolveDeps(input: StackInput): StackDeps {
183208
scripts,
184209
};
185210
}
211+
212+
export function getFeatureDependencies(features: string[], _language: 'ts' | 'js') {
213+
const deps: Record<string, string> = {};
214+
const dev: Record<string, string> = {};
215+
const scripts: Record<string, string> = {};
216+
217+
if (features.includes('oauth')) {
218+
deps['passport'] = '^0.7.0';
219+
deps['passport-google-oauth20'] = '^2.0.0';
220+
deps['passport-github2'] = '^0.1.12';
221+
}
222+
223+
if (features.includes('clerk-auth')) {
224+
deps['@clerk/clerk-sdk-node'] = '^4.13.14';
225+
deps['@clerk/express'] = '^0.1.7';
226+
}
227+
228+
if (features.includes('graphql')) {
229+
deps['apollo-server-express'] = '^3.13.0';
230+
deps['graphql'] = '^16.8.1';
231+
deps['class-validator'] = '^0.14.1';
232+
deps['type-graphql'] = '^2.0.0-beta.6';
233+
}
234+
235+
if (features.includes('microservices')) {
236+
deps['kafkajs'] = '^2.2.4';
237+
deps['amqplib'] = '^0.10.4';
238+
deps['redis'] = '^4.6.13';
239+
deps['axios'] = '^1.6.7';
240+
}
241+
242+
if (features.includes('websocket')) {
243+
deps['socket.io'] = '^4.7.5';
244+
deps['socket.io-client'] = '^4.7.5';
245+
}
246+
247+
if (features.includes('message-queue')) {
248+
deps['amqplib'] = '^0.10.4';
249+
}
250+
251+
if (features.includes('s3-upload')) {
252+
deps['aws-sdk'] = '^2.1606.0';
253+
deps['multer'] = '^1.4.5-lts.1';
254+
deps['multer-s3'] = '^3.0.1';
255+
deps['uuid'] = '^9.0.1';
256+
}
257+
258+
if (features.includes('email')) {
259+
deps['@sendgrid/mail'] = '^8.1.3';
260+
deps['nodemailer'] = '^6.9.13';
261+
}
262+
263+
if (features.includes('payment-stripe')) {
264+
deps['stripe'] = '^15.7.0';
265+
}
266+
267+
if (features.includes('payment-razorpay')) {
268+
deps['razorpay'] = '^2.9.2';
269+
}
270+
271+
if (features.includes('admin-dashboard')) {
272+
deps['adminjs'] = '^7.1.0';
273+
deps['@adminjs/express'] = '^6.1.0';
274+
deps['@adminjs/mongoose'] = '^4.0.0';
275+
}
276+
277+
if (features.includes('migrations-ui')) {
278+
deps['migrate-mongo'] = '^11.0.0';
279+
}
280+
281+
if (features.includes('load-testing')) {
282+
dev['k6'] = '^0.52.0';
283+
}
284+
285+
return {
286+
dependencies: deps,
287+
devDependencies: dev,
288+
scripts,
289+
};
290+
}

packages/backforge-core/src/scaffold/normalize.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function ensureAbsolute(p: string): string {
1313
return path.isAbsolute(p) ? p : path.resolve(process.cwd(), p);
1414
}
1515

16+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1617
async function exists(p: string): Promise<boolean> {
1718
try {
1819
await fs.access(p);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<% if (features.includes('oauth')) { %>
2+
// OAuth2.0 Configuration (Google & GitHub)
3+
import passport from 'passport';
4+
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
5+
import { Strategy as GitHubStrategy } from 'passport-github2';
6+
7+
passport.use(new GoogleStrategy({
8+
clientID: process.env.GOOGLE_CLIENT_ID!,
9+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
10+
callbackURL: `${process.env.BASE_URL}/api/v1/auth/google/callback`
11+
}, async (accessToken, refreshToken, profile, done) => {
12+
try {
13+
// Find or create user logic here
14+
const user = { id: profile.id, email: profile.emails?.[0].value, name: profile.displayName };
15+
return done(null, user);
16+
} catch (error) {
17+
return done(error, null);
18+
}
19+
}));
20+
21+
passport.use(new GitHubStrategy({
22+
clientID: process.env.GITHUB_CLIENT_ID!,
23+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
24+
callbackURL: `${process.env.BASE_URL}/api/v1/auth/github/callback`
25+
}, async (accessToken, refreshToken, profile, done) => {
26+
try {
27+
// Find or create user logic here
28+
const user = { id: profile.id, email: profile.emails?.[0].value, name: profile.displayName };
29+
return done(null, user);
30+
} catch (error) {
31+
return done(error, null);
32+
}
33+
}));
34+
35+
export default passport;
36+
<% } %>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
FROM node:18-alpine AS builder
2+
3+
WORKDIR /app
4+
5+
COPY package*.json ./
6+
RUN npm ci --only=production
7+
8+
COPY . .
9+
RUN npm run build
10+
11+
FROM node:18-alpine AS runner
12+
13+
RUN apk add --no-cache tini
14+
15+
WORKDIR /app
16+
17+
COPY --from=builder /app/dist ./dist
18+
COPY --from=builder /app/node_modules ./node_modules
19+
COPY --from=builder /app/package.json ./
20+
21+
ENV NODE_ENV=production
22+
23+
EXPOSE 3000
24+
25+
USER node
26+
27+
ENTRYPOINT ["/sbin/tini", "--"]
28+
CMD ["node", "dist/server.js"]
29+
30+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
31+
CMD node dist/healthcheck.js || exit 1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM node:18-alpine
2+
3+
WORKDIR /app
4+
5+
COPY package*.json ./
6+
RUN npm install
7+
8+
COPY . .
9+
10+
EXPOSE 3000
11+
12+
CMD ["npm", "run", "dev"]

0 commit comments

Comments
 (0)