create-efc-app 0.9.1 → 0.9.2

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 CHANGED
@@ -153,7 +153,7 @@ async function scaffold(opts) {
153
153
  }
154
154
  async function writePackageJson(dest, opts) {
155
155
  const deps = {
156
- "express-file-cluster": "^0.9.1"
156
+ "express-file-cluster": "^0.9.2"
157
157
  };
158
158
  if (opts.database === "mongodb") deps["mongoose"] = "^9.7.2";
159
159
  if (opts.database === "postgresql") {
@@ -162,10 +162,8 @@ async function writePackageJson(dest, opts) {
162
162
  }
163
163
  if (opts.tasks && opts.taskBackend === "bullmq") deps["bullmq"] = "^5.0.0";
164
164
  if (opts.tasks && opts.taskBackend === "pg-boss") deps["pg-boss"] = "^12.0.0";
165
- if (opts.mailer) deps["nodemailer"] = "^6.9.0";
165
+ if (opts.mailer) deps["nodemailer"] = "^10.2.1";
166
166
  if (usesOAuth(opts)) deps["jose"] = "^6.2.3";
167
- const auth = !opts.registry || opts.userPortal || opts.adminPortal;
168
- if (auth) deps["bcrypt"] = "^5.1.0";
169
167
  const devDeps = {
170
168
  vitest: "^5.0.3"
171
169
  };
@@ -175,8 +173,7 @@ async function writePackageJson(dest, opts) {
175
173
  devDeps["@types/node"] = "^22.0.0";
176
174
  devDeps["@types/express"] = "^4.17.21";
177
175
  devDeps["tsx"] = "^4.0.0";
178
- if (opts.mailer) devDeps["@types/nodemailer"] = "^6.4.0";
179
- if (auth) devDeps["@types/bcrypt"] = "^5.0.0";
176
+ if (opts.mailer) devDeps["@types/nodemailer"] = "^8.0.2";
180
177
  if (opts.database === "postgresql") devDeps["@types/pg"] = "^8.11.0";
181
178
  }
182
179
  const pkg = {
@@ -194,7 +191,9 @@ async function writePackageJson(dest, opts) {
194
191
  "db:studio": "efc db studio"
195
192
  },
196
193
  dependencies: deps,
197
- devDependencies: devDeps
194
+ devDependencies: devDeps,
195
+ // tsup 8 still asks for esbuild 0.27, which has an open advisory; 0.28 builds the same.
196
+ ...opts.language === "typescript" ? { overrides: { esbuild: "^0.28.1" } } : {}
198
197
  };
199
198
  await fs.writeJson(path.join(dest, "package.json"), pkg, { spaces: 2 });
200
199
  }
@@ -584,14 +583,13 @@ export const meta: RouteMeta = {
584
583
  ` : "";
585
584
  const { user: hasUser, admin: hasAdmin } = principals(opts);
586
585
  const hasModels = hasUser || hasAdmin;
587
- const loginDbImports = hasModels ? `import bcrypt from 'bcrypt';
588
- import crypto from 'node:crypto';
586
+ const loginDbImports = hasModels ? `import crypto from 'node:crypto';
589
587
  ${hasUser ? `import { User } from '../../model/User.js';
590
588
  ` : ""}${hasAdmin ? `import { Admin } from '../../model/Admin.js';
591
589
  ` : ""}` : "";
592
590
  const loginBody = hasAdmin && hasUser ? ` const admin = await ${db.findOne("Admin", `{ email }`)};
593
591
  if (admin) {
594
- const match = await bcrypt.compare(password, admin.password);
592
+ const match = await verifyPassword(password, admin.password);
595
593
  if (!match) return res.status(401).json({ error: 'Invalid credentials' });
596
594
  if (!admin.isActive) return res.status(403).json({ error: 'Account suspended' });
597
595
  await issueToken(res, { id: admin.id, role: admin.role, email: admin.email });
@@ -601,27 +599,27 @@ ${hasUser ? `import { User } from '../../model/User.js';
601
599
 
602
600
  const user = await ${db.findOne("User", `{ email }`)};
603
601
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });
604
- const match = await bcrypt.compare(password, user.password);
602
+ const match = await verifyPassword(password, user.password);
605
603
  if (!match) return res.status(401).json({ error: 'Invalid credentials' });
606
604
  if (!user.isActive) return res.status(403).json({ error: 'Account suspended' });
607
605
  await issueToken(res, { id: user.id, role: user.role, email: user.email });
608
606
  await issueRefreshToken(res, User, user.id);
609
607
  res.json({ message: 'Logged in' });` : hasAdmin ? ` const admin = await ${db.findOne("Admin", `{ email }`)};
610
608
  if (!admin) return res.status(401).json({ error: 'Invalid credentials' });
611
- const match = await bcrypt.compare(password, admin.password);
609
+ const match = await verifyPassword(password, admin.password);
612
610
  if (!match) return res.status(401).json({ error: 'Invalid credentials' });
613
611
  if (!admin.isActive) return res.status(403).json({ error: 'Account suspended' });
