create-nara 1.0.25 → 1.0.26

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.

Potentially problematic release.


This version of create-nara might be problematic. Click here for more details.

package/dist/cli.js CHANGED
@@ -29,16 +29,7 @@ export async function main() {
29
29
  });
30
30
  if (!mode)
31
31
  process.exit(1);
32
- const { features } = await prompts({
33
- type: 'multiselect',
34
- name: 'features',
35
- message: 'Select features:',
36
- choices: [
37
- { title: 'Authentication', value: 'auth', selected: true },
38
- { title: 'Database (SQLite)', value: 'db', selected: true },
39
- { title: 'File uploads', value: 'uploads', selected: false }
40
- ]
41
- });
32
+ const features = ['auth', 'db', 'uploads'];
42
33
  const targetDir = path.resolve(process.cwd(), projectName);
43
34
  console.log(pc.dim(`\nCreating project in ${targetDir}...\n`));
44
35
  await setupProject({ projectName, targetDir, mode, features: features || [] });
package/dist/template.js CHANGED
@@ -60,25 +60,7 @@ export async function setupProject(options) {
60
60
  if (features.includes('uploads')) {
61
61
  fs.mkdirSync(path.join(targetDir, 'uploads'), { recursive: true });
62
62
  }
63
- // 6. Modify server.ts to import feature routes
64
- const serverPath = path.join(targetDir, 'server.ts');
65
- if (fs.existsSync(serverPath)) {
66
- let serverContent = fs.readFileSync(serverPath, 'utf8');
67
- // Add auth routes import and registration
68
- if (features.includes('auth')) {
69
- // Add import after web routes import
70
- serverContent = serverContent.replace("import { registerRoutes } from './routes/web.js';", "import { registerRoutes } from './routes/web.js';\nimport { registerAuthRoutes } from './routes/auth.js';");
71
- // Add registration before app.start()
72
- serverContent = serverContent.replace('app.start();', 'registerAuthRoutes(app);\napp.start();');
73
- }
74
- // Add upload routes import and registration
75
- if (features.includes('uploads')) {
76
- serverContent = serverContent.replace("import { registerRoutes } from './routes/web.js';", "import { registerRoutes } from './routes/web.js';\nimport { registerUploadRoutes } from './routes/uploads.js';");
77
- serverContent = serverContent.replace('app.start();', 'registerUploadRoutes(app);\napp.start();');
78
- }
79
- fs.writeFileSync(serverPath, serverContent);
80
- }
81
- // 7. Generate package.json (dynamic content)
63
+ // 6. Generate package.json (dynamic content)
82
64
  const pkg = createPackageJson(projectName, mode, features);
83
65
  fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkg, null, 2));
84
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nara",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "CLI to scaffold NARA projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -169,7 +169,7 @@ export function rateLimit(options: RateLimitOptions = {}): NaraMiddleware {
169
169
  res.setHeader('Retry-After', String(Math.ceil(resetMs / 1000)));
170
170
  }
171
171
 
172
- return jsonError(res, message, 429, 'TOO_MANY_REQUESTS');
172
+ return jsonError(res, message, 429);
173
173
  }
174
174
 
175
175
  // Record this request
@@ -1,6 +1,17 @@
1
1
  import type { NaraApp } from '@nara-web/core';
2
+ import { AuthController } from '../app/controllers/AuthController.js';
3
+ import { ProfileController } from '../app/controllers/ProfileController.js';
4
+ import { UserController } from '../app/controllers/UserController.js';
5
+ import { UploadController } from '../app/controllers/UploadController.js';
6
+ import { authMiddleware, webAuthMiddleware } from '../app/middlewares/auth.js';
7
+ import { wrapHandler } from '../app/utils/route-helper.js';
2
8
 
