create-arktos 1.0.0

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 ADDED
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ const TEMPLATE_DIR = path.join(__dirname, '../src');
8
+ const ROOT_DIR = path.join(__dirname, '..');
9
+
10
+ function createProject(projectName) {
11
+ console.log(`🚀 Creating Arktos project: ${projectName}`);
12
+ console.log();
13
+
14
+ // Validate project name
15
+ if (!projectName || !/^[a-zA-Z0-9-_]+$/.test(projectName)) {
16
+ console.error('❌ Invalid project name. Use only letters, numbers, hyphens, and underscores.');
17
+ process.exit(1);
18
+ }
19
+
20
+ // Check if directory already exists
21
+ if (fs.existsSync(projectName)) {
22
+ console.error(`❌ Directory '${projectName}' already exists.`);
23
+ process.exit(1);
24
+ }
25
+
26
+ try {
27
+ // Create project directory
28
+ console.log('📁 Creating project directory...');
29
+ fs.mkdirSync(projectName);
30
+
31
+ // Copy template files
32
+ console.log('📋 Copying template files...');
33
+ copyDir(TEMPLATE_DIR, projectName);
34
+
35
+ // Copy template config files
36
+ console.log('📝 Copying configuration files...');
37
+ copyTemplateFiles(projectName);
38
+
39
+ // Copy and update package.json
40
+ console.log('🔧 Updating project configuration...');
41
+ const templatePackageJsonPath = path.join(ROOT_DIR, 'template.package.json');
42
+ const targetPackageJsonPath = path.join(projectName, 'package.json');
43
+
44
+ if (fs.existsSync(templatePackageJsonPath)) {
45
+ const packageJson = JSON.parse(fs.readFileSync(templatePackageJsonPath, 'utf8'));
46
+
47
+ // Update project name
48
+ packageJson.name = projectName;
49
+
50
+ fs.writeFileSync(targetPackageJsonPath, JSON.stringify(packageJson, null, 2));
51
+ }
52
+
53
+ console.log('✅ Project created successfully!');
54
+ console.log();
55
+ console.log('🎯 Next steps:');
56
+ console.log(` cd ${projectName}`);
57
+ console.log(' npm install');
58
+ console.log(' cp .env.example .env');
59
+ console.log(' # Edit .env with your database and API keys');
60
+ console.log(' npx prisma migrate dev');
61
+ console.log(' npm run dev');
62
+ console.log();
63
+ console.log('📚 Documentation:');
64
+ console.log(' • Backend API: http://localhost:3001');
65
+ console.log(' • Setup your Neon database: https://neon.tech');
66
+ console.log(' • Setup Resend for emails: https://resend.com');
67
+ console.log(' • Deploy on Vercel: https://vercel.com');
68
+ console.log();
69
+ console.log('🔧 Available commands:');
70
+ console.log(' npm run dev - Start development server');
71
+ console.log(' npm run build - Build for production');
72
+ console.log(' npm run start - Start production server');
73
+ console.log(' npm run db:studio - Open Prisma Studio');
74
+ console.log();
75
+ console.log('Happy coding! 🎉');
76
+
77
+ } catch (error) {
78
+ console.error('❌ Error creating project:', error.message);
79
+
80
+ // Cleanup on error
81
+ try {
82
+ if (fs.existsSync(projectName)) {
83
+ fs.rmSync(projectName, { recursive: true, force: true });
84
+ }
85
+ } catch (cleanupError) {
86
+ console.error('❌ Error during cleanup:', cleanupError.message);
87
+ }
88
+
89
+ process.exit(1);
90
+ }
91
+ }
92
+
93
+ function copyDir(src, dest) {
94
+ // Create destination directory
95
+ if (!fs.existsSync(dest)) {
96
+ fs.mkdirSync(dest, { recursive: true });
97
+ }
98
+
99
+ // Read source directory
100
+ const entries = fs.readdirSync(src, { withFileTypes: true });
101
+
102
+ for (const entry of entries) {
103
+ const srcPath = path.join(src, entry.name);
104
+ const destPath = path.join(dest, entry.name);
105
+
106
+ if (entry.isDirectory()) {
107
+ // Recursively copy subdirectories
108
+ copyDir(srcPath, destPath);
109
+ } else {
110
+ // Copy files
111
+ fs.copyFileSync(srcPath, destPath);
112
+ }
113
+ }
114
+ }
115
+
116
+ function copyTemplateFiles(dest) {
117
+ // Create common config files
118
+ const configFiles = {
119
+ '.gitignore': `# Dependencies
120
+ node_modules/
121
+ npm-debug.log*
122
+ yarn-debug.log*
123
+ yarn-error.log*
124
+
125
+ # Environment variables
126
+ .env
127
+ .env.local
128
+ .env.development.local
129
+ .env.test.local
130
+ .env.production.local
131
+
132
+ # Build outputs
133
+ dist/
134
+ build/
135
+
136
+ # Logs
137
+ logs/
138
+ *.log
139
+
140
+ # Runtime data
141
+ pids
142
+ *.pid
143
+ *.seed
144
+ *.pid.lock
145
+
146
+ # Coverage directory used by tools like istanbul
147
+ coverage/
148
+
149
+ # OS generated files
150
+ .DS_Store
151
+ .DS_Store?
152
+ ._*
153
+ .Spotlight-V100
154
+ .Trashes
155
+ ehthumbs.db
156
+ Thumbs.db
157
+
158
+ # IDE files
159
+ .vscode/
160
+ .idea/
161
+ *.swp
162
+ *.swo
163
+ *~
164
+
165
+ # Prisma
166
+ prisma/migrations/dev.db*
167
+
168
+ # Temporary files
169
+ *.tmp
170
+ *.temp
171
+ .cache/
172
+
173
+ # Vercel
174
+ .vercel
175
+
176
+ # TypeScript
177
+ *.tsbuildinfo`,
178
+ '.prettierrc': `{
179
+ "semi": true,
180
+ "trailingComma": "es5",
181
+ "singleQuote": true,
182
+ "printWidth": 80,
183
+ "tabWidth": 2,
184
+ "useTabs": false,
185
+ "bracketSpacing": true,
186
+ "arrowParens": "avoid",
187
+ "endOfLine": "lf"
188
+ }`,
189
+ 'README.md': `# ${path.basename(dest)} API
190
+
191
+ A modern Node.js backend API built with Arktos boilerplate.
192
+
193
+ ## 🚀 Features
194
+
195
+ - **TypeScript**: Full TypeScript support with strict type checking
196
+ - **Express.js**: Fast, unopinionated web framework
197
+ - **Authentication**: Complete JWT-based authentication system
198
+ - **Database**: Prisma ORM with PostgreSQL (Neon serverless)
199
+ - **Email Service**: Resend integration for transactional emails
200
+ - **Security**: Helmet, CORS, rate limiting, and security middleware
201
+ - **Validation**: Zod-based request validation
202
+ - **Logging**: Winston logger with file rotation
203
+ - **Error Handling**: Comprehensive error handling and responses
204
+ - **API Documentation**: Structured API responses
205
+ - **Deployment Ready**: Vercel configuration included
206
+
207
+ ## 📋 Prerequisites
208
+
209
+ - Node.js 18+ and npm 8+
210
+ - PostgreSQL database (recommend [Neon](https://neon.tech/) for serverless)
211
+ - Resend account for email service
212
+
213
+ ## 🛠️ Installation
214
+
215
+ 1. **Install dependencies**
216
+ \`\`\`bash
217
+ npm install
218
+ \`\`\`
219
+
220
+ 2. **Environment Setup**
221
+ \`\`\`bash
222
+ cp .env.example .env
223
+ \`\`\`
224
+ Fill in your environment variables in \`.env\`
225
+
226
+ 3. **Database Setup**
227
+ \`\`\`bash
228
+ # Generate Prisma client
229
+ npx prisma generate
230
+
231
+ # Run database migrations
232
+ npx prisma migrate dev
233
+ \`\`\`
234
+
235
+ 4. **Start Development Server**
236
+ \`\`\`bash
237
+ npm run dev
238
+ \`\`\`
239
+
240
+ ## 🔧 Available Scripts
241
+
242
+ - \`npm run dev\` - Start development server with hot reload
243
+ - \`npm run build\` - Build for production
244
+ - \`npm start\` - Start production server
245
+ - \`npm run type-check\` - Run TypeScript type checking
246
+ - \`npm run lint\` - Run ESLint
247
+ - \`npm run db:studio\` - Open Prisma Studio
248
+
249
+ Happy coding! 🎉`
250
+ };
251
+
252
+ // Write config files
253
+ for (const [fileName, content] of Object.entries(configFiles)) {
254
+ fs.writeFileSync(path.join(dest, fileName), content);
255
+ }
256
+
257
+ // Copy template files from root
258
+ const templateFiles = ['.env.example', 'vercel.json', 'tsconfig.json', 'eslint.config.js'];
259
+
260
+ for (const templateFile of templateFiles) {
261
+ const srcPath = path.join(ROOT_DIR, templateFile);
262
+ const destPath = path.join(dest, templateFile);
263
+
264
+ if (fs.existsSync(srcPath)) {
265
+ fs.copyFileSync(srcPath, destPath);
266
+ }
267
+ }
268
+ }
269
+
270
+ function showHelp() {
271
+ console.log();
272
+ console.log('🏛️ Arktos - Modern Node.js Backend Boilerplate');
273
+ console.log();
274
+ console.log('Usage:');
275
+ console.log(' npx create-arktos <project-name>');
276
+ console.log();
277
+ console.log('Example:');
278
+ console.log(' npx create-arktos my-awesome-api');
279
+ console.log();
280
+ console.log('Features:');
281
+ console.log(' ✅ Express.js server with modern middleware');
282
+ console.log(' ✅ JWT authentication with refresh tokens');
283
+ console.log(' ✅ Prisma ORM with PostgreSQL (Neon)');
284
+ console.log(' ✅ Email service with Resend');
285
+ console.log(' ✅ User authentication and management');
286
+ console.log(' ✅ Login logging and security features');
287
+ console.log(' ✅ Rate limiting and security headers');
288
+ console.log(' ✅ Comprehensive error handling');
289
+ console.log(' ✅ Winston logging');
290
+ console.log(' ✅ ESLint and Prettier configuration');
291
+ console.log(' ✅ Vercel deployment ready');
292
+ console.log();
293
+ console.log('Options:');
294
+ console.log(' -h, --help Show this help message');
295
+ console.log(' -v, --version Show version');
296
+ console.log();
297
+ }
298
+
299
+ function showVersion() {
300
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
301
+ console.log(packageJson.version);
302
+ }
303
+
304
+ // Main CLI logic
305
+ const args = process.argv.slice(2);
306
+
307
+ if (args.length === 0 || args[0] === '-h' || args[0] === '--help') {
308
+ showHelp();
309
+ process.exit(0);
310
+ }
311
+
312
+ if (args[0] === '-v' || args[0] === '--version') {
313
+ showVersion();
314
+ process.exit(0);
315
+ }
316
+
317
+ const projectName = args[0];
318
+ createProject(projectName);
@@ -0,0 +1,79 @@
1
+ const js = require('@eslint/js');
2
+
3
+ module.exports = [
4
+ js.configs.recommended,
5
+ {
6
+ languageOptions: {
7
+ ecmaVersion: 2022,
8
+ sourceType: 'commonjs',
9
+ globals: {
10
+ console: 'readonly',
11
+ process: 'readonly',
12
+ Buffer: 'readonly',
13
+ __dirname: 'readonly',
14
+ __filename: 'readonly',
15
+ module: 'readonly',
16
+ require: 'readonly',
17
+ exports: 'readonly',
18
+ global: 'readonly',
19
+ setTimeout: 'readonly',
20
+ setInterval: 'readonly',
21
+ clearTimeout: 'readonly',
22
+ clearInterval: 'readonly',
23
+ },
24
+ },
25
+ rules: {
26
+ 'no-unused-vars': ['error', {
27
+ argsIgnorePattern: '^_',
28
+ varsIgnorePattern: '^_'
29
+ }],
30
+ 'no-console': ['warn', {
31
+ allow: ['warn', 'error']
32
+ }],
33
+ 'prefer-const': 'error',
34
+ 'no-var': 'error',
35
+ 'eqeqeq': ['error', 'always'],
36
+ 'curly': ['error', 'all'],
37
+ 'brace-style': ['error', '1tbs'],
38
+ 'comma-dangle': ['error', 'always-multiline'],
39
+ 'indent': ['error', 2],
40
+ 'quotes': ['error', 'single'],
41
+ 'semi': ['error', 'always'],
42
+ 'no-trailing-spaces': 'error',
43
+ 'no-multiple-empty-lines': ['error', {
44
+ max: 2,
45
+ maxEOF: 1
46
+ }],
47
+ 'object-curly-spacing': ['error', 'always'],
48
+ 'array-bracket-spacing': ['error', 'never'],
49
+ 'key-spacing': ['error', {
50
+ beforeColon: false,
51
+ afterColon: true
52
+ }],
53
+ 'comma-spacing': ['error', {
54
+ before: false,
55
+ after: true
56
+ }],
57
+ 'no-undef': 'error',
58
+ 'no-redeclare': 'error',
59
+ 'no-dupe-keys': 'error',
60
+ 'no-unreachable': 'error',
61
+ 'valid-typeof': 'error',
62
+ },
63
+ },
64
+ {
65
+ files: ['**/*.test.js', '**/*.spec.js'],
66
+ languageOptions: {
67
+ globals: {
68
+ describe: 'readonly',
69
+ it: 'readonly',
70
+ test: 'readonly',
71
+ expect: 'readonly',
72
+ beforeEach: 'readonly',
73
+ afterEach: 'readonly',
74
+ before: 'readonly',
75
+ after: 'readonly',
76
+ },
77
+ },
78
+ },
79
+ ];
package/package.json ADDED
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "create-arktos",
3
+ "version": "1.0.0",
4
+ "description": "🚀 A modern Node.js backend boilerplate with TypeScript, Express, JWT authentication, Prisma ORM, PostgreSQL, and Resend email service. Includes complete authentication flow, security middleware, and database management.",
5
+ "main": "bin/cli.js",
6
+ "bin": {
7
+ "create-arktos": "./bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "src/",
12
+ "prisma/",
13
+ "template.package.json",
14
+ ".env.example",
15
+ "vercel.json",
16
+ "tsconfig.json",
17
+ "eslint.config.js",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "start": "node bin/cli.js",
24
+ "dev": "ts-node --esm bin/cli.js",
25
+ "test": "echo \"Error: no test specified\" && exit 1",
26
+ "lint": "eslint . --ext .ts,.js",
27
+ "lint:fix": "eslint . --ext .ts,.js --fix",
28
+ "format": "prettier --write .",
29
+ "format:check": "prettier --check .",
30
+ "typecheck": "tsc --noEmit",
31
+ "prepublishOnly": "echo 'Publishing arktos CLI tool...'",
32
+ "clean": "rm -rf dist"
33
+ },
34
+ "keywords": [
35
+ "nodejs",
36
+ "typescript",
37
+ "express",
38
+ "jwt",
39
+ "authentication",
40
+ "prisma",
41
+ "orm",
42
+ "postgresql",
43
+ "database",
44
+ "resend",
45
+ "email",
46
+ "auth",
47
+ "security",
48
+ "middleware",
49
+ "boilerplate",
50
+ "template",
51
+ "generator",
52
+ "cli",
53
+ "backend",
54
+ "api",
55
+ "rest",
56
+ "starter",
57
+ "scaffold"
58
+ ],
59
+ "author": {
60
+ "name": "Zafer Gök",
61
+ "email": "gok.zaferr@gmail.com",
62
+ "url": "https://github.com/zzafergok"
63
+ },
64
+ "license": "MIT",
65
+ "engines": {
66
+ "node": ">=18.0.0",
67
+ "npm": ">=8.0.0"
68
+ },
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "git+https://github.com/zzafergok/arktos.git"
72
+ },
73
+ "bugs": {
74
+ "url": "https://github.com/zzafergok/arktos/issues"
75
+ },
76
+ "homepage": "https://github.com/zzafergok/arktos#readme",
77
+ "funding": {
78
+ "type": "github",
79
+ "url": "https://github.com/sponsors/zzafergok"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public",
83
+ "registry": "https://registry.npmjs.org/"
84
+ },
85
+ "dependencies": {
86
+ "chalk": "^5.3.0",
87
+ "commander": "^12.0.0",
88
+ "inquirer": "^10.0.0",
89
+ "ora": "^8.0.1",
90
+ "fs-extra": "^11.2.0"
91
+ },
92
+ "devDependencies": {
93
+ "@types/fs-extra": "^11.0.4",
94
+ "@types/inquirer": "^9.0.7",
95
+ "@types/node": "^22.0.0",
96
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
97
+ "@typescript-eslint/parser": "^8.0.0",
98
+ "eslint": "^9.0.0",
99
+ "eslint-config-prettier": "^9.0.0",
100
+ "eslint-plugin-prettier": "^5.0.0",
101
+ "prettier": "^3.3.0",
102
+ "ts-node": "^10.9.2",
103
+ "typescript": "^5.5.0"
104
+ },
105
+ "peerDependencies": {
106
+ "typescript": ">=4.7.0"
107
+ }
108
+ }
@@ -0,0 +1,172 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "postgresql"
7
+ url = env("DATABASE_URL")
8
+ directUrl = env("DIRECT_URL")
9
+ }
10
+
11
+ enum Role {
12
+ USER
13
+ ADMIN
14
+ MODERATOR
15
+ }
16
+
17
+ enum LoginType {
18
+ EMAIL
19
+ GOOGLE
20
+ GITHUB
21
+ }
22
+
23
+ enum EmailVerificationStatus {
24
+ PENDING
25
+ VERIFIED
26
+ EXPIRED
27
+ }
28
+
29
+ model User {
30
+ id String @id @default(cuid())
31
+ email String @unique
32
+ username String? @unique
33
+ firstName String?
34
+ lastName String?
35
+ avatar String?
36
+ password String?
37
+ role Role @default(USER)
38
+ isActive Boolean @default(true)
39
+ isEmailVerified Boolean @default(false)
40
+ emailVerifiedAt DateTime?
41
+ lastLoginAt DateTime?
42
+ createdAt DateTime @default(now())
43
+ updatedAt DateTime @updatedAt
44
+
45
+ // Relations
46
+ loginLogs LoginLog[]
47
+ emailVerifications EmailVerification[]
48
+ refreshTokens RefreshToken[]
49
+ passwordResets PasswordReset[]
50
+
51
+ @@map("users")
52
+ }
53
+
54
+ model LoginLog {
55
+ id String @id @default(cuid())
56
+ userId String
57
+ loginType LoginType @default(EMAIL)
58
+ ipAddress String?
59
+ userAgent String?
60
+ location String?
61
+ isSuccess Boolean @default(true)
62
+ failReason String?
63
+ createdAt DateTime @default(now())
64
+
65
+ // Relations
66
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
67
+
68
+ @@map("login_logs")
69
+ }
70
+
71
+ model EmailVerification {
72
+ id String @id @default(cuid())
73
+ userId String
74
+ token String @unique
75
+ email String
76
+ status EmailVerificationStatus @default(PENDING)
77
+ expiresAt DateTime
78
+ createdAt DateTime @default(now())
79
+
80
+ // Relations
81
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
82
+
83
+ @@map("email_verifications")
84
+ }
85
+
86
+ model PasswordReset {
87
+ id String @id @default(cuid())
88
+ userId String
89
+ token String @unique
90
+ isUsed Boolean @default(false)
91
+ expiresAt DateTime
92
+ createdAt DateTime @default(now())
93
+
94
+ // Relations
95
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
96
+
97
+ @@map("password_resets")
98
+ }
99
+
100
+ model RefreshToken {
101
+ id String @id @default(cuid())
102
+ userId String
103
+ token String @unique
104
+ isRevoked Boolean @default(false)
105
+ expiresAt DateTime
106
+ createdAt DateTime @default(now())
107
+
108
+ // Relations
109
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
110
+
111
+ @@map("refresh_tokens")
112
+ }
113
+
114
+ model Booking {
115
+ id String @id @default(cuid())
116
+ userId String
117
+ title String
118
+ description String?
119
+ startDate DateTime
120
+ endDate DateTime
121
+ status String @default("PENDING")
122
+ totalAmount Decimal? @db.Decimal(10, 2)
123
+ createdAt DateTime @default(now())
124
+ updatedAt DateTime @updatedAt
125
+
126
+ @@map("bookings")
127
+ }
128
+
129
+ model Product {
130
+ id String @id @default(cuid())
131
+ name String
132
+ description String?
133
+ price Decimal @db.Decimal(10, 2)
134
+ category String?
135
+ isActive Boolean @default(true)
136
+ stock Int @default(0)
137
+ images String[]
138
+ createdAt DateTime @default(now())
139
+ updatedAt DateTime @updatedAt
140
+
141
+ @@map("products")
142
+ }
143
+
144
+ model Blog {
145
+ id String @id @default(cuid())
146
+ title String
147
+ slug String @unique
148
+ content String
149
+ excerpt String?
150
+ isPublished Boolean @default(false)
151
+ authorId String?
152
+ tags String[]
153
+ createdAt DateTime @default(now())
154
+ updatedAt DateTime @updatedAt
155
+
156
+ @@map("blogs")
157
+ }
158
+
159
+ model Payment {
160
+ id String @id @default(cuid())
161
+ userId String?
162
+ amount Decimal @db.Decimal(10, 2)
163
+ currency String @default("USD")
164
+ status String @default("PENDING")
165
+ paymentMethod String?
166
+ transactionId String? @unique
167
+ metadata Json?
168
+ createdAt DateTime @default(now())
169
+ updatedAt DateTime @updatedAt
170
+
171
+ @@map("payments")
172
+ }