614
612
  await issueToken(res, { id: admin.id, role: admin.role, email: admin.email });
615
613
  await issueRefreshToken(res, Admin, admin.id);
616
614
  res.json({ message: 'Logged in as admin' });` : ` const user = await ${db.findOne("User", `{ email }`)};
617
615
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });
618
- const match = await bcrypt.compare(password, user.password);
616
+ const match = await verifyPassword(password, user.password);
619
617
  if (!match) return res.status(401).json({ error: 'Invalid credentials' });
620
618
  if (!user.isActive) return res.status(403).json({ error: 'Account suspended' });
621
619
  await issueToken(res, { id: user.id, role: user.role, email: user.email });
622
620
  await issueRefreshToken(res, User, user.id);
623
621
  res.json({ message: 'Logged in' });`;
624
- const loginContent = hasModels ? ts ? `import { issueToken } from 'express-file-cluster/auth';
622
+ const loginContent = hasModels ? ts ? `import { issueToken, verifyPassword } from 'express-file-cluster/auth';
625
623
  import type { Request, Response } from 'express';
626
624
  ${loginDbImports}${loginMeta}const REFRESH_TOKEN_TTL_MS = 1000 * 60 * 60 * 24 * 30; // 30 days
627
625
 
@@ -646,7 +644,7 @@ export const POST = async (req: Request, res: Response) => {
646
644
 
647
645
  ${loginBody}
648
646
  };
649
- ` : `import { issueToken } from 'express-file-cluster/auth';
647
+ ` : `import { issueToken, verifyPassword } from 'express-file-cluster/auth';
650
648
  ${loginDbImports}${loginMeta}const REFRESH_TOKEN_TTL_MS = 1000 * 60 * 60 * 24 * 30; // 30 days
651
649
 
652
650
  async function issueRefreshToken(res, model, id) {
@@ -735,7 +733,7 @@ export const meta: RouteMeta = {
735
733
  };
736
734
 
737
735
  ` : "";
738
- const registerDbImports = `import bcrypt from 'bcrypt';
736
+ const registerDbImports = `import { hashPassword } from 'express-file-cluster/auth';
739
737
  import crypto from 'node:crypto';
740
738
  ${sendsMail(opts) ? "import { enqueue } from 'express-file-cluster/tasks';\n" : ""}import { User } from '../../model/User.js';
741
739
  `;
@@ -757,7 +755,7 @@ ${registerDbImports}${registerMeta}export const POST = async (req: Request, res:
757
755
  }
758
756
  const existing = await ${db.findOne("User", `{ email }`)};
759
757
  if (existing) return res.status(409).json({ error: 'Email already in use' });
760
- const hashed = await bcrypt.hash(password, 10);
758
+ const hashed = await hashPassword(password);
761
759
  const verifyToken = crypto.randomBytes(32).toString('hex');
762
760
  const user = await ${db.create("User", `{ name, email, password: hashed, verifyToken }`)};
763
761
  ${sendVerifyEmail}
@@ -771,7 +769,7 @@ ${sendVerifyEmail}
771
769
  }
772
770
  const existing = await ${db.findOne("User", `{ email }`)};
773
771
  if (existing) return res.status(409).json({ error: 'Email already in use' });
774
- const hashed = await bcrypt.hash(password, 10);
772
+ const hashed = await hashPassword(password);
775
773
  const verifyToken = crypto.randomBytes(32).toString('hex');
776
774
  const user = await ${db.create("User", `{ name, email, password: hashed, verifyToken }`)};
777
775
  ${sendVerifyEmail}
@@ -918,12 +916,9 @@ export const meta: RouteMeta = {
918
916
  };
919
917
 
920
918
  ` : "";
