create-efc-app 0.3.7 → 0.4.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/dist/index.js +390 -127
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -183,14 +183,18 @@ async function writeExampleRoute(dest, opts) {
|
|
|
183
183
|
const metaTs = opts.routeDocs ? `import type { RouteMeta } from 'express-file-cluster';
|
|
184
184
|
|
|
185
185
|
export const meta: RouteMeta = {
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
GET: {
|
|
187
|
+
description: 'Health check \u2014 returns server status and current timestamp.',
|
|
188
|
+
response: { status: 200, body: { status: 'OK', timestamp: '2024-01-01T00:00:00.000Z' } },
|
|
189
|
+
},
|
|
188
190
|
};
|
|
189
191
|
|
|
190
192
|
` : "";
|
|
191
193
|
const metaJs = opts.routeDocs ? `export const meta = {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
GET: {
|
|
195
|
+
description: 'Health check \u2014 returns server status and current timestamp.',
|
|
196
|
+
response: { status: 200, body: { status: 'OK', timestamp: '2024-01-01T00:00:00.000Z' } },
|
|
197
|
+
},
|
|
194
198
|
};
|
|
195
199
|
|
|
196
200
|
` : "";
|
|
@@ -420,15 +424,19 @@ async function writeAuthRoutes(dest, opts) {
|
|
|
420
424
|
const loginMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
421
425
|
|
|
422
426
|
export const meta: RouteMeta = {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
427
|
+
POST: {
|
|
428
|
+
description: 'Authenticate a user or admin and issue a JWT.',
|
|
429
|
+
request: { body: { email: 'user@example.com', password: 'user' } },
|
|
430
|
+
response: { status: 200, body: { message: 'Logged in as user' } },
|
|
431
|
+
},
|
|
426
432
|
};
|
|
427
433
|
|
|
428
434
|
` : `export const meta = {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
435
|
+
POST: {
|
|
436
|
+
description: 'Authenticate a user or admin and issue a JWT.',
|
|
437
|
+
request: { body: { email: 'user@example.com', password: 'user' } },
|
|
438
|
+
response: { status: 200, body: { message: 'Logged in as user' } },
|
|
439
|
+
},
|
|
432
440
|
};
|
|
433
441
|
|
|
434
442
|
` : "";
|
|
@@ -537,13 +545,17 @@ ${loginMeta}export const POST = async (req, res) => {
|
|
|
537
545
|
const logoutMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
538
546
|
|
|
539
547
|
export const meta: RouteMeta = {
|
|
540
|
-
|
|
541
|
-
|
|
548
|
+
POST: {
|
|
549
|
+
description: 'Clear the auth cookie and log the user out.',
|
|
550
|
+
response: { status: 200, body: { message: 'Logged out successfully' } },
|
|
551
|
+
},
|
|
542
552
|
};
|
|
543
553
|
|
|
544
554
|
` : `export const meta = {
|
|
545
|
-
|
|
546
|
-
|
|
555
|
+
POST: {
|
|
556
|
+
description: 'Clear the auth cookie and log the user out.',
|
|
557
|
+
response: { status: 200, body: { message: 'Logged out successfully' } },
|
|
558
|
+
},
|
|
547
559
|
};
|
|
548
560
|
|
|
549
561
|
` : "";
|
|
@@ -567,15 +579,19 @@ ${logoutMeta}export const POST = async (_req, res) => {
|
|
|
567
579
|
const registerMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
568
580
|
|
|
569
581
|
export const meta: RouteMeta = {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
582
|
+
POST: {
|
|
583
|
+
description: 'Register a new user account.',
|
|
584
|
+
request: { body: { name: 'Jane Doe', email: 'jane@example.com', password: 'secret' } },
|
|
585
|
+
response: { status: 201, body: { message: 'Account created successfully' } },
|
|
586
|
+
},
|
|
573
587
|
};
|
|
574
588
|
|
|
575
589
|
` : `export const meta = {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
590
|
+
POST: {
|
|
591
|
+
description: 'Register a new user account.',
|
|
592
|
+
request: { body: { name: 'Jane Doe', email: 'jane@example.com', password: 'secret' } },
|
|
593
|
+
response: { status: 201, body: { message: 'Account created successfully' } },
|
|
594
|
+
},
|
|
579
595
|
};
|
|
580
596
|
|
|
581
597
|
` : "";
|
|
@@ -643,13 +659,17 @@ ${registerMeta}export const POST = async (req: Request, res: Response) => {
|
|
|
643
659
|
const meMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
644
660
|
|
|
645
661
|
export const meta: RouteMeta = {
|
|
646
|
-
|
|
647
|
-
|
|
662
|
+
GET: {
|
|
663
|
+
description: 'Return the currently authenticated user.',
|
|
664
|
+
response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
|
|
665
|
+
},
|
|
648
666
|
};
|
|
649
667
|
|
|
650
668
|
` : `export const meta = {
|
|
651
|
-
|
|
652
|
-
|
|
669
|
+
GET: {
|
|
670
|
+
description: 'Return the currently authenticated user.',
|
|
671
|
+
response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
|
|
672
|
+
},
|
|
653
673
|
};
|
|
654
674
|
|
|
655
675
|
` : "";
|
|
@@ -691,13 +711,17 @@ async function writeAdminRoutes(dest, opts) {
|
|
|
691
711
|
const dashboardMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
692
712
|
|
|
693
713
|
export const meta: RouteMeta = {
|
|
694
|
-
|
|
695
|
-
|
|
714
|
+
GET: {
|
|
715
|
+
description: 'Admin dashboard stats. Requires admin role.',
|
|
716
|
+
response: { status: 200, body: { stats: { totalUsers: 120, activeUsers: 98, verifiedUsers: 84 } } },
|
|
717
|
+
},
|
|
696
718
|
};
|
|
697
719
|
|
|
698
720
|
` : `export const meta = {
|
|
699
|
-
|
|
700
|
-
|
|
721
|
+
GET: {
|
|
722
|
+
description: 'Admin dashboard stats. Requires admin role.',
|
|
723
|
+
response: { status: 200, body: { stats: { totalUsers: 120, activeUsers: 98, verifiedUsers: 84 } } },
|
|
724
|
+
},
|
|
701
725
|
};
|
|
702
726
|
|
|
703
727
|
` : "";
|
|
@@ -742,13 +766,29 @@ ${roleGuard} // TODO: aggregate stats from DB
|
|
|
742
766
|
const usersListMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
743
767
|
|
|
744
768
|
export const meta: RouteMeta = {
|
|
745
|
-
|
|
746
|
-
|
|
769
|
+
GET: {
|
|
770
|
+
description: 'List all users, paginated (admin only).',
|
|
771
|
+
request: { query: { page: '1', limit: '20' } },
|
|
772
|
+
response: { status: 200, body: { users: [], total: 0, page: 1, limit: 20 } },
|
|
773
|
+
},
|
|
774
|
+
POST: {
|
|
775
|
+
description: 'Create a new user account (admin only).',
|
|
776
|
+
request: { body: { name: 'Jane Doe', email: 'jane@example.com', password: 'secret', role: 'user' } },
|
|
777
|
+
response: { status: 201, body: { message: 'User created', user: { id: 'new-id', name: 'Jane Doe', email: 'jane@example.com', role: 'user' } } },
|
|
778
|
+
},
|
|
747
779
|
};
|
|
748
780
|
|
|
749
781
|
` : `export const meta = {
|
|
750
|
-
|
|
751
|
-
|
|
782
|
+
GET: {
|
|
783
|
+
description: 'List all users, paginated (admin only).',
|
|
784
|
+
request: { query: { page: '1', limit: '20' } },
|
|
785
|
+
response: { status: 200, body: { users: [], total: 0, page: 1, limit: 20 } },
|
|
786
|
+
},
|
|
787
|
+
POST: {
|
|
788
|
+
description: 'Create a new user account (admin only).',
|
|
789
|
+
request: { body: { name: 'Jane Doe', email: 'jane@example.com', password: 'secret', role: 'user' } },
|
|
790
|
+
response: { status: 201, body: { message: 'User created', user: { id: 'new-id', name: 'Jane Doe', email: 'jane@example.com', role: 'user' } } },
|
|
791
|
+
},
|
|
752
792
|
};
|
|
753
793
|
|
|
754
794
|
` : "";
|
|
@@ -830,11 +870,39 @@ ${roleGuard} const { name, email, role } = req.body;
|
|
|
830
870
|
const userByIdMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
831
871
|
|
|
832
872
|
export const meta: RouteMeta = {
|
|
833
|
-
|
|
873
|
+
GET: {
|
|
874
|
+
description: 'Fetch a single user by ID (admin only).',
|
|
875
|
+
request: { params: { id: 'usr_01HXZ' } },
|
|
876
|
+
response: { status: 200, body: { user: { id: 'usr_01HXZ', name: 'Jane Doe', email: 'jane@example.com', role: 'user' } } },
|
|
877
|
+
},
|
|
878
|
+
PUT: {
|
|
879
|
+
description: 'Update a user by ID (admin only).',
|
|
880
|
+
request: { params: { id: 'usr_01HXZ' }, body: { name: 'Jane Doe', email: 'jane@example.com', role: 'user', isActive: true } },
|
|
881
|
+
response: { status: 200, body: { message: 'User updated', user: { id: 'usr_01HXZ', name: 'Jane Doe', email: 'jane@example.com', role: 'user' } } },
|
|
882
|
+
},
|
|
883
|
+
DELETE: {
|
|
884
|
+
description: 'Delete a user by ID (admin only).',
|
|
885
|
+
request: { params: { id: 'usr_01HXZ' } },
|
|
886
|
+
response: { status: 200, body: { message: 'User usr_01HXZ deleted' } },
|
|
887
|
+
},
|
|
834
888
|
};
|
|
835
889
|
|
|
836
890
|
` : `export const meta = {
|
|
837
|
-
|
|
891
|
+
GET: {
|
|
892
|
+
description: 'Fetch a single user by ID (admin only).',
|
|
893
|
+
request: { params: { id: 'usr_01HXZ' } },
|
|
894
|
+
response: { status: 200, body: { user: { id: 'usr_01HXZ', name: 'Jane Doe', email: 'jane@example.com', role: 'user' } } },
|
|
895
|
+
},
|
|
896
|
+
PUT: {
|
|
897
|
+
description: 'Update a user by ID (admin only).',
|
|
898
|
+
request: { params: { id: 'usr_01HXZ' }, body: { name: 'Jane Doe', email: 'jane@example.com', role: 'user', isActive: true } },
|
|
899
|
+
response: { status: 200, body: { message: 'User updated', user: { id: 'usr_01HXZ', name: 'Jane Doe', email: 'jane@example.com', role: 'user' } } },
|
|
900
|
+
},
|
|
901
|
+
DELETE: {
|
|
902
|
+
description: 'Delete a user by ID (admin only).',
|
|
903
|
+
request: { params: { id: 'usr_01HXZ' } },
|
|
904
|
+
response: { status: 200, body: { message: 'User usr_01HXZ deleted' } },
|
|
905
|
+
},
|
|
838
906
|
};
|
|
839
907
|
|
|
840
908
|
` : "";
|
|
@@ -944,13 +1012,27 @@ async function writeUserRoutes(dest, opts) {
|
|
|
944
1012
|
const profileMeta = opts.routeDocs ? ts ? `import type { RouteMeta } from 'express-file-cluster';
|
|
945
1013
|
|
|
946
1014
|
export const meta: RouteMeta = {
|
|
947
|
-
|
|
948
|
-
|
|
1015
|
+
GET: {
|
|
1016
|
+
description: "Fetch the authenticated user's profile.",
|
|
1017
|
+
response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
|
|
1018
|
+
},
|
|
1019
|
+
PUT: {
|
|
1020
|
+
description: "Update the authenticated user's profile.",
|
|
1021
|
+
request: { body: { name: 'Jane Doe', email: 'jane@example.com' } },
|
|
1022
|
+
response: { status: 200, body: { message: 'Profile updated', user: { id: '1', role: 'user', email: 'jane@example.com' } } },
|
|
1023
|
+
},
|
|
949
1024
|
};
|
|
950
1025
|
|
|
951
1026
|
` : `export const meta = {
|
|
952
|
-
|
|
953
|
-
|
|
1027
|
+
GET: {
|
|
1028
|
+
description: "Fetch the authenticated user's profile.",
|
|
1029
|
+
response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
|
|
1030
|
+
},
|
|
1031
|
+
PUT: {
|
|
1032
|
+
description: "Update the authenticated user's profile.",
|
|
1033
|
+
request: { body: { name: 'Jane Doe', email: 'jane@example.com' } },
|
|
1034
|
+
response: { status: 200, body: { message: 'Profile updated', user: { id: '1', role: 'user', email: 'jane@example.com' } } },
|
|
1035
|
+
},
|
|
954
1036
|
};
|
|
955
1037
|
|
|
956
1038
|
` : "";
|
|
@@ -1019,20 +1101,32 @@ export const PUT = async (req, res) => {
|
|
|
1019
1101
|
`;
|
|
1020
1102
|
await fs.outputFile(path.join(dest, "src", "api", "user", `profile.${ext}`), profileContent);
|
|
1021
1103
|
}
|
|
1022
|
-
function mkMeta(opts,
|
|
1104
|
+
function mkMeta(opts, methods) {
|
|
1023
1105
|
if (!opts.routeDocs) return "";
|
|
1024
|
-
const
|
|
1106
|
+
const blocks = Object.entries(methods).map(([method, m]) => {
|
|
1107
|
+
const reqParts = [];
|
|
1108
|
+
if (m.params) reqParts.push(`params: ${m.params}`);
|
|
1109
|
+
if (m.query) reqParts.push(`query: ${m.query}`);
|
|
1110
|
+
if (m.request) reqParts.push(`body: ${m.request}`);
|
|
1111
|
+
const reqLine = reqParts.length ? ` request: { ${reqParts.join(", ")} },
|
|
1025
1112
|
` : "";
|
|
1113
|
+
const status = m.status ?? 200;
|
|
1114
|
+
const respLine = m.response !== void 0 ? ` response: { status: ${status}, body: ${m.response} },
|
|
1115
|
+
` : ` response: { status: ${status} },
|
|
1116
|
+
`;
|
|
1117
|
+
const description = m.description.replace(/'/g, "\\'");
|
|
1118
|
+
return ` ${method}: {
|
|
1119
|
+
description: '${description}',
|
|
1120
|
+
${reqLine}${respLine} },`;
|
|
1121
|
+
}).join("\n");
|
|
1026
1122
|
return opts.language === "typescript" ? `import type { RouteMeta } from 'express-file-cluster';
|
|
1027
1123
|
|
|
1028
1124
|
export const meta: RouteMeta = {
|
|
1029
|
-
|
|
1030
|
-
${reqLine} response: { status: 200, body: ${body} },
|
|
1125
|
+
${blocks}
|
|
1031
1126
|
};
|
|
1032
1127
|
|
|
1033
1128
|
` : `export const meta = {
|
|
1034
|
-
|
|
1035
|
-
${reqLine} response: { status: 200, body: ${body} },
|
|
1129
|
+
${blocks}
|
|
1036
1130
|
};
|
|
1037
1131
|
|
|
1038
1132
|
`;
|
|
@@ -1774,7 +1868,9 @@ async function writeAuthExtendedRoutes(dest, opts) {
|
|
|
1774
1868
|
`;
|
|
1775
1869
|
const mailerTaskImport = opts.mailer ? `import { enqueue } from 'express-file-cluster/tasks';
|
|
1776
1870
|
` : "";
|
|
1777
|
-
const refreshMeta = mkMeta(opts,
|
|
1871
|
+
const refreshMeta = mkMeta(opts, {
|
|
1872
|
+
POST: { description: "Refresh the JWT using the refresh-token cookie and issue a new access token.", response: `{ message: 'Token refreshed' }` }
|
|
1873
|
+
});
|
|
1778
1874
|
const refreshContent = mongo ? ts ? `import { issueToken } from 'express-file-cluster/auth';
|
|
1779
1875
|
import type { Request, Response } from 'express';
|
|
1780
1876
|
import crypto from 'node:crypto';
|
|
@@ -1853,7 +1949,10 @@ ${refreshMeta}export const POST = async (_req: Request, res: Response) => {
|
|
|
1853
1949
|
};
|
|
1854
1950
|
`;
|
|
1855
1951
|
await fs.outputFile(path.join(dest, "src", "api", "auth", `refresh.${ext}`), refreshContent);
|
|
1856
|
-
const veMeta = mkMeta(opts,
|
|
1952
|
+
const veMeta = mkMeta(opts, {
|
|
1953
|
+
GET: { description: "Verify an email address using the token from the verification link.", query: `{ token: 'a1b2c3d4' }`, response: `{ message: 'Email verified' }` },
|
|
1954
|
+
POST: { description: "Resend the verification email to a given address.", request: `{ email: 'user@example.com' }`, response: `{ message: 'Verification email sent' }` }
|
|
1955
|
+
});
|
|
1857
1956
|
const veContent = mongo ? ts ? `import type { Request, Response } from 'express';
|
|
1858
1957
|
import crypto from 'node:crypto';
|
|
1859
1958
|
${mailerTaskImport}import { User } from '../../model/User.js';
|
|
@@ -1928,7 +2027,9 @@ export const POST = async (_req, res) => {
|
|
|
1928
2027
|
};
|
|
1929
2028
|
`;
|
|
1930
2029
|
await fs.outputFile(path.join(dest, "src", "api", "auth", `verify-email.${ext}`), veContent);
|
|
1931
|
-
const fpMeta = mkMeta(opts,
|
|
2030
|
+
const fpMeta = mkMeta(opts, {
|
|
2031
|
+
POST: { description: "Send a password reset email to the given address.", request: `{ email: 'user@example.com' }`, response: `{ message: 'Reset email sent' }` }
|
|
2032
|
+
});
|
|
1932
2033
|
const fpContent = mongo ? ts ? `import type { Request, Response } from 'express';
|
|
1933
2034
|
import crypto from 'node:crypto';
|
|
1934
2035
|
${mailerTaskImport}import { User } from '../../model/User.js';
|
|
@@ -1991,7 +2092,9 @@ ${fpMeta}export const POST = async (req: Request, res: Response) => {
|
|
|
1991
2092
|
};
|
|
1992
2093
|
`;
|
|
1993
2094
|
await fs.outputFile(path.join(dest, "src", "api", "auth", `forgot-password.${ext}`), fpContent);
|
|
1994
|
-
const rpMeta = mkMeta(opts,
|
|
2095
|
+
const rpMeta = mkMeta(opts, {
|
|
2096
|
+
POST: { description: "Reset password using a valid reset token.", request: `{ token: 'reset-token', password: 'newpassword' }`, response: `{ message: 'Password reset successfully' }` }
|
|
2097
|
+
});
|
|
1995
2098
|
const rpContent = mongo ? ts ? `import type { Request, Response } from 'express';
|
|
1996
2099
|
import bcrypt from 'bcrypt';
|
|
1997
2100
|
import { User } from '../../model/User.js';
|
|
@@ -2050,7 +2153,9 @@ ${rpMeta}export const POST = async (req: Request, res: Response) => {
|
|
|
2050
2153
|
};
|
|
2051
2154
|
`;
|
|
2052
2155
|
await fs.outputFile(path.join(dest, "src", "api", "auth", `reset-password.${ext}`), rpContent);
|
|
2053
|
-
const cpMeta = mkMeta(opts,
|
|
2156
|
+
const cpMeta = mkMeta(opts, {
|
|
2157
|
+
POST: { description: "Change password for the authenticated user.", request: `{ oldPassword: 'current', newPassword: 'newpassword' }`, response: `{ message: 'Password changed successfully' }` }
|
|
2158
|
+
});
|
|
2054
2159
|
const cpContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2055
2160
|
${cpMeta}${mwUser2}
|
|
2056
2161
|
export const POST = async (req: Request, res: Response) => {
|
|
@@ -2068,7 +2173,10 @@ export const POST = async (req, res) => {
|
|
|
2068
2173
|
};
|
|
2069
2174
|
`;
|
|
2070
2175
|
await fs.outputFile(path.join(dest, "src", "api", "auth", `change-password.${ext}`), cpContent);
|
|
2071
|
-
const tfaSetupMeta = mkMeta(opts,
|
|
2176
|
+
const tfaSetupMeta = mkMeta(opts, {
|
|
2177
|
+
GET: { description: "Generate a TOTP secret and QR code to set up 2FA.", response: `{ qrCode: 'otpauth://totp/...', secret: 'BASE32SECRET' }` },
|
|
2178
|
+
POST: { description: "Confirm a TOTP code to enable 2FA for the authenticated user.", request: `{ code: '123456' }`, response: `{ message: '2FA enabled' }` }
|
|
2179
|
+
});
|
|
2072
2180
|
const tfaSetupContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2073
2181
|
${tfaSetupMeta}${mwUser3}
|
|
2074
2182
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2096,7 +2204,9 @@ export const POST = async (req, res) => {
|
|
|
2096
2204
|
};
|
|
2097
2205
|
`;
|
|
2098
2206
|
await fs.outputFile(path.join(dest, "src", "api", "auth", "2fa", `setup.${ext}`), tfaSetupContent);
|
|
2099
|
-
const tfaVerifyMeta = mkMeta(opts,
|
|
2207
|
+
const tfaVerifyMeta = mkMeta(opts, {
|
|
2208
|
+
POST: { description: "Verify a TOTP code during login.", request: `{ code: '123456' }`, response: `{ message: '2FA verified' }` }
|
|
2209
|
+
});
|
|
2100
2210
|
const tfaVerifyContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2101
2211
|
${tfaVerifyMeta}export const POST = async (req: Request, res: Response) => {
|
|
2102
2212
|
const { code } = req.body;
|
|
@@ -2112,7 +2222,9 @@ ${tfaVerifyMeta}export const POST = async (req: Request, res: Response) => {
|
|
|
2112
2222
|
};
|
|
2113
2223
|
`;
|
|
2114
2224
|
await fs.outputFile(path.join(dest, "src", "api", "auth", "2fa", `verify.${ext}`), tfaVerifyContent);
|
|
2115
|
-
const tfaDisableMeta = mkMeta(opts,
|
|
2225
|
+
const tfaDisableMeta = mkMeta(opts, {
|
|
2226
|
+
POST: { description: "Disable 2FA for the authenticated user.", request: `{ code: '123456' }`, response: `{ message: '2FA disabled' }` }
|
|
2227
|
+
});
|
|
2116
2228
|
const tfaDisableContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2117
2229
|
${tfaDisableMeta}${mwUser3}
|
|
2118
2230
|
export const POST = async (req: Request, res: Response) => {
|
|
@@ -2130,7 +2242,9 @@ export const POST = async (req, res) => {
|
|
|
2130
2242
|
};
|
|
2131
2243
|
`;
|
|
2132
2244
|
await fs.outputFile(path.join(dest, "src", "api", "auth", "2fa", `disable.${ext}`), tfaDisableContent);
|
|
2133
|
-
const sessListMeta = mkMeta(opts,
|
|
2245
|
+
const sessListMeta = mkMeta(opts, {
|
|
2246
|
+
GET: { description: "List all active sessions for the authenticated user.", response: `{ sessions: [] }` }
|
|
2247
|
+
});
|
|
2134
2248
|
const sessListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2135
2249
|
${sessListMeta}${mwUser3}
|
|
2136
2250
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2144,14 +2258,17 @@ export const GET = async (req, res) => {
|
|
|
2144
2258
|
};
|
|
2145
2259
|
`;
|
|
2146
2260
|
await fs.outputFile(path.join(dest, "src", "api", "auth", "sessions", `index.${ext}`), sessListContent);
|
|
2261
|
+
const sessRevokeMeta = mkMeta(opts, {
|
|
2262
|
+
DELETE: { description: "Revoke a single active session by ID.", params: `{ id: 'sess_01HXZ' }`, response: `{ message: 'Session sess_01HXZ revoked' }` }
|
|
2263
|
+
});
|
|
2147
2264
|
const sessRevokeContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2148
|
-
${mwUser3}
|
|
2265
|
+
${sessRevokeMeta}${mwUser3}
|
|
2149
2266
|
export const DELETE = async (req: Request, res: Response) => {
|
|
2150
2267
|
const { id } = req.params;
|
|
2151
2268
|
// TODO: revoke session by id
|
|
2152
2269
|
res.json({ message: \`Session \${id} revoked\` });
|
|
2153
2270
|
};
|
|
2154
|
-
` : `${RA}${mwUser3}
|
|
2271
|
+
` : `${RA}${sessRevokeMeta}${mwUser3}
|
|
2155
2272
|
export const DELETE = async (req, res) => {
|
|
2156
2273
|
const { id } = req.params;
|
|
2157
2274
|
// TODO: revoke session by id
|
|
@@ -2170,7 +2287,10 @@ async function writeUserExtendedRoutes(dest, opts) {
|
|
|
2170
2287
|
const RA = `import { requireAuth } from 'express-file-cluster/auth';
|
|
2171
2288
|
`;
|
|
2172
2289
|
const user = ts ? `(req as any).user` : `req.user`;
|
|
2173
|
-
const avatarMeta = mkMeta(opts,
|
|
2290
|
+
const avatarMeta = mkMeta(opts, {
|
|
2291
|
+
POST: { description: "Upload a new avatar image for the authenticated user.", response: `{ message: 'Avatar updated', url: 'https://example.com/avatar.jpg' }` },
|
|
2292
|
+
DELETE: { description: "Remove the authenticated user's avatar.", response: `{ message: 'Avatar removed' }` }
|
|
2293
|
+
});
|
|
2174
2294
|
const avatarContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2175
2295
|
${avatarMeta}${mw2}
|
|
2176
2296
|
export const POST = async (req: Request, res: Response) => {
|
|
@@ -2194,7 +2314,10 @@ export const DELETE = async (req, res) => {
|
|
|
2194
2314
|
};
|
|
2195
2315
|
`;
|
|
2196
2316
|
await fs.outputFile(path.join(dest, "src", "api", "user", `avatar.${ext}`), avatarContent);
|
|
2197
|
-
const settingsMeta = mkMeta(opts,
|
|
2317
|
+
const settingsMeta = mkMeta(opts, {
|
|
2318
|
+
GET: { description: "Get account settings (notifications, language, theme, privacy) for the authenticated user.", response: `{ settings: { notifications: true, language: 'en', theme: 'system', privacy: 'public' } }` },
|
|
2319
|
+
PUT: { description: "Update account settings for the authenticated user.", request: `{ notifications: true, language: 'en', theme: 'system', privacy: 'public' }`, response: `{ message: 'Settings updated', settings: { notifications: true, language: 'en', theme: 'system', privacy: 'public' } }` }
|
|
2320
|
+
});
|
|
2198
2321
|
const settingsContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2199
2322
|
${settingsMeta}${mw2}
|
|
2200
2323
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2220,7 +2343,10 @@ export const PUT = async (req, res) => {
|
|
|
2220
2343
|
};
|
|
2221
2344
|
`;
|
|
2222
2345
|
await fs.outputFile(path.join(dest, "src", "api", "user", `settings.${ext}`), settingsContent);
|
|
2223
|
-
const accountMeta = mkMeta(opts,
|
|
2346
|
+
const accountMeta = mkMeta(opts, {
|
|
2347
|
+
GET: { description: "Download a personal data export for the authenticated user.", response: `{ data: { user: { id: '1', role: 'user', email: 'user@example.com' }, exportedAt: '2026-01-01T00:00:00.000Z' } }` },
|
|
2348
|
+
DELETE: { description: "Schedule the authenticated account for deletion (requires password confirmation).", request: `{ password: 'current' }`, response: `{ message: 'Account scheduled for deletion' }` }
|
|
2349
|
+
});
|
|
2224
2350
|
const accountContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2225
2351
|
${accountMeta}${mw2}
|
|
2226
2352
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2248,7 +2374,9 @@ export const DELETE = async (req, res) => {
|
|
|
2248
2374
|
};
|
|
2249
2375
|
`;
|
|
2250
2376
|
await fs.outputFile(path.join(dest, "src", "api", "user", `account.${ext}`), accountContent);
|
|
2251
|
-
const dashMeta = mkMeta(opts,
|
|
2377
|
+
const dashMeta = mkMeta(opts, {
|
|
2378
|
+
GET: { description: "Personal dashboard: stats, recent activity, and quick actions.", response: `{ stats: {}, recentActivity: [], quickActions: [] }` }
|
|
2379
|
+
});
|
|
2252
2380
|
const dashContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2253
2381
|
${dashMeta}${mw2}
|
|
2254
2382
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2262,7 +2390,9 @@ export const GET = async (req, res) => {
|
|
|
2262
2390
|
};
|
|
2263
2391
|
`;
|
|
2264
2392
|
await fs.outputFile(path.join(dest, "src", "api", "user", `dashboard.${ext}`), dashContent);
|
|
2265
|
-
const actMeta = mkMeta(opts,
|
|
2393
|
+
const actMeta = mkMeta(opts, {
|
|
2394
|
+
GET: { description: "Paginated activity history for the authenticated user.", query: `{ page: '1', limit: '20' }`, response: `{ activities: [], total: 0, page: 1, limit: 20 }` }
|
|
2395
|
+
});
|
|
2266
2396
|
const actContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2267
2397
|
${actMeta}${mw2}
|
|
2268
2398
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2278,7 +2408,10 @@ export const GET = async (req, res) => {
|
|
|
2278
2408
|
};
|
|
2279
2409
|
`;
|
|
2280
2410
|
await fs.outputFile(path.join(dest, "src", "api", "user", `activity.${ext}`), actContent);
|
|
2281
|
-
const notifListMeta = mkMeta(opts,
|
|
2411
|
+
const notifListMeta = mkMeta(opts, {
|
|
2412
|
+
GET: { description: "List notifications for the authenticated user.", response: `{ notifications: [], total: 0, unread: 0 }` },
|
|
2413
|
+
POST: { description: "Mark all notifications as read for the authenticated user.", response: `{ message: 'All notifications marked as read' }` }
|
|
2414
|
+
});
|
|
2282
2415
|
const notifListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2283
2416
|
${notifListMeta}${mw3}
|
|
2284
2417
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2302,8 +2435,13 @@ export const POST = async (req, res) => {
|
|
|
2302
2435
|
};
|
|
2303
2436
|
`;
|
|
2304
2437
|
await fs.outputFile(path.join(dest, "src", "api", "user", "notifications", `index.${ext}`), notifListContent);
|
|
2438
|
+
const notifByIdMeta = mkMeta(opts, {
|
|
2439
|
+
GET: { description: "Fetch a single notification by ID.", params: `{ id: 'notif_01HXZ' }`, response: `{ notification: { id: 'notif_01HXZ' } }` },
|
|
2440
|
+
PATCH: { description: "Mark a single notification as read.", params: `{ id: 'notif_01HXZ' }`, response: `{ message: 'Notification marked as read', id: 'notif_01HXZ' }` },
|
|
2441
|
+
DELETE: { description: "Delete a single notification.", params: `{ id: 'notif_01HXZ' }`, response: `{ message: 'Notification notif_01HXZ deleted' }` }
|
|
2442
|
+
});
|
|
2305
2443
|
const notifByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2306
|
-
${mw3}
|
|
2444
|
+
${notifByIdMeta}${mw3}
|
|
2307
2445
|
export const GET = async (req: Request, res: Response) => {
|
|
2308
2446
|
const { id } = req.params;
|
|
2309
2447
|
// TODO: fetch notification by id
|
|
@@ -2321,7 +2459,7 @@ export const DELETE = async (req: Request, res: Response) => {
|
|
|
2321
2459
|
// TODO: delete notification
|
|
2322
2460
|
res.json({ message: \`Notification \${id} deleted\` });
|
|
2323
2461
|
};
|
|
2324
|
-
` : `${RA}${mw3}
|
|
2462
|
+
` : `${RA}${notifByIdMeta}${mw3}
|
|
2325
2463
|
export const GET = async (req, res) => {
|
|
2326
2464
|
const { id } = req.params;
|
|
2327
2465
|
// TODO: fetch notification by id
|
|
@@ -2341,7 +2479,10 @@ export const DELETE = async (req, res) => {
|
|
|
2341
2479
|
};
|
|
2342
2480
|
`;
|
|
2343
2481
|
await fs.outputFile(path.join(dest, "src", "api", "user", "notifications", `[id].${ext}`), notifByIdContent);
|
|
2344
|
-
const filesListMeta = mkMeta(opts,
|
|
2482
|
+
const filesListMeta = mkMeta(opts, {
|
|
2483
|
+
GET: { description: "List uploaded files with storage usage for the authenticated user.", response: `{ files: [], total: 0, storageUsed: 0 }` },
|
|
2484
|
+
POST: { description: "Upload a new file for the authenticated user.", response: `{ message: 'File uploaded', file: { id: 'new-id' } }`, status: 201 }
|
|
2485
|
+
});
|
|
2345
2486
|
const filesListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2346
2487
|
${filesListMeta}${mw3}
|
|
2347
2488
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2365,8 +2506,12 @@ export const POST = async (req, res) => {
|
|
|
2365
2506
|
};
|
|
2366
2507
|
`;
|
|
2367
2508
|
await fs.outputFile(path.join(dest, "src", "api", "user", "files", `index.${ext}`), filesListContent);
|
|
2509
|
+
const fileByIdMeta = mkMeta(opts, {
|
|
2510
|
+
GET: { description: "Get a download/preview URL for a single file by ID.", params: `{ id: 'file_01HXZ' }`, response: `{ file: { id: 'file_01HXZ', url: 'https://example.com/files/file_01HXZ' } }` },
|
|
2511
|
+
DELETE: { description: "Delete a file from storage.", params: `{ id: 'file_01HXZ' }`, response: `{ message: 'File file_01HXZ deleted' }` }
|
|
2512
|
+
});
|
|
2368
2513
|
const fileByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2369
|
-
${mw3}
|
|
2514
|
+
${fileByIdMeta}${mw3}
|
|
2370
2515
|
export const GET = async (req: Request, res: Response) => {
|
|
2371
2516
|
const { id } = req.params;
|
|
2372
2517
|
// TODO: stream or redirect to file download/preview URL
|
|
@@ -2378,7 +2523,7 @@ export const DELETE = async (req: Request, res: Response) => {
|
|
|
2378
2523
|
// TODO: delete file from storage and DB
|
|
2379
2524
|
res.json({ message: \`File \${id} deleted\` });
|
|
2380
2525
|
};
|
|
2381
|
-
` : `${RA}${mw3}
|
|
2526
|
+
` : `${RA}${fileByIdMeta}${mw3}
|
|
2382
2527
|
export const GET = async (req, res) => {
|
|
2383
2528
|
const { id } = req.params;
|
|
2384
2529
|
// TODO: stream or redirect to file download/preview URL
|
|
@@ -2392,8 +2537,12 @@ export const DELETE = async (req, res) => {
|
|
|
2392
2537
|
};
|
|
2393
2538
|
`;
|
|
2394
2539
|
await fs.outputFile(path.join(dest, "src", "api", "user", "files", `[id].${ext}`), fileByIdContent);
|
|
2540
|
+
const favListMeta = mkMeta(opts, {
|
|
2541
|
+
GET: { description: "List favorites for the authenticated user.", response: `{ favorites: [] }` },
|
|
2542
|
+
POST: { description: "Add an entity to the authenticated user's favorites.", request: `{ entityId: 'post_01HXZ', entityType: 'post' }`, response: `{ message: 'Added to favorites' }`, status: 201 }
|
|
2543
|
+
});
|
|
2395
2544
|
const favListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2396
|
-
${mw3}
|
|
2545
|
+
${favListMeta}${mw3}
|
|
2397
2546
|
export const GET = async (req: Request, res: Response) => {
|
|
2398
2547
|
// TODO: fetch user favorites
|
|
2399
2548
|
res.json({ favorites: [] });
|
|
@@ -2405,7 +2554,7 @@ export const POST = async (req: Request, res: Response) => {
|
|
|
2405
2554
|
// TODO: add to favorites
|
|
2406
2555
|
res.status(201).json({ message: 'Added to favorites' });
|
|
2407
2556
|
};
|
|
2408
|
-
` : `${RA}${mw3}
|
|
2557
|
+
` : `${RA}${favListMeta}${mw3}
|
|
2409
2558
|
export const GET = async (req, res) => {
|
|
2410
2559
|
// TODO: fetch user favorites
|
|
2411
2560
|
res.json({ favorites: [] });
|
|
@@ -2419,14 +2568,17 @@ export const POST = async (req, res) => {
|
|
|
2419
2568
|
};
|
|
2420
2569
|
`;
|
|
2421
2570
|
await fs.outputFile(path.join(dest, "src", "api", "user", "favorites", `index.${ext}`), favListContent);
|
|
2571
|
+
const favByIdMeta = mkMeta(opts, {
|
|
2572
|
+
DELETE: { description: "Remove an entity from the authenticated user favorites.", params: `{ id: 'fav_01HXZ' }`, response: `{ message: 'Removed favorite fav_01HXZ' }` }
|
|
2573
|
+
});
|
|
2422
2574
|
const favByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2423
|
-
${mw3}
|
|
2575
|
+
${favByIdMeta}${mw3}
|
|
2424
2576
|
export const DELETE = async (req: Request, res: Response) => {
|
|
2425
2577
|
const { id } = req.params;
|
|
2426
2578
|
// TODO: remove from favorites
|
|
2427
2579
|
res.json({ message: \`Removed favorite \${id}\` });
|
|
2428
2580
|
};
|
|
2429
|
-
` : `${RA}${mw3}
|
|
2581
|
+
` : `${RA}${favByIdMeta}${mw3}
|
|
2430
2582
|
export const DELETE = async (req, res) => {
|
|
2431
2583
|
const { id } = req.params;
|
|
2432
2584
|
// TODO: remove from favorites
|
|
@@ -2434,8 +2586,12 @@ export const DELETE = async (req, res) => {
|
|
|
2434
2586
|
};
|
|
2435
2587
|
`;
|
|
2436
2588
|
await fs.outputFile(path.join(dest, "src", "api", "user", "favorites", `[id].${ext}`), favByIdContent);
|
|
2589
|
+
const bkListMeta = mkMeta(opts, {
|
|
2590
|
+
GET: { description: "List bookmarks for the authenticated user.", response: `{ bookmarks: [] }` },
|
|
2591
|
+
POST: { description: "Save a new bookmark for the authenticated user.", request: `{ url: 'https://example.com', title: 'Example' }`, response: `{ message: 'Bookmark saved' }`, status: 201 }
|
|
2592
|
+
});
|
|
2437
2593
|
const bkListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2438
|
-
${mw3}
|
|
2594
|
+
${bkListMeta}${mw3}
|
|
2439
2595
|
export const GET = async (req: Request, res: Response) => {
|
|
2440
2596
|
// TODO: fetch user bookmarks
|
|
2441
2597
|
res.json({ bookmarks: [] });
|
|
@@ -2447,7 +2603,7 @@ export const POST = async (req: Request, res: Response) => {
|
|
|
2447
2603
|
// TODO: save bookmark
|
|
2448
2604
|
res.status(201).json({ message: 'Bookmark saved' });
|
|
2449
2605
|
};
|
|
2450
|
-
` : `${RA}${mw3}
|
|
2606
|
+
` : `${RA}${bkListMeta}${mw3}
|
|
2451
2607
|
export const GET = async (req, res) => {
|
|
2452
2608
|
// TODO: fetch user bookmarks
|
|
2453
2609
|
res.json({ bookmarks: [] });
|
|
@@ -2461,14 +2617,17 @@ export const POST = async (req, res) => {
|
|
|
2461
2617
|
};
|
|
2462
2618
|
`;
|
|
2463
2619
|
await fs.outputFile(path.join(dest, "src", "api", "user", "bookmarks", `index.${ext}`), bkListContent);
|
|
2620
|
+
const bkByIdMeta = mkMeta(opts, {
|
|
2621
|
+
DELETE: { description: "Delete a bookmark by ID.", params: `{ id: 'bm_01HXZ' }`, response: `{ message: 'Bookmark bm_01HXZ removed' }` }
|
|
2622
|
+
});
|
|
2464
2623
|
const bkByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2465
|
-
${mw3}
|
|
2624
|
+
${bkByIdMeta}${mw3}
|
|
2466
2625
|
export const DELETE = async (req: Request, res: Response) => {
|
|
2467
2626
|
const { id } = req.params;
|
|
2468
2627
|
// TODO: delete bookmark
|
|
2469
2628
|
res.json({ message: \`Bookmark \${id} removed\` });
|
|
2470
2629
|
};
|
|
2471
|
-
` : `${RA}${mw3}
|
|
2630
|
+
` : `${RA}${bkByIdMeta}${mw3}
|
|
2472
2631
|
export const DELETE = async (req, res) => {
|
|
2473
2632
|
const { id } = req.params;
|
|
2474
2633
|
// TODO: delete bookmark
|
|
@@ -2476,7 +2635,9 @@ export const DELETE = async (req, res) => {
|
|
|
2476
2635
|
};
|
|
2477
2636
|
`;
|
|
2478
2637
|
await fs.outputFile(path.join(dest, "src", "api", "user", "bookmarks", `[id].${ext}`), bkByIdContent);
|
|
2479
|
-
const searchMeta = mkMeta(opts,
|
|
2638
|
+
const searchMeta = mkMeta(opts, {
|
|
2639
|
+
GET: { description: "Search with filters, sort, and pagination.", query: `{ q: 'query', filter: 'active', sort: 'newest', page: '1', limit: '20' }`, response: `{ results: [], total: 0, page: 1, limit: 20 }` }
|
|
2640
|
+
});
|
|
2480
2641
|
const searchContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2481
2642
|
${searchMeta}${mw2}
|
|
2482
2643
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2492,7 +2653,10 @@ export const GET = async (req, res) => {
|
|
|
2492
2653
|
};
|
|
2493
2654
|
`;
|
|
2494
2655
|
await fs.outputFile(path.join(dest, "src", "api", "user", `search.${ext}`), searchContent);
|
|
2495
|
-
const akListMeta = mkMeta(opts,
|
|
2656
|
+
const akListMeta = mkMeta(opts, {
|
|
2657
|
+
GET: { description: "List API keys for the authenticated user.", response: `{ apiKeys: [] }` },
|
|
2658
|
+
POST: { description: "Generate a new API key for the authenticated user.", request: `{ name: 'CI key' }`, response: `{ message: 'API key created', key: 'efc_...' }`, status: 201 }
|
|
2659
|
+
});
|
|
2496
2660
|
const akListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2497
2661
|
${akListMeta}${mw3}
|
|
2498
2662
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2520,14 +2684,17 @@ export const POST = async (req, res) => {
|
|
|
2520
2684
|
};
|
|
2521
2685
|
`;
|
|
2522
2686
|
await fs.outputFile(path.join(dest, "src", "api", "user", "api-keys", `index.${ext}`), akListContent);
|
|
2687
|
+
const akByIdMeta = mkMeta(opts, {
|
|
2688
|
+
DELETE: { description: "Revoke and delete an API key by ID.", params: `{ id: 'key_01HXZ' }`, response: `{ message: 'API key key_01HXZ revoked' }` }
|
|
2689
|
+
});
|
|
2523
2690
|
const akByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2524
|
-
${mw3}
|
|
2691
|
+
${akByIdMeta}${mw3}
|
|
2525
2692
|
export const DELETE = async (req: Request, res: Response) => {
|
|
2526
2693
|
const { id } = req.params;
|
|
2527
2694
|
// TODO: revoke and delete API key
|
|
2528
2695
|
res.json({ message: \`API key \${id} revoked\` });
|
|
2529
2696
|
};
|
|
2530
|
-
` : `${RA}${mw3}
|
|
2697
|
+
` : `${RA}${akByIdMeta}${mw3}
|
|
2531
2698
|
export const DELETE = async (req, res) => {
|
|
2532
2699
|
const { id } = req.params;
|
|
2533
2700
|
// TODO: revoke and delete API key
|
|
@@ -2545,7 +2712,9 @@ async function writeUserBillingRoutes(dest, opts) {
|
|
|
2545
2712
|
const mw4 = mw3;
|
|
2546
2713
|
const RA = `import { requireAuth } from 'express-file-cluster/auth';
|
|
2547
2714
|
`;
|
|
2548
|
-
const plansMeta = mkMeta(opts,
|
|
2715
|
+
const plansMeta = mkMeta(opts, {
|
|
2716
|
+
GET: { description: "List all available subscription plans.", response: `{ plans: [] }` }
|
|
2717
|
+
});
|
|
2549
2718
|
const plansContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2550
2719
|
${plansMeta}${mw3}
|
|
2551
2720
|
export const GET = async (_req: Request, res: Response) => {
|
|
@@ -2559,7 +2728,11 @@ export const GET = async (_req, res) => {
|
|
|
2559
2728
|
};
|
|
2560
2729
|
`;
|
|
2561
2730
|
await fs.outputFile(path.join(dest, "src", "api", "user", "billing", `plans.${ext}`), plansContent);
|
|
2562
|
-
const subMeta = mkMeta(opts,
|
|
2731
|
+
const subMeta = mkMeta(opts, {
|
|
2732
|
+
GET: { description: "Get the current subscription for the authenticated user.", response: `{ subscription: null }` },
|
|
2733
|
+
POST: { description: "Subscribe the authenticated user to a plan.", request: `{ planId: 'plan_01HXZ' }`, response: `{ message: 'Subscribed', subscription: { planId: 'plan_01HXZ' } }`, status: 201 },
|
|
2734
|
+
DELETE: { description: "Cancel the current subscription at period end.", response: `{ message: 'Subscription cancelled' }` }
|
|
2735
|
+
});
|
|
2563
2736
|
const subContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2564
2737
|
${subMeta}${mw3}
|
|
2565
2738
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2597,8 +2770,12 @@ export const DELETE = async (req, res) => {
|
|
|
2597
2770
|
};
|
|
2598
2771
|
`;
|
|
2599
2772
|
await fs.outputFile(path.join(dest, "src", "api", "user", "billing", `subscription.${ext}`), subContent);
|
|
2773
|
+
const pmListMeta = mkMeta(opts, {
|
|
2774
|
+
GET: { description: "List payment methods for the authenticated user.", response: `{ paymentMethods: [] }` },
|
|
2775
|
+
POST: { description: "Attach a new payment method via the payment gateway.", request: `{ token: 'tok_...' }`, response: `{ message: 'Payment method added' }`, status: 201 }
|
|
2776
|
+
});
|
|
2600
2777
|
const pmListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2601
|
-
${mw4}
|
|
2778
|
+
${pmListMeta}${mw4}
|
|
2602
2779
|
export const GET = async (req: Request, res: Response) => {
|
|
2603
2780
|
// TODO: fetch payment methods for user
|
|
2604
2781
|
res.json({ paymentMethods: [] });
|
|
@@ -2610,7 +2787,7 @@ export const POST = async (req: Request, res: Response) => {
|
|
|
2610
2787
|
// TODO: attach payment method via payment gateway
|
|
2611
2788
|
res.status(201).json({ message: 'Payment method added' });
|
|
2612
2789
|
};
|
|
2613
|
-
` : `${RA}${mw4}
|
|
2790
|
+
` : `${RA}${pmListMeta}${mw4}
|
|
2614
2791
|
export const GET = async (req, res) => {
|
|
2615
2792
|
// TODO: fetch payment methods for user
|
|
2616
2793
|
res.json({ paymentMethods: [] });
|
|
@@ -2624,14 +2801,17 @@ export const POST = async (req, res) => {
|
|
|
2624
2801
|
};
|
|
2625
2802
|
`;
|
|
2626
2803
|
await fs.outputFile(path.join(dest, "src", "api", "user", "billing", "payment-methods", `index.${ext}`), pmListContent);
|
|
2804
|
+
const pmByIdMeta = mkMeta(opts, {
|
|
2805
|
+
DELETE: { description: "Detach a payment method from the payment gateway.", params: `{ id: 'pm_01HXZ' }`, response: `{ message: 'Payment method pm_01HXZ removed' }` }
|
|
2806
|
+
});
|
|
2627
2807
|
const pmByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2628
|
-
${mw4}
|
|
2808
|
+
${pmByIdMeta}${mw4}
|
|
2629
2809
|
export const DELETE = async (req: Request, res: Response) => {
|
|
2630
2810
|
const { id } = req.params;
|
|
2631
2811
|
// TODO: detach payment method from payment gateway
|
|
2632
2812
|
res.json({ message: \`Payment method \${id} removed\` });
|
|
2633
2813
|
};
|
|
2634
|
-
` : `${RA}${mw4}
|
|
2814
|
+
` : `${RA}${pmByIdMeta}${mw4}
|
|
2635
2815
|
export const DELETE = async (req, res) => {
|
|
2636
2816
|
const { id } = req.params;
|
|
2637
2817
|
// TODO: detach payment method from payment gateway
|
|
@@ -2639,7 +2819,9 @@ export const DELETE = async (req, res) => {
|
|
|
2639
2819
|
};
|
|
2640
2820
|
`;
|
|
2641
2821
|
await fs.outputFile(path.join(dest, "src", "api", "user", "billing", "payment-methods", `[id].${ext}`), pmByIdContent);
|
|
2642
|
-
const invListMeta = mkMeta(opts,
|
|
2822
|
+
const invListMeta = mkMeta(opts, {
|
|
2823
|
+
GET: { description: "List all invoices for the authenticated user.", response: `{ invoices: [], total: 0 }` }
|
|
2824
|
+
});
|
|
2643
2825
|
const invListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2644
2826
|
${invListMeta}${mw4}
|
|
2645
2827
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2653,14 +2835,17 @@ export const GET = async (req, res) => {
|
|
|
2653
2835
|
};
|
|
2654
2836
|
`;
|
|
2655
2837
|
await fs.outputFile(path.join(dest, "src", "api", "user", "billing", "invoices", `index.${ext}`), invListContent);
|
|
2838
|
+
const invByIdMeta = mkMeta(opts, {
|
|
2839
|
+
GET: { description: "Get the download URL for a single invoice by ID.", params: `{ id: 'inv_01HXZ' }`, response: `{ invoice: { id: 'inv_01HXZ', downloadUrl: 'https://example.com/invoices/inv_01HXZ.pdf' } }` }
|
|
2840
|
+
});
|
|
2656
2841
|
const invByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2657
|
-
${mw4}
|
|
2842
|
+
${invByIdMeta}${mw4}
|
|
2658
2843
|
export const GET = async (req: Request, res: Response) => {
|
|
2659
2844
|
const { id } = req.params;
|
|
2660
2845
|
// TODO: return invoice PDF URL or inline data
|
|
2661
2846
|
res.json({ invoice: { id, downloadUrl: 'https://...' } });
|
|
2662
2847
|
};
|
|
2663
|
-
` : `${RA}${mw4}
|
|
2848
|
+
` : `${RA}${invByIdMeta}${mw4}
|
|
2664
2849
|
export const GET = async (req, res) => {
|
|
2665
2850
|
const { id } = req.params;
|
|
2666
2851
|
// TODO: return invoice PDF URL or inline data
|
|
@@ -2677,7 +2862,10 @@ async function writeSupportRoutes(dest, opts) {
|
|
|
2677
2862
|
`;
|
|
2678
2863
|
const RA = `import { requireAuth } from 'express-file-cluster/auth';
|
|
2679
2864
|
`;
|
|
2680
|
-
const ticketListMeta = mkMeta(opts,
|
|
2865
|
+
const ticketListMeta = mkMeta(opts, {
|
|
2866
|
+
GET: { description: "List the authenticated user's own support tickets.", response: `{ tickets: [], total: 0 }` },
|
|
2867
|
+
POST: { description: "Create a new support ticket.", request: `{ subject: 'Issue with login', message: 'I cannot log in', priority: 'normal' }`, response: `{ message: 'Ticket created', ticket: { id: 'new-id', subject: 'Issue with login', status: 'open' } }`, status: 201 }
|
|
2868
|
+
});
|
|
2681
2869
|
const ticketListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2682
2870
|
${ticketListMeta}${mw3}
|
|
2683
2871
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2705,7 +2893,10 @@ export const POST = async (req, res) => {
|
|
|
2705
2893
|
};
|
|
2706
2894
|
`;
|
|
2707
2895
|
await fs.outputFile(path.join(dest, "src", "api", "support", "tickets", `index.${ext}`), ticketListContent);
|
|
2708
|
-
const ticketByIdMeta = mkMeta(opts,
|
|
2896
|
+
const ticketByIdMeta = mkMeta(opts, {
|
|
2897
|
+
GET: { description: "Fetch a single support ticket by ID.", params: `{ id: 'tk_01HXZ' }`, response: `{ ticket: { id: 'tk_01HXZ', subject: 'Issue', status: 'open', replies: [] } }` },
|
|
2898
|
+
PUT: { description: "Add a reply to a support ticket or update its status.", params: `{ id: 'tk_01HXZ' }`, request: `{ reply: 'Thanks, resolved.', status: 'closed' }`, response: `{ message: 'Ticket updated', ticket: { id: 'tk_01HXZ', status: 'closed' } }` }
|
|
2899
|
+
});
|
|
2709
2900
|
const ticketByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2710
2901
|
${ticketByIdMeta}${mw3}
|
|
2711
2902
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2750,7 +2941,9 @@ async function writeAdminExtendedRoutes(dest, opts) {
|
|
|
2750
2941
|
`;
|
|
2751
2942
|
const RA = `import { requireAuth } from 'express-file-cluster/auth';
|
|
2752
2943
|
`;
|
|
2753
|
-
const analyticsOverviewMeta = mkMeta(opts,
|
|
2944
|
+
const analyticsOverviewMeta = mkMeta(opts, {
|
|
2945
|
+
GET: { description: "Analytics overview: users, revenue, traffic (admin only).", response: `{ users: {}, revenue: {}, traffic: {} }` }
|
|
2946
|
+
});
|
|
2754
2947
|
const analyticsOverviewContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2755
2948
|
${analyticsOverviewMeta}${mwAdmin3}
|
|
2756
2949
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2764,7 +2957,9 @@ ${roleGuard} // TODO: aggregate analytics overview
|
|
|
2764
2957
|
};
|
|
2765
2958
|
`;
|
|
2766
2959
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "analytics", `index.${ext}`), analyticsOverviewContent);
|
|
2767
|
-
const analyticsUsersMeta = mkMeta(opts,
|
|
2960
|
+
const analyticsUsersMeta = mkMeta(opts, {
|
|
2961
|
+
GET: { description: "User analytics: registrations, active users, churn (admin only).", query: `{ period: '30d' }`, response: `{ registrations: [], activeUsers: 0, churn: 0, period: '30d' }` }
|
|
2962
|
+
});
|
|
2768
2963
|
const analyticsUsersContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2769
2964
|
${analyticsUsersMeta}${mwAdmin3}
|
|
2770
2965
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2780,7 +2975,9 @@ ${roleGuard} const { period = '30d' } = req.query;
|
|
|
2780
2975
|
};
|
|
2781
2976
|
`;
|
|
2782
2977
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "analytics", `users.${ext}`), analyticsUsersContent);
|
|
2783
|
-
const analyticsRevenueMeta = mkMeta(opts,
|
|
2978
|
+
const analyticsRevenueMeta = mkMeta(opts, {
|
|
2979
|
+
GET: { description: "Revenue analytics: MRR, ARR, payment history (admin only).", query: `{ period: '30d' }`, response: `{ mrr: 0, arr: 0, history: [], period: '30d' }` }
|
|
2980
|
+
});
|
|
2784
2981
|
const analyticsRevenueContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2785
2982
|
${analyticsRevenueMeta}${mwAdmin3}
|
|
2786
2983
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2796,7 +2993,9 @@ ${roleGuard} const { period = '30d' } = req.query;
|
|
|
2796
2993
|
};
|
|
2797
2994
|
`;
|
|
2798
2995
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "analytics", `revenue.${ext}`), analyticsRevenueContent);
|
|
2799
|
-
const analyticsTrafficMeta = mkMeta(opts,
|
|
2996
|
+
const analyticsTrafficMeta = mkMeta(opts, {
|
|
2997
|
+
GET: { description: "Traffic analytics: page views, devices, countries (admin only).", query: `{ period: '30d' }`, response: `{ pageViews: 0, devices: {}, countries: {}, period: '30d' }` }
|
|
2998
|
+
});
|
|
2800
2999
|
const analyticsTrafficContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2801
3000
|
${analyticsTrafficMeta}${mwAdmin3}
|
|
2802
3001
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -2812,15 +3011,18 @@ ${roleGuard} const { period = '30d' } = req.query;
|
|
|
2812
3011
|
};
|
|
2813
3012
|
`;
|
|
2814
3013
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "analytics", `traffic.${ext}`), analyticsTrafficContent);
|
|
3014
|
+
const suspendMeta = mkMeta(opts, {
|
|
3015
|
+
POST: { description: "Suspend a user account (admin only).", params: `{ id: 'usr_01HXZ' }`, request: `{ reason: 'Terms of service violation' }`, response: `{ message: 'User usr_01HXZ suspended', reason: 'Terms of service violation' }` }
|
|
3016
|
+
});
|
|
2815
3017
|
const suspendContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2816
|
-
${mwAdmin4}
|
|
3018
|
+
${suspendMeta}${mwAdmin4}
|
|
2817
3019
|
export const POST = async (req: Request, res: Response) => {
|
|
2818
3020
|
${roleGuard} const { id } = req.params;
|
|
2819
3021
|
const { reason } = req.body;
|
|
2820
3022
|
// TODO: set user.isActive = false, log audit event
|
|
2821
3023
|
res.json({ message: \`User \${id} suspended\`, reason });
|
|
2822
3024
|
};
|
|
2823
|
-
` : `${RA}${mwAdmin4}
|
|
3025
|
+
` : `${RA}${suspendMeta}${mwAdmin4}
|
|
2824
3026
|
export const POST = async (req, res) => {
|
|
2825
3027
|
${roleGuard} const { id } = req.params;
|
|
2826
3028
|
const { reason } = req.body;
|
|
@@ -2829,14 +3031,17 @@ ${roleGuard} const { id } = req.params;
|
|
|
2829
3031
|
};
|
|
2830
3032
|
`;
|
|
2831
3033
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "users", "[id]", `suspend.${ext}`), suspendContent);
|
|
3034
|
+
const activateMeta = mkMeta(opts, {
|
|
3035
|
+
POST: { description: "Reactivate a suspended user account (admin only).", params: `{ id: 'usr_01HXZ' }`, response: `{ message: 'User usr_01HXZ activated' }` }
|
|
3036
|
+
});
|
|
2832
3037
|
const activateContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2833
|
-
${mwAdmin4}
|
|
3038
|
+
${activateMeta}${mwAdmin4}
|
|
2834
3039
|
export const POST = async (req: Request, res: Response) => {
|
|
2835
3040
|
${roleGuard} const { id } = req.params;
|
|
2836
3041
|
// TODO: set user.isActive = true, log audit event
|
|
2837
3042
|
res.json({ message: \`User \${id} activated\` });
|
|
2838
3043
|
};
|
|
2839
|
-
` : `${RA}${mwAdmin4}
|
|
3044
|
+
` : `${RA}${activateMeta}${mwAdmin4}
|
|
2840
3045
|
export const POST = async (req, res) => {
|
|
2841
3046
|
${roleGuard} const { id } = req.params;
|
|
2842
3047
|
// TODO: set user.isActive = true, log audit event
|
|
@@ -2844,14 +3049,17 @@ ${roleGuard} const { id } = req.params;
|
|
|
2844
3049
|
};
|
|
2845
3050
|
`;
|
|
2846
3051
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "users", "[id]", `activate.${ext}`), activateContent);
|
|
3052
|
+
const verifyUserMeta = mkMeta(opts, {
|
|
3053
|
+
POST: { description: "Mark a user's email as verified (admin only).", params: `{ id: 'usr_01HXZ' }`, response: `{ message: 'User usr_01HXZ verified' }` }
|
|
3054
|
+
});
|
|
2847
3055
|
const verifyUserContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2848
|
-
${mwAdmin4}
|
|
3056
|
+
${verifyUserMeta}${mwAdmin4}
|
|
2849
3057
|
export const POST = async (req: Request, res: Response) => {
|
|
2850
3058
|
${roleGuard} const { id } = req.params;
|
|
2851
3059
|
// TODO: set user.isVerified = true
|
|
2852
3060
|
res.json({ message: \`User \${id} verified\` });
|
|
2853
3061
|
};
|
|
2854
|
-
` : `${RA}${mwAdmin4}
|
|
3062
|
+
` : `${RA}${verifyUserMeta}${mwAdmin4}
|
|
2855
3063
|
export const POST = async (req, res) => {
|
|
2856
3064
|
${roleGuard} const { id } = req.params;
|
|
2857
3065
|
// TODO: set user.isVerified = true
|
|
@@ -2859,7 +3067,9 @@ ${roleGuard} const { id } = req.params;
|
|
|
2859
3067
|
};
|
|
2860
3068
|
`;
|
|
2861
3069
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "users", "[id]", `verify.${ext}`), verifyUserContent);
|
|
2862
|
-
const exportMeta = mkMeta(opts,
|
|
3070
|
+
const exportMeta = mkMeta(opts, {
|
|
3071
|
+
GET: { description: "Export all users as a CSV download (admin only).", response: `'id,name,email,role,createdAt\\n'` }
|
|
3072
|
+
});
|
|
2863
3073
|
const exportContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2864
3074
|
${exportMeta}${mwAdmin3}
|
|
2865
3075
|
export const GET = async (_req: Request, res: Response) => {
|
|
@@ -2877,7 +3087,10 @@ ${roleGuard} // TODO: generate CSV of all users and stream response
|
|
|
2877
3087
|
};
|
|
2878
3088
|
`;
|
|
2879
3089
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "users", `export.${ext}`), exportContent);
|
|
2880
|
-
const adminsListMeta = mkMeta(opts,
|
|
3090
|
+
const adminsListMeta = mkMeta(opts, {
|
|
3091
|
+
GET: { description: "List all admin accounts (admin only).", response: `{ admins: [], total: 0 }` },
|
|
3092
|
+
POST: { description: "Create a new admin account (admin only).", request: `{ name: 'Jane Doe', email: 'jane@example.com', role: 'admin' }`, response: `{ message: 'Admin created', admin: { id: 'new-id', name: 'Jane Doe', email: 'jane@example.com', role: 'admin' } }`, status: 201 }
|
|
3093
|
+
});
|
|
2881
3094
|
const adminsListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2882
3095
|
${adminsListMeta}${mwAdmin3}
|
|
2883
3096
|
export const GET = async (_req: Request, res: Response) => {
|
|
@@ -2905,8 +3118,13 @@ ${roleGuard} const { name, email, role } = req.body;
|
|
|
2905
3118
|
};
|
|
2906
3119
|
`;
|
|
2907
3120
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "admins", `index.${ext}`), adminsListContent);
|
|
3121
|
+
const adminByIdMeta = mkMeta(opts, {
|
|
3122
|
+
GET: { description: "Fetch a single admin account by ID (admin only).", params: `{ id: 'adm_01HXZ' }`, response: `{ admin: { id: 'adm_01HXZ' } }` },
|
|
3123
|
+
PUT: { description: "Update an admin account by ID (admin only).", params: `{ id: 'adm_01HXZ' }`, request: `{ name: 'Jane Doe', role: 'admin' }`, response: `{ message: 'Admin updated', admin: { id: 'adm_01HXZ', name: 'Jane Doe', role: 'admin' } }` },
|
|
3124
|
+
DELETE: { description: "Delete an admin account by ID (admin only).", params: `{ id: 'adm_01HXZ' }`, response: `{ message: 'Admin adm_01HXZ deleted' }` }
|
|
3125
|
+
});
|
|
2908
3126
|
const adminByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2909
|
-
${mwAdmin3}
|
|
3127
|
+
${adminByIdMeta}${mwAdmin3}
|
|
2910
3128
|
export const GET = async (req: Request, res: Response) => {
|
|
2911
3129
|
${roleGuard} const { id } = req.params;
|
|
2912
3130
|
// TODO: fetch admin by id
|
|
@@ -2924,7 +3142,7 @@ ${roleGuard} const { id } = req.params;
|
|
|
2924
3142
|
// TODO: delete admin
|
|
2925
3143
|
res.json({ message: \`Admin \${id} deleted\` });
|
|
2926
3144
|
};
|
|
2927
|
-
` : `${RA}${mwAdmin3}
|
|
3145
|
+
` : `${RA}${adminByIdMeta}${mwAdmin3}
|
|
2928
3146
|
export const GET = async (req, res) => {
|
|
2929
3147
|
${roleGuard} const { id } = req.params;
|
|
2930
3148
|
// TODO: fetch admin by id
|
|
@@ -2945,8 +3163,12 @@ ${roleGuard} const { id } = req.params;
|
|
|
2945
3163
|
`;
|
|
2946
3164
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "admins", `[id].${ext}`), adminByIdContent);
|
|
2947
3165
|
if (opts.rbac) {
|
|
3166
|
+
const rolesListMeta = mkMeta(opts, {
|
|
3167
|
+
GET: { description: "List all roles (admin only).", response: `{ roles: [] }` },
|
|
3168
|
+
POST: { description: "Create a new role (admin only).", request: `{ name: 'editor', description: 'Can edit content', permissions: ['content:write'] }`, response: `{ message: 'Role created', role: { id: 'new-id', name: 'editor', permissions: ['content:write'] } }`, status: 201 }
|
|
3169
|
+
});
|
|
2948
3170
|
const rolesListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2949
|
-
${mwAdmin3}
|
|
3171
|
+
${rolesListMeta}${mwAdmin3}
|
|
2950
3172
|
export const GET = async (_req: Request, res: Response) => {
|
|
2951
3173
|
// TODO: fetch all roles
|
|
2952
3174
|
res.json({ roles: [] });
|
|
@@ -2958,7 +3180,7 @@ export const POST = async (req: Request, res: Response) => {
|
|
|
2958
3180
|
// TODO: create role
|
|
2959
3181
|
res.status(201).json({ message: 'Role created', role: { id: 'new-id', name, permissions: permissions ?? [] } });
|
|
2960
3182
|
};
|
|
2961
|
-
` : `${RA}${mwAdmin3}
|
|
3183
|
+
` : `${RA}${rolesListMeta}${mwAdmin3}
|
|
2962
3184
|
export const GET = async (_req, res) => {
|
|
2963
3185
|
// TODO: fetch all roles
|
|
2964
3186
|
res.json({ roles: [] });
|
|
@@ -2972,8 +3194,13 @@ export const POST = async (req, res) => {
|
|
|
2972
3194
|
};
|
|
2973
3195
|
`;
|
|
2974
3196
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "roles", `index.${ext}`), rolesListContent);
|
|
3197
|
+
const roleByIdMeta = mkMeta(opts, {
|
|
3198
|
+
GET: { description: "Fetch a single role by ID (admin only).", params: `{ id: 'role_01HXZ' }`, response: `{ role: { id: 'role_01HXZ', name: 'editor', permissions: ['content:write'] } }` },
|
|
3199
|
+
PUT: { description: "Update a role by ID (admin only).", params: `{ id: 'role_01HXZ' }`, request: `{ name: 'editor', permissions: ['content:write'] }`, response: `{ message: 'Role updated', role: { id: 'role_01HXZ', name: 'editor', permissions: ['content:write'] } }` },
|
|
3200
|
+
DELETE: { description: "Delete a role by ID (admin only).", params: `{ id: 'role_01HXZ' }`, response: `{ message: 'Role role_01HXZ deleted' }` }
|
|
3201
|
+
});
|
|
2975
3202
|
const roleByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
2976
|
-
${mwAdmin3}
|
|
3203
|
+
${roleByIdMeta}${mwAdmin3}
|
|
2977
3204
|
export const GET = async (req: Request, res: Response) => {
|
|
2978
3205
|
const { id } = req.params;
|
|
2979
3206
|
// TODO: fetch role by id
|
|
@@ -2991,7 +3218,7 @@ export const DELETE = async (req: Request, res: Response) => {
|
|
|
2991
3218
|
// TODO: delete role
|
|
2992
3219
|
res.json({ message: \`Role \${id} deleted\` });
|
|
2993
3220
|
};
|
|
2994
|
-
` : `${RA}${mwAdmin3}
|
|
3221
|
+
` : `${RA}${roleByIdMeta}${mwAdmin3}
|
|
2995
3222
|
export const GET = async (req, res) => {
|
|
2996
3223
|
const { id } = req.params;
|
|
2997
3224
|
// TODO: fetch role by id
|
|
@@ -3012,8 +3239,12 @@ export const DELETE = async (req, res) => {
|
|
|
3012
3239
|
`;
|
|
3013
3240
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "roles", `[id].${ext}`), roleByIdContent);
|
|
3014
3241
|
}
|
|
3242
|
+
const adminNotifMeta = mkMeta(opts, {
|
|
3243
|
+
GET: { description: "List all sent notifications (admin only).", response: `{ notifications: [], total: 0 }` },
|
|
3244
|
+
POST: { description: "Send a notification to a specific user (admin only).", request: `{ userId: 'usr_01HXZ', title: 'Welcome', message: 'Thanks for joining!', type: 'info' }`, response: `{ message: 'Notification sent' }`, status: 201 }
|
|
3245
|
+
});
|
|
3015
3246
|
const adminNotifContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3016
|
-
${mwAdmin3}
|
|
3247
|
+
${adminNotifMeta}${mwAdmin3}
|
|
3017
3248
|
export const GET = async (_req: Request, res: Response) => {
|
|
3018
3249
|
${roleGuard} // TODO: fetch sent notifications
|
|
3019
3250
|
res.json({ notifications: [], total: 0 });
|
|
@@ -3025,7 +3256,7 @@ ${roleGuard} const { userId, title, message, type } = req.body;
|
|
|
3025
3256
|
// TODO: create and send notification to user
|
|
3026
3257
|
res.status(201).json({ message: 'Notification sent' });
|
|
3027
3258
|
};
|
|
3028
|
-
` : `${RA}${mwAdmin3}
|
|
3259
|
+
` : `${RA}${adminNotifMeta}${mwAdmin3}
|
|
3029
3260
|
export const GET = async (_req, res) => {
|
|
3030
3261
|
${roleGuard} // TODO: fetch sent notifications
|
|
3031
3262
|
res.json({ notifications: [], total: 0 });
|
|
@@ -3039,7 +3270,9 @@ ${roleGuard} const { userId, title, message, type } = req.body;
|
|
|
3039
3270
|
};
|
|
3040
3271
|
`;
|
|
3041
3272
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "notifications", `index.${ext}`), adminNotifContent);
|
|
3042
|
-
const broadcastMeta = mkMeta(opts,
|
|
3273
|
+
const broadcastMeta = mkMeta(opts, {
|
|
3274
|
+
POST: { description: "Broadcast a notification to all users (admin only).", request: `{ title: 'Announcement', message: 'Hello everyone!', type: 'info' }`, response: `{ message: 'Broadcast sent', count: 0 }` }
|
|
3275
|
+
});
|
|
3043
3276
|
const broadcastContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3044
3277
|
${broadcastMeta}${mwAdmin3}
|
|
3045
3278
|
export const POST = async (req: Request, res: Response) => {
|
|
@@ -3058,7 +3291,9 @@ ${roleGuard} const { title, message, type } = req.body;
|
|
|
3058
3291
|
`;
|
|
3059
3292
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "notifications", `broadcast.${ext}`), broadcastContent);
|
|
3060
3293
|
const mkLogContent = (label) => {
|
|
3061
|
-
const meta = mkMeta(opts,
|
|
3294
|
+
const meta = mkMeta(opts, {
|
|
3295
|
+
GET: { description: `Paginated ${label} log entries (admin only).`, query: `{ page: '1', limit: '50' }`, response: `{ logs: [], total: 0, page: 1, limit: 50 }` }
|
|
3296
|
+
});
|
|
3062
3297
|
return ts ? `${RA}import type { Request, Response } from 'express';
|
|
3063
3298
|
${meta}${mwAdmin3}
|
|
3064
3299
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -3077,7 +3312,10 @@ ${roleGuard} const { page = 1, limit = 50 } = req.query;
|
|
|
3077
3312
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "logs", `audit.${ext}`), mkLogContent("audit"));
|
|
3078
3313
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "logs", `activity.${ext}`), mkLogContent("activity"));
|
|
3079
3314
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "logs", `security.${ext}`), mkLogContent("security"));
|
|
3080
|
-
const settingsMeta = mkMeta(opts,
|
|
3315
|
+
const settingsMeta = mkMeta(opts, {
|
|
3316
|
+
GET: { description: "Get system-wide settings (admin only).", response: `{ settings: {} }` },
|
|
3317
|
+
PUT: { description: "Update system-wide settings (admin only).", request: `{ maintenanceMode: false }`, response: `{ message: 'Settings updated', settings: { maintenanceMode: false } }` }
|
|
3318
|
+
});
|
|
3081
3319
|
const settingsContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3082
3320
|
${settingsMeta}${mwAdmin3}
|
|
3083
3321
|
export const GET = async (_req: Request, res: Response) => {
|
|
@@ -3101,7 +3339,9 @@ ${roleGuard} // TODO: update system settings
|
|
|
3101
3339
|
};
|
|
3102
3340
|
`;
|
|
3103
3341
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "settings", `index.${ext}`), settingsContent);
|
|
3104
|
-
const healthMeta = mkMeta(opts,
|
|
3342
|
+
const healthMeta = mkMeta(opts, {
|
|
3343
|
+
GET: { description: "System health check: DB, queue, cache status (admin only).", response: `{ status: 'ok', db: 'ok', queue: 'ok', uptime: 12345 }` }
|
|
3344
|
+
});
|
|
3105
3345
|
const healthContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3106
3346
|
${healthMeta}${mwAdmin3}
|
|
3107
3347
|
export const GET = async (_req: Request, res: Response) => {
|
|
@@ -3115,7 +3355,9 @@ ${roleGuard} // TODO: check DB connection, queue, cache health
|
|
|
3115
3355
|
};
|
|
3116
3356
|
`;
|
|
3117
3357
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "system", `health.${ext}`), healthContent);
|
|
3118
|
-
const cacheMeta = mkMeta(opts,
|
|
3358
|
+
const cacheMeta = mkMeta(opts, {
|
|
3359
|
+
DELETE: { description: "Flush the application cache (admin only).", response: `{ message: 'Cache cleared' }` }
|
|
3360
|
+
});
|
|
3119
3361
|
const cacheContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3120
3362
|
${cacheMeta}${mwAdmin3}
|
|
3121
3363
|
export const DELETE = async (_req: Request, res: Response) => {
|
|
@@ -3129,7 +3371,9 @@ ${roleGuard} // TODO: flush Redis or in-memory cache
|
|
|
3129
3371
|
};
|
|
3130
3372
|
`;
|
|
3131
3373
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "system", `cache.${ext}`), cacheContent);
|
|
3132
|
-
const adminTicketsListMeta = mkMeta(opts,
|
|
3374
|
+
const adminTicketsListMeta = mkMeta(opts, {
|
|
3375
|
+
GET: { description: "List all support tickets with filters (admin only).", query: `{ status: 'open', priority: 'normal', page: '1', limit: '20' }`, response: `{ tickets: [], total: 0, page: 1, limit: 20 }` }
|
|
3376
|
+
});
|
|
3133
3377
|
const adminTicketsListContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3134
3378
|
${adminTicketsListMeta}${mwAdmin3}
|
|
3135
3379
|
export const GET = async (req: Request, res: Response) => {
|
|
@@ -3145,8 +3389,12 @@ ${roleGuard} const { status, priority, page = 1, limit = 20 } = req.query;
|
|
|
3145
3389
|
};
|
|
3146
3390
|
`;
|
|
3147
3391
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "tickets", `index.${ext}`), adminTicketsListContent);
|
|
3392
|
+
const adminTicketByIdMeta = mkMeta(opts, {
|
|
3393
|
+
GET: { description: "Fetch a single support ticket by ID (admin only).", params: `{ id: 'tk_01HXZ' }`, response: `{ ticket: { id: 'tk_01HXZ' } }` },
|
|
3394
|
+
PUT: { description: "Assign, reply to, or change the status of a support ticket (admin only).", params: `{ id: 'tk_01HXZ' }`, request: `{ reply: 'Looking into it.', status: 'in_progress', assignedTo: 'adm_01HXZ' }`, response: `{ message: 'Ticket updated', ticket: { id: 'tk_01HXZ', status: 'in_progress', assignedTo: 'adm_01HXZ' } }` }
|
|
3395
|
+
});
|
|
3148
3396
|
const adminTicketByIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3149
|
-
${mwAdmin3}
|
|
3397
|
+
${adminTicketByIdMeta}${mwAdmin3}
|
|
3150
3398
|
export const GET = async (req: Request, res: Response) => {
|
|
3151
3399
|
${roleGuard} const { id } = req.params;
|
|
3152
3400
|
// TODO: fetch ticket by id
|
|
@@ -3159,7 +3407,7 @@ ${roleGuard} const { id } = req.params;
|
|
|
3159
3407
|
// TODO: update ticket (assign, change status, add reply)
|
|
3160
3408
|
res.json({ message: 'Ticket updated', ticket: { id, status, assignedTo } });
|
|
3161
3409
|
};
|
|
3162
|
-
` : `${RA}${mwAdmin3}
|
|
3410
|
+
` : `${RA}${adminTicketByIdMeta}${mwAdmin3}
|
|
3163
3411
|
export const GET = async (req, res) => {
|
|
3164
3412
|
${roleGuard} const { id } = req.params;
|
|
3165
3413
|
// TODO: fetch ticket by id
|
|
@@ -3175,7 +3423,15 @@ ${roleGuard} const { id } = req.params;
|
|
|
3175
3423
|
`;
|
|
3176
3424
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "tickets", `[id].${ext}`), adminTicketByIdContent);
|
|
3177
3425
|
const mkContentCrud = (name, namePlural, dir, createFields) => {
|
|
3178
|
-
const
|
|
3426
|
+
const lower = name.toLowerCase();
|
|
3427
|
+
const firstField = createFields.split(", ")[0];
|
|
3428
|
+
const sampleCreateBody = `{ ${createFields.split(", ").map((f) => `${f}: 'sample-${f}'`).join(", ")} }`;
|
|
3429
|
+
const sampleCreatedRecord = `{ id: 'new-id', ${firstField}: 'sample-${firstField}' }`;
|
|
3430
|
+
const sampleFullRecord = `{ id: '${lower}_01HXZ', ${createFields.split(", ").map((f) => `${f}: 'sample-${f}'`).join(", ")} }`;
|
|
3431
|
+
const listMeta = mkMeta(opts, {
|
|
3432
|
+
GET: { description: `List all ${namePlural} (admin only).`, response: `{ ${namePlural}: [], total: 0 }` },
|
|
3433
|
+
POST: { description: `Create a new ${lower} (admin only).`, request: sampleCreateBody, response: `{ message: '${name} created', ${lower}: ${sampleCreatedRecord} }`, status: 201 }
|
|
3434
|
+
});
|
|
3179
3435
|
const listContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3180
3436
|
${listMeta}${mwAdmin4}
|
|
3181
3437
|
export const GET = async (_req: Request, res: Response) => {
|
|
@@ -3186,7 +3442,7 @@ ${roleGuard} // TODO: fetch ${namePlural}
|
|
|
3186
3442
|
export const POST = async (req: Request, res: Response) => {
|
|
3187
3443
|
${roleGuard} const { ${createFields} } = req.body;
|
|
3188
3444
|
// TODO: create ${name}
|
|
3189
|
-
res.status(201).json({ message: '${name} created', ${
|
|
3445
|
+
res.status(201).json({ message: '${name} created', ${lower}: { id: 'new-id', ${firstField} } });
|
|
3190
3446
|
};
|
|
3191
3447
|
` : `${RA}${listMeta}${mwAdmin4}
|
|
3192
3448
|
export const GET = async (_req, res) => {
|
|
@@ -3197,21 +3453,26 @@ ${roleGuard} // TODO: fetch ${namePlural}
|
|
|
3197
3453
|
export const POST = async (req, res) => {
|
|
3198
3454
|
${roleGuard} const { ${createFields} } = req.body;
|
|
3199
3455
|
// TODO: create ${name}
|
|
3200
|
-
res.status(201).json({ message: '${name} created', ${
|
|
3456
|
+
res.status(201).json({ message: '${name} created', ${lower}: { id: 'new-id' } });
|
|
3201
3457
|
};
|
|
3202
3458
|
`;
|
|
3459
|
+
const byIdMeta = mkMeta(opts, {
|
|
3460
|
+
GET: { description: `Fetch a single ${lower} by ID (admin only).`, params: `{ id: '${lower}_01HXZ' }`, response: `{ ${lower}: ${sampleFullRecord} }` },
|
|
3461
|
+
PUT: { description: `Update a ${lower} by ID (admin only).`, params: `{ id: '${lower}_01HXZ' }`, request: sampleCreateBody, response: `{ message: '${name} updated', ${lower}: ${sampleFullRecord} }` },
|
|
3462
|
+
DELETE: { description: `Delete a ${lower} by ID (admin only).`, params: `{ id: '${lower}_01HXZ' }`, response: `{ message: '${name} ${lower}_01HXZ deleted' }` }
|
|
3463
|
+
});
|
|
3203
3464
|
const byIdContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3204
|
-
${mwAdmin4}
|
|
3465
|
+
${byIdMeta}${mwAdmin4}
|
|
3205
3466
|
export const GET = async (req: Request, res: Response) => {
|
|
3206
3467
|
${roleGuard} const { id } = req.params;
|
|
3207
3468
|
// TODO: fetch ${name} by id
|
|
3208
|
-
res.json({ ${
|
|
3469
|
+
res.json({ ${lower}: { id } });
|
|
3209
3470
|
};
|
|
3210
3471
|
|
|
3211
3472
|
export const PUT = async (req: Request, res: Response) => {
|
|
3212
3473
|
${roleGuard} const { id } = req.params;
|
|
3213
3474
|
// TODO: update ${name}
|
|
3214
|
-
res.json({ message: '${name} updated', ${
|
|
3475
|
+
res.json({ message: '${name} updated', ${lower}: { id, ...req.body } });
|
|
3215
3476
|
};
|
|
3216
3477
|
|
|
3217
3478
|
export const DELETE = async (req: Request, res: Response) => {
|
|
@@ -3219,17 +3480,17 @@ ${roleGuard} const { id } = req.params;
|
|
|
3219
3480
|
// TODO: delete ${name}
|
|
3220
3481
|
res.json({ message: \`${name} \${id} deleted\` });
|
|
3221
3482
|
};
|
|
3222
|
-
` : `${RA}${mwAdmin4}
|
|
3483
|
+
` : `${RA}${byIdMeta}${mwAdmin4}
|
|
3223
3484
|
export const GET = async (req, res) => {
|
|
3224
3485
|
${roleGuard} const { id } = req.params;
|
|
3225
3486
|
// TODO: fetch ${name} by id
|
|
3226
|
-
res.json({ ${
|
|
3487
|
+
res.json({ ${lower}: { id } });
|
|
3227
3488
|
};
|
|
3228
3489
|
|
|
3229
3490
|
export const PUT = async (req, res) => {
|
|
3230
3491
|
${roleGuard} const { id } = req.params;
|
|
3231
3492
|
// TODO: update ${name}
|
|
3232
|
-
res.json({ message: '${name} updated', ${
|
|
3493
|
+
res.json({ message: '${name} updated', ${lower}: { id, ...req.body } });
|
|
3233
3494
|
};
|
|
3234
3495
|
|
|
3235
3496
|
export const DELETE = async (req, res) => {
|
|
@@ -3255,7 +3516,9 @@ ${roleGuard} const { id } = req.params;
|
|
|
3255
3516
|
const { listContent: couponList, byIdContent: couponById } = mkContentCrud("Coupon", "coupons", [], "code, type, value");
|
|
3256
3517
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "billing", "coupons", `index.${ext}`), couponList);
|
|
3257
3518
|
await fs.outputFile(path.join(dest, "src", "api", "admin", "billing", "coupons", `[id].${ext}`), couponById);
|
|
3258
|
-
const adminSubsMeta = mkMeta(opts,
|
|
3519
|
+
const adminSubsMeta = mkMeta(opts, {
|
|
3520
|
+
GET: { description: "List all subscriptions with filters (admin only).", query: `{ status: 'active', page: '1', limit: '20' }`, response: `{ subscriptions: [], total: 0, page: 1, limit: 20 }` }
|
|
3521
|
+
});
|
|
3259
3522
|
const adminSubsContent = ts ? `${RA}import type { Request, Response } from 'express';
|
|
3260
3523
|
${adminSubsMeta}${mwAdmin4}
|
|
3261
3524
|
export const GET = async (req: Request, res: Response) => {
|