create-efc-app 0.9.0 → 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 +27 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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"] = "^
|
|
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"] = "^
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3762
|
+
password: await hashPassword(randomToken()),
|
|
3771
3763
|
isVerified: true,
|
|
3772
3764
|
}`
|
|
3773
3765
|
)});
|