921
- const adminUsersDbImport = ts ? `import bcrypt from 'bcrypt';
922
- import { User } from '../../../model/User.js';
923
- ` : `import bcrypt from 'bcrypt';
924
- import { User } from '../../../model/User.js';
919
+ const adminUsersDbImport = `import { User } from '../../../model/User.js';
925
920
  `;
926
- const usersListContent = ts ? `import { requireAuth } from 'express-file-cluster/auth';
921
+ const usersListContent = ts ? `import { hashPassword, requireAuth } from 'express-file-cluster/auth';
927
922
  import type { Request, Response } from 'express';
928
923
  ${adminUsersDbImport}${usersListMeta}${middlewares}
929
924
  export const GET = async (req: Request, res: Response) => {
@@ -939,12 +934,12 @@ ${roleGuard} const { name, email, password, role } = req.body;
939
934
  if (!name || !email || !password) return res.status(400).json({ error: 'name, email and password are required' });
940
935
  const existing = await ${db.findOne("User", `{ email }`)};
941
936
  if (existing) return res.status(409).json({ error: 'Email already in use' });
942
- const hashed = await bcrypt.hash(password, 10);
937
+ const hashed = await hashPassword(password);
943
938
  const user = await ${db.create("User", `{ name, email, password: hashed, role: role ?? 'user' }`)};
944
939
  const { password: _, ...safe } = user;
945
940
  res.status(201).json({ message: 'User created', user: safe });
946
941
  };
947
- ` : `import { requireAuth } from 'express-file-cluster/auth';
942
+ ` : `import { hashPassword, requireAuth } from 'express-file-cluster/auth';
948
943
  ${adminUsersDbImport}${usersListMeta}${middlewares}
949
944
  export const GET = async (req, res) => {
950
945
  ${roleGuard} const page = Math.max(1, Number(req.query.page) || 1);
@@ -959,7 +954,7 @@ ${roleGuard} const { name, email, password, role } = req.body;
959
954
  if (!name || !email || !password) return res.status(400).json({ error: 'name, email and password are required' });
960
955
  const existing = await ${db.findOne("User", `{ email }`)};
961
956
  if (existing) return res.status(409).json({ error: 'Email already in use' });
962
- const hashed = await bcrypt.hash(password, 10);
957
+ const hashed = await hashPassword(password);
963
958
  const user = await ${db.create("User", `{ name, email, password: hashed, role: role ?? 'user' }`)};
964
959
  const { password: _, ...safe } = user;
965
960
  res.status(201).json({ message: 'User created', user: safe });
@@ -1630,8 +1625,7 @@ ${hasAdmin ? `import { Admin } from '../../model/Admin.js';
1630
1625
  const rpPersist = hasAdmin ? ` if (user) await ${db.update("User", `user.id`, `{ password: hashed, resetToken: '' }`)};
1631
1626
  else if (admin) await ${db.update("Admin", `admin.id`, `{ password: hashed, resetToken: '' }`)};` : ` await ${db.update("User", `user.id`, `{ password: hashed, resetToken: '' }`)};`;
1632
1627
  const rpContent = ts ? `import type { Request, Response } from 'express';
1633
- import bcrypt from 'bcrypt';
1634
- import { revokeAllSessions } from 'express-file-cluster/auth';
1628
+ import { hashPassword, revokeAllSessions } from 'express-file-cluster/auth';
1635
1629
  ${rpModelImports}${rpMeta}export const POST = async (req: Request, res: Response) => {
1636
1630
  const { token, password } = req.body;
1637
1631
  if (!token || !password) return res.status(400).json({ error: 'token and password are required' });
@@ -1642,15 +1636,14 @@ ${rpLookup}
1642
1636
  return res.status(400).json({ error: 'Invalid or expired reset token' });
1643
1637
  }
1644
1638
 
1645
- const hashed = await bcrypt.hash(password, 10);
1639
+ const hashed = await hashPassword(password);
1646
1640
  ${rpPersist}
1647
1641
  // Tokens issued with the old password stop working everywhere.
1648
1642
  await revokeAllSessions(account.id);
1649
1643
 
1650
1644
  res.json({ message: 'Password reset successfully' });
1651
1645
  };
1652
- ` : `import bcrypt from 'bcrypt';
1653
- import { revokeAllSessions } from 'express-file-cluster/auth';
1646
+ ` : `import { hashPassword, revokeAllSessions } from 'express-file-cluster/auth';
1654
1647
  ${rpModelImports}${rpMeta}export const POST = async (req, res) => {
1655
1648
  const { token, password } = req.body;
1656
1649
  if (!token || !password) return res.status(400).json({ error: 'token and password are required' });
@@ -1661,7 +1654,7 @@ ${rpLookup}
1661
1654
  return res.status(400).json({ error: 'Invalid or expired reset token' });
1662
1655
  }
1663
1656
 
1664
- const hashed = await bcrypt.hash(password, 10);
1657
+ const hashed = await hashPassword(password);
1665
1658
  ${rpPersist}
1666
1659
  // Tokens issued with the old password stop working everywhere.
1667
1660
  await revokeAllSessions(account.id);
@@ -3671,9 +3664,8 @@ async function writeOAuth(dest, opts) {
3671
3664
  const T = (s) => ts ? s : "";
3672
3665
  const userField = usesTables(opts) && generatedModels(opts).has("User") ? "user" : "userId";
3673
3666
  const lib = `import crypto from 'node:crypto';
3674
- import bcrypt from 'bcrypt';
3675
3667
  import { HttpError } from 'express-file-cluster';
3676
- import { issueToken } from 'express-file-cluster/auth';
3668
+ import { hashPassword, issueToken } from 'express-file-cluster/auth';
3677
3669
  ${T("import type { Request, Response } from 'express';\n")}import { User } from '../model/User.js';
3678
3670
  import { OAuthAccount } from '../model/OAuthAccount.js';
3679
3671
 
@@ -3767,7 +3759,7 @@ export async function signInWithOAuth(res${T(": Response")}, profile${T(": OAuth
3767
3759
  `{
3768
3760
  name: profile.name || email.split('@')[0],
3769
3761
  email,
3770
- password: await bcrypt.hash(randomToken(), 10),
3762
+ password: await hashPassword(randomToken()),
3771
3763
  isVerified: true,
3772
3764
  }`
3773
3765
  )});