chadstart 1.0.3 → 1.0.4

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.
@@ -310,10 +310,10 @@ function _apiKeyPermGuard(operation, entity) {
310
310
  return (req, res, next) => {
311
311
  if (!req._apiKeyPermissions) return next();
312
312
  const { operations, entities: keyEntities } = req._apiKeyPermissions;
313
- if (operations && operations.length > 0 && !operations.includes(operation)) {
313
+ if (operations?.length && !operations.includes(operation)) {
314
314
  return res.status(403).json({ error: 'API key does not have permission for this operation' });
315
315
  }
316
- if (keyEntities && keyEntities.length > 0 && !keyEntities.includes(entity.slug)) {
316
+ if (keyEntities?.length && !keyEntities.includes(entity.slug)) {
317
317
  return res.status(403).json({ error: 'API key does not have access to this entity' });
318
318
  }
319
319
  next();
package/core/auth.js CHANGED
@@ -16,6 +16,7 @@ const crypto = require('crypto');
16
16
  const jwt = require('jsonwebtoken');
17
17
  const bcrypt = require('bcryptjs');
18
18
  const db = require('./db');
19
+ const { q: _q, DB_ENGINE: _DB_ENGINE } = db;
19
20
  const logger = require('../utils/logger');
20
21
 
21
22
  const API_KEY_PREFIX = 'cs_';
@@ -29,10 +30,6 @@ const JWT_SECRET = process.env.JWT_SECRET || process.env.TOKEN_SECRET_KEY || (()
29
30
  const JWT_EXPIRES = process.env.JWT_EXPIRES || '7d';
30
31
  const BCRYPT_ROUNDS = 10;
31
32
 
32
- // Quote an identifier for the current database engine (mirrors db.js helper)
33
- const _DB_ENGINE = (process.env.DB_ENGINE || 'sqlite').toLowerCase();
34
- function _q(name) { return _DB_ENGINE === 'mysql' ? `\`${name}\`` : `"${name}"`; }
35
-
36
33
  // Column types for the API keys table (must be indexable in all engines)
37
34
  const _ID_T = _DB_ENGINE === 'mysql' ? 'VARCHAR(36)' : 'TEXT';
38
35
  const _HASH_T = _DB_ENGINE === 'mysql' ? 'VARCHAR(64)' : 'TEXT';
package/core/db.js CHANGED
@@ -552,6 +552,7 @@ async function closeDb() {
552
552
  }
553
553
 
554
554
  module.exports = {
555
+ DB_ENGINE, q, sqlType, idColType, authStrType, toPgPlaceholders,
555
556
  initDb, syncSchema, getDb, generateUUID, closeDb,
556
557
  exec, queryAll, queryOne, queryRun,
557
558
  findAll, findAllSimple, findById, create, update, remove,
@@ -4,6 +4,18 @@ const path = require('path');
4
4
  const fs = require('fs');
5
5
  const express = require('express');
6
6
  const logger = require('../utils/logger');
7
+ const { sanitizeFilename } = require('./upload');
8
+
9
+ // Lazy-load busboy (shared with upload.js)
10
+ function getBusboy() {
11
+ try {
12
+ return require('busboy');
13
+ } catch {
14
+ throw new Error(
15
+ 'busboy is required for file uploads. Install it with: npm install busboy'
16
+ );
17
+ }
18
+ }
7
19
 
8
20
  /**
9
21
  * Register file storage routes for all buckets defined in core.files.
@@ -50,11 +62,7 @@ function registerFileRoutes(app, core) {
50
62
  file.resume();
51
63
  return;
52
64
  }
53
- // Sanitize filename — strip directory traversal and disallow problematic characters
54
- const safeName = path
55
- .basename(filename)
56
- .replace(/[^a-zA-Z0-9._-]/g, '_')
57
- .replace(/^\.+/, '_');
65
+ const safeName = sanitizeFilename(filename);
58
66
  const dest = path.join(bucketPath, safeName);
59
67
  const writeStream = fs.createWriteStream(dest);
60
68
  file.pipe(writeStream);
@@ -84,14 +92,4 @@ function registerFileRoutes(app, core) {
84
92
  }
85
93
  }
86
94
 
87
- function getBusboy() {
88
- try {
89
- return require('busboy');
90
- } catch {
91
- throw new Error(
92
- 'busboy is required for file uploads. Install it with: npm install busboy'
93
- );
94
- }
95
- }
96
-
97
95
  module.exports = { registerFileRoutes };
@@ -7,6 +7,7 @@ const YAML = require('yaml');
7
7
  const logger = require('../utils/logger');
8
8
 
9
9
  const { buildCore, toSnakeCase } = require('./entity-engine');
10
+ const { DB_ENGINE, q, sqlType, idColType, authStrType } = require('./db');
10
11
 
11
12
  // ─── Git helpers ──────────────────────────────────────────────────────────────
12
13
 
@@ -46,58 +47,6 @@ function loadCurrentYaml(yamlPath) {
46
47
  return YAML.parse(fs.readFileSync(resolved, 'utf8'));
47
48
  }
48
49
 
49
- // ─── SQL generation helpers ───────────────────────────────────────────────────
50
-
51
- const DB_ENGINE = (process.env.DB_ENGINE || 'sqlite').toLowerCase();
52
-
53
- const SQL_TYPE_SQLITE = {
54
- text: 'TEXT', string: 'TEXT', richText: 'TEXT',
55
- integer: 'INTEGER', int: 'INTEGER',
56
- number: 'REAL', float: 'REAL', real: 'REAL', money: 'REAL',
57
- boolean: 'INTEGER', bool: 'INTEGER',
58
- date: 'TEXT', timestamp: 'TEXT', email: 'TEXT', link: 'TEXT',
59
- password: 'TEXT', choice: 'TEXT', location: 'TEXT',
60
- file: 'TEXT', image: 'TEXT', group: 'TEXT', json: 'TEXT',
61
- };
62
-
63
- const SQL_TYPE_PG = {
64
- text: 'TEXT', string: 'TEXT', richText: 'TEXT',
65
- integer: 'INTEGER', int: 'INTEGER',
66
- number: 'NUMERIC', float: 'NUMERIC', real: 'NUMERIC', money: 'NUMERIC',
67
- boolean: 'BOOLEAN', bool: 'BOOLEAN',
68
- date: 'TEXT', timestamp: 'TEXT', email: 'TEXT', link: 'TEXT',
69
- password: 'TEXT', choice: 'TEXT', location: 'TEXT',
70
- file: 'TEXT', image: 'TEXT', group: 'TEXT', json: 'TEXT',
71
- };
72
-
73
- const SQL_TYPE_MYSQL = {
74
- text: 'TEXT', string: 'TEXT', richText: 'TEXT',
75
- integer: 'INT', int: 'INT',
76
- number: 'DECIMAL(15,4)', float: 'DECIMAL(15,4)', real: 'DECIMAL(15,4)', money: 'DECIMAL(15,4)',
77
- boolean: 'TINYINT(1)', bool: 'TINYINT(1)',
78
- date: 'TEXT', timestamp: 'TEXT', email: 'TEXT', link: 'TEXT',
79
- password: 'TEXT', choice: 'TEXT', location: 'TEXT',
80
- file: 'TEXT', image: 'TEXT', group: 'TEXT', json: 'TEXT',
81
- };
82
-
83
- function sqlType(type) {
84
- if (DB_ENGINE === 'postgres') return SQL_TYPE_PG[type] || 'TEXT';
85
- if (DB_ENGINE === 'mysql') return SQL_TYPE_MYSQL[type] || 'TEXT';
86
- return SQL_TYPE_SQLITE[type] || 'TEXT';
87
- }
88
-
89
- function idColType() {
90
- return DB_ENGINE === 'mysql' ? 'VARCHAR(36)' : 'TEXT';
91
- }
92
-
93
- function authStrType() {
94
- return DB_ENGINE === 'mysql' ? 'VARCHAR(191)' : 'TEXT';
95
- }
96
-
97
- function q(name) {
98
- if (DB_ENGINE === 'mysql') return `\`${name}\``;
99
- return `"${name}"`;
100
- }
101
50
 
102
51
  // ─── Diff engine ──────────────────────────────────────────────────────────────
103
52
 
package/core/oauth.js CHANGED
@@ -15,7 +15,7 @@
15
15
  const crypto = require('crypto');
16
16
  const Grant = require('grant').express();
17
17
  const rateLimit = require('express-rate-limit');
18
- const { signToken } = require('./auth');
18
+ const { signToken, omitPassword } = require('./auth');
19
19
  const db = require('./db');
20
20
  const logger = require('../utils/logger');
21
21
 
@@ -195,7 +195,7 @@ function registerOAuthRoutes(app, core, emit) {
195
195
  return res.redirect(`${successRedirect}${sep}token=${encodeURIComponent(token)}`);
196
196
  }
197
197
 
198
- res.json({ token, user: _omitSensitive(user) });
198
+ res.json({ token, user: omitPassword(user) });
199
199
  } catch (e) {
200
200
  logger.error('OAuth callback error:', e.message);
201
201
  return _handleError(res, oauthConfig, e.message);
@@ -245,12 +245,6 @@ async function _findOrCreateOAuthUser(entity, { email, name, provider, providerI
245
245
  return created;
246
246
  }
247
247
 
248
- function _omitSensitive(user) {
249
- if (!user) return null;
250
- const { password: _, ...rest } = user;
251
- return rest;
252
- }
253
-
254
248
  function _handleError(res, oauthConfig, message) {
255
249
  const errorRedirect = oauthConfig.errorRedirect;
256
250
  if (errorRedirect) {
package/core/seeder.js CHANGED
@@ -44,7 +44,7 @@ function fakeValueForProp(prop, idx, groups = {}) {
44
44
  const { name, type, options } = prop;
45
45
  const n = idx + 1;
46
46
 
47
- if (options && Array.isArray(options) && options.length > 0) {
47
+ if (Array.isArray(options) && options.length) {
48
48
  return options[randomInt(0, options.length - 1)];
49
49
  }
50
50
 
@@ -184,7 +184,7 @@ async function seedAll(core) {
184
184
  for (const rel of entity.belongsTo || []) {
185
185
  const parentName = typeof rel === 'string' ? rel : (rel.entity || rel.name);
186
186
  const parentEntity = core.entities[parentName];
187
- if (parentEntity && seededIds[parentName] && seededIds[parentName].length > 0) {
187
+ if (parentEntity && seededIds[parentName]?.length) {
188
188
  const fk = `${parentEntity.tableName}_id`;
189
189
  const parentIds = seededIds[parentName];
190
190
  record[fk] = parentIds[randomInt(0, parentIds.length - 1)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chadstart",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "YAML-first Backend as a Service — define your entire backend in one YAML file",
5
5
  "main": "server/express-server.js",
6
6
  "bin": {