3
9
  export function registerRoutes(app: NaraApp) {
10
+ const auth = new AuthController();
11
+ const profile = new ProfileController();
12
+ const users = new UserController();
13
+ const upload = new UploadController();
14
+
4
15
  app.get('/', (req, res) => {
5
16
  res.json({ message: 'Welcome to NARA API' });
6
17
  });
@@ -8,4 +19,29 @@ export function registerRoutes(app: NaraApp) {
8
19
  app.get('/health', (req, res) => {
9
20
  res.json({ status: 'ok' });
10
21
  });
22
+
23
+ // --- API Routes ---
24
+
25
+ // Auth
26
+ app.post('/api/auth/login', wrapHandler((req, res) => auth.login(req, res)));
27
+ app.post('/api/auth/register', wrapHandler((req, res) => auth.register(req, res)));
28
+ app.post('/api/auth/logout', wrapHandler((req, res) => auth.logout(req, res)));
29
+ app.post('/api/auth/forgot-password', wrapHandler((req, res) => auth.forgotPassword(req, res)));
30
+ app.post('/api/auth/reset-password', wrapHandler((req, res) => auth.resetPassword(req, res)));
31
+ app.get('/api/auth/me', authMiddleware as any, wrapHandler((req, res) => auth.me(req, res)));
32
+
33
+ // Profile
34
+ app.post('/api/profile/update', webAuthMiddleware as any, wrapHandler((req, res) => profile.update(req, res)));
35
+ app.post('/api/profile/password', webAuthMiddleware as any, wrapHandler((req, res) => profile.changePassword(req, res)));
36
+ app.post('/api/profile/avatar', webAuthMiddleware as any, wrapHandler((req, res) => profile.uploadAvatar(req, res)));
37
+
38
+ // Users
39
+ app.get('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.index(req, res)));
40
+ app.post('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.store(req, res)));
41
+ app.put('/api/users/:id', webAuthMiddleware as any, wrapHandler((req, res) => users.update(req, res)));
42
+ app.delete('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.destroy(req, res)));
43
+
44
+ // Uploads
45
+ app.post('/api/uploads', wrapHandler((req, res) => upload.upload(req, res)));
46
+ app.delete('/api/uploads/:filename', wrapHandler((req, res) => upload.delete(req, res)));
11
47
  }
@@ -10,17 +10,19 @@ export function wrapHandler(handler: NaraHandler): NaraHandler {
10
10
  await handler(req, res);
11
11
  } catch (error: any) {
12
12
  if (error instanceof ValidationError) {
13
- return jsonValidationError(res, error.errors);
13
+ jsonValidationError(res, error.errors);
14
+ return;
14
15
  }
15
16
 
16
17
  // Handle other HttpErrors
17
18
  if (error.statusCode) {
18
- return jsonError(res, error.message, error.statusCode);
19
+ jsonError(res, error.message, error.statusCode);
20
+ return;
19
21
  }
20
22
 
21
23
  // Unknown error
22
24
  console.error('[Unhandled Error]:', error);
23
- return jsonError(res, 'Internal server error', 500);
25
+ jsonError(res, 'Internal server error', 500);
24
26
  }
25
27
  };
26
28
  }
@@ -1,61 +1,74 @@
1
1
  import type { NaraApp } from '@nara-web/core';
2
- import { webAuthMiddleware, guestMiddleware } from '../app/middlewares/auth.js';
3
- import fs from 'fs';
4
- import path from 'path';
2
+ import { AuthController } from '../app/controllers/AuthController.js';
3
+ import { ProfileController } from '../app/controllers/ProfileController.js';
4
+ import { UserController } from '../app/controllers/UserController.js';
5
+ import { UploadController } from '../app/controllers/UploadController.js';
6
+ import { authMiddleware, webAuthMiddleware, guestMiddleware } from '../app/middlewares/auth.js';
7
+ import { wrapHandler } from '../app/utils/route-helper.js';
5
8
 
6
9
  export function registerRoutes(app: NaraApp) {
7
- // Serve uploaded files
8
- app.get('/uploads/*', (req, res) => {
9
- const requestPath = req.path?.replace('/uploads/', '') || '';
10
- const filePath = path.join(process.cwd(), 'uploads', requestPath);
11
-
12
- if (fs.existsSync(filePath)) {
13
- const ext = path.extname(filePath).toLowerCase();
14
- const mimeTypes: Record<string, string> = {
15
- '.webp': 'image/webp',
16
- '.png': 'image/png',
17
- '.jpg': 'image/jpeg',
18
- '.jpeg': 'image/jpeg',
19
- '.gif': 'image/gif',
20
- };
21
- res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
22
- res.setHeader('Cache-Control', 'public, max-age=31536000');
23
- res.send(fs.readFileSync(filePath));
24
- } else {
25
- res.status(404).send('Not found');
26
- }
27
- });
28
-
29
- // Public routes
10
+ const auth = new AuthController();
11
+ const profile = new ProfileController();
12
+ const users = new UserController();
13
+ const upload = new UploadController();
14
+
15
+ // --- Page Routes (Inertia) ---
16
+
17
+ // Public
30
18
  app.get('/', (req, res) => {
31
- res.inertia?.('landing', {
32
- title: 'Welcome to NARA'
19
+ return res.inertia('landing', {
20
+ title: 'Welcome to NARA'
33
21
  });
34
22
  });
35
23
 
36
- // Guest only routes (redirect to dashboard if logged in)
24
+ // Guest only
37
25
  app.get('/login', guestMiddleware as any, (req, res) => {
38
- res.inertia?.('auth/login');
26
+ return res.inertia('auth/login');
39
27
  });
40
-
41
28
  app.get('/register', guestMiddleware as any, (req, res) => {
42
- res.inertia?.('auth/register');
29
+ return res.inertia('auth/register');
43
30
  });
44
-
45
31
  app.get('/forgot-password', guestMiddleware as any, (req, res) => {
46
- res.inertia?.('auth/forgot-password');
32
+ return res.inertia('auth/forgot-password');
33
+ });
34
+ app.get('/reset-password/:token', guestMiddleware as any, (req, res) => {
35
+ return res.inertia('auth/reset-password', { token: req.params.token });
47
36
  });
48
37
 
49
- // Protected routes (redirect to login if not authenticated)
38
+ // Protected
50
39
  app.get('/dashboard', webAuthMiddleware as any, (req, res) => {
51
- res.inertia?.('dashboard');
40
+ return res.inertia('dashboard');
52
41
  });
53
-
54
42
  app.get('/users', webAuthMiddleware as any, (req, res) => {
55
- res.inertia?.('users');
43
+ return res.inertia('users');
56
44
  });
57
-
58
45
  app.get('/profile', webAuthMiddleware as any, (req, res) => {
59
- res.inertia?.('profile');
46
+ return res.inertia('profile');
60
47
  });
48
+
49
+
50
+ // --- API Routes ---
51
+
52
+ // Auth
53
+ app.post('/api/auth/login', wrapHandler((req, res) => auth.login(req, res)));
54
+ app.post('/api/auth/register', wrapHandler((req, res) => auth.register(req, res)));
55
+ app.post('/api/auth/logout', wrapHandler((req, res) => auth.logout(req, res)));
56
+ app.post('/api/auth/forgot-password', wrapHandler((req, res) => auth.forgotPassword(req, res)));
57
+ app.post('/api/auth/reset-password', wrapHandler((req, res) => auth.resetPassword(req, res)));
58
+ app.get('/api/auth/me', authMiddleware as any, wrapHandler((req, res) => auth.me(req, res)));
59
+
60
+ // Profile
61
+ app.post('/api/profile/update', webAuthMiddleware as any, wrapHandler((req, res) => profile.update(req, res)));
62
+ app.post('/api/profile/password', webAuthMiddleware as any, wrapHandler((req, res) => profile.changePassword(req, res)));
63
+ app.post('/api/profile/avatar', webAuthMiddleware as any, wrapHandler((req, res) => profile.uploadAvatar(req, res)));
64
+
65
+ // Users
66
+ app.get('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.index(req, res)));
67
+ app.post('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.store(req, res)));
68
+ app.put('/api/users/:id', webAuthMiddleware as any, wrapHandler((req, res) => users.update(req, res)));
69
+ app.delete('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.destroy(req, res)));
70
+
71
+ // Uploads
72
+ app.post('/api/uploads', wrapHandler((req, res) => upload.upload(req, res)));
73
+ app.delete('/api/uploads/:filename', wrapHandler((req, res) => upload.delete(req, res)));
61
74
  }
@@ -9,9 +9,11 @@ const app = createApp({
9
9
  });
10
10
 
11
11
  // Global error handler
12
- app.getServer().set_error_handler((req, res, error) => {
12
+ app.getServer().set_error_handler((req, res, error: any) => {
13
13
  console.error('[Server Error]:', error);
14
- return jsonError(res, error.message || 'Internal server error', error.statusCode || 500);
14
+ const message = error.message || 'Internal server error';
15
+ const status = error.statusCode || 500;
16
+ return jsonError(res, message, status);
15
17
  });
16
18
 
17
19
  registerRoutes(app);
@@ -1,17 +1,74 @@
1
1
  import type { NaraApp } from '@nara-web/core';
2
+ import { AuthController } from '../app/controllers/AuthController.js';
3
+ import { ProfileController } from '../app/controllers/ProfileController.js';
4
+ import { UserController } from '../app/controllers/UserController.js';
5
+ import { UploadController } from '../app/controllers/UploadController.js';
6
+ import { authMiddleware, webAuthMiddleware, guestMiddleware } from '../app/middlewares/auth.js';
7
+ import { wrapHandler } from '../app/utils/route-helper.js';
2
8
 
3
9
  export function registerRoutes(app: NaraApp) {
10
+ const auth = new AuthController();
11
+ const profile = new ProfileController();
12
+ const users = new UserController();
13
+ const upload = new UploadController();
14
+
15
+ // --- Page Routes (Inertia) ---
16
+
17
+ // Public
4
18
  app.get('/', (req, res) => {
5
19
  return res.inertia('landing', {
6
20
  title: 'Welcome to NARA'
7
21
  });
8
22
  });
9
23
 
10
- app.get('/dashboard', (req, res) => {
11
- return res.inertia('dashboard');
24
+ // Guest only
25
+ app.get('/login', guestMiddleware as any, (req, res) => {
26
+ return res.inertia('auth/login');
27
+ });
28
+ app.get('/register', guestMiddleware as any, (req, res) => {
29
+ return res.inertia('auth/register');
30
+ });
31
+ app.get('/forgot-password', guestMiddleware as any, (req, res) => {
32
+ return res.inertia('auth/forgot-password');
33
+ });
34
+ app.get('/reset-password/:token', guestMiddleware as any, (req, res) => {
35
+ return res.inertia('auth/reset-password', { token: req.params.token });
12
36
  });
13
37
 
14
- app.get('/login', (req, res) => {
15
- return res.inertia('auth/login');
38
+ // Protected
39
+ app.get('/dashboard', webAuthMiddleware as any, (req, res) => {
40
+ return res.inertia('dashboard');
16
41
  });
42
+ app.get('/users', webAuthMiddleware as any, (req, res) => {
43
+ return res.inertia('users');
44
+ });
45
+ app.get('/profile', webAuthMiddleware as any, (req, res) => {
46
+ return res.inertia('profile');
47
+ });
48
+
49
+
50
+ // --- API Routes ---
51
+
52
+ // Auth
53
+ app.post('/api/auth/login', wrapHandler((req, res) => auth.login(req, res)));
54
+ app.post('/api/auth/register', wrapHandler((req, res) => auth.register(req, res)));
55
+ app.post('/api/auth/logout', wrapHandler((req, res) => auth.logout(req, res)));
56
+ app.post('/api/auth/forgot-password', wrapHandler((req, res) => auth.forgotPassword(req, res)));
57
+ app.post('/api/auth/reset-password', wrapHandler((req, res) => auth.resetPassword(req, res)));
58
+ app.get('/api/auth/me', authMiddleware as any, wrapHandler((req, res) => auth.me(req, res)));
59
+
60
+ // Profile
61
+ app.post('/api/profile/update', webAuthMiddleware as any, wrapHandler((req, res) => profile.update(req, res)));
62
+ app.post('/api/profile/password', webAuthMiddleware as any, wrapHandler((req, res) => profile.changePassword(req, res)));
63
+ app.post('/api/profile/avatar', webAuthMiddleware as any, wrapHandler((req, res) => profile.uploadAvatar(req, res)));
64
+
65
+ // Users
66
+ app.get('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.index(req, res)));
67
+ app.post('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.store(req, res)));
68
+ app.put('/api/users/:id', webAuthMiddleware as any, wrapHandler((req, res) => users.update(req, res)));
69
+ app.delete('/api/users', webAuthMiddleware as any, wrapHandler((req, res) => users.destroy(req, res)));
70
+
71
+ // Uploads
72
+ app.post('/api/uploads', wrapHandler((req, res) => upload.upload(req, res)));
73
+ app.delete('/api/uploads/:filename', wrapHandler((req, res) => upload.delete(req, res)));
17
74
  }
@@ -1,60 +0,0 @@
1
- import type { NaraApp } from '@nara-web/core';
2
- import { AuthController } from '../app/controllers/AuthController.js';
3
- import { ProfileController } from '../app/controllers/ProfileController.js';
4
- import { UserController } from '../app/controllers/UserController.js';
5
- import { authMiddleware, webAuthMiddleware } from '../app/middlewares/auth.js';
6
- import { wrapHandler } from '../app/utils/route-helper.js';
7
-
8
- export function registerAuthRoutes(app: NaraApp) {
9
- const auth = new AuthController();
10
- const profile = new ProfileController();
11
- const users = new UserController();
12
-
13
- // ===== Auth Routes =====
14
- // Public routes
15
- app.post('/api/auth/login', wrapHandler(async (req, res) => {
16
- await auth.login(req, res);
17
- }));
18
- app.post('/api/auth/register', wrapHandler(async (req, res) => {
19
- await auth.register(req, res);
20
- }));
21
- app.post('/api/auth/logout', wrapHandler(async (req, res) => {
22
- await auth.logout(req, res);
23
- }));
24
- app.post('/api/auth/forgot-password', wrapHandler(async (req, res) => {
25
- await auth.forgotPassword(req, res);
26
- }));
27
- app.post('/api/auth/reset-password', wrapHandler(async (req, res) => {
28
- await auth.resetPassword(req, res);
29
- }));
30
-
31
- // Protected API routes (Bearer token)
32
- app.get('/api/auth/me', authMiddleware as any, wrapHandler(async (req, res) => {
33
- await auth.me(req, res);
34
- }));
35
-
36
- // ===== Profile Routes (cookie-based auth) =====
37
- app.post('/api/profile/update', webAuthMiddleware as any, wrapHandler(async (req, res) => {
38
- await profile.update(req, res);
39
- }));
40
- app.post('/api/profile/password', webAuthMiddleware as any, wrapHandler(async (req, res) => {
41
- await profile.changePassword(req, res);
42
- }));
43
- app.post('/api/profile/avatar', webAuthMiddleware as any, wrapHandler(async (req, res) => {
44
- await profile.uploadAvatar(req, res);
45
- }));
46
-
47
- // ===== User Management Routes (cookie-based auth, admin only) =====
48
- app.get('/api/users', webAuthMiddleware as any, wrapHandler(async (req, res) => {
49
- await users.index(req, res);
50
- }));
51
- app.post('/api/users', webAuthMiddleware as any, wrapHandler(async (req, res) => {
52
- await users.store(req, res);
53
- }));
54
- app.put('/api/users/:id', webAuthMiddleware as any, wrapHandler(async (req, res) => {
55
- await users.update(req, res);
56
- }));
57
- app.delete('/api/users', webAuthMiddleware as any, wrapHandler(async (req, res) => {
58
- await users.destroy(req, res);
59
- }));
60
- }
@@ -1,14 +0,0 @@
1
- import type { NaraApp } from '@nara-web/core';
2
- import { UploadController } from '../app/controllers/UploadController.js';
3
- import { wrapHandler } from '../app/utils/route-helper.js';
4
-
5
- export function registerUploadRoutes(app: NaraApp) {
6
- const upload = new UploadController();
7
-
8
- app.post('/api/uploads', wrapHandler(async (req, res) => {
9
- await upload.upload(req, res);
10
- }));
11
- app.delete('/api/uploads/:filename', wrapHandler(async (req, res) => {
12
- await upload.delete(req, res);
13
- }));
14
- }