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 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
- description: 'Health check \u2014 returns server status and current timestamp.',
187
- response: { status: 200, body: { status: 'OK', timestamp: '2024-01-01T00:00:00.000Z' } },
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
- description: 'Health check \u2014 returns server status and current timestamp.',
193
- response: { status: 200, body: { status: 'OK', timestamp: '2024-01-01T00:00:00.000Z' } },
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
- description: 'Authenticate a user and issue a JWT.',
424
- request: { body: { email: 'user@example.com', password: 'user' } },
425
- response: { status: 200, body: { message: 'Logged in as user' } },
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
- description: 'Authenticate a user and issue a JWT.',
430
- request: { body: { email: 'user@example.com', password: 'user' } },
431
- response: { status: 200, body: { message: 'Logged in as user' } },
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
- description: 'Clear the auth cookie and log the user out.',
541
- response: { status: 200, body: { message: 'Logged out successfully' } },
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
- description: 'Clear the auth cookie and log the user out.',
546
- response: { status: 200, body: { message: 'Logged out successfully' } },
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
- description: 'Register a new user account.',
571
- request: { body: { name: 'Jane Doe', email: 'jane@example.com', password: 'secret' } },
572
- response: { status: 201, body: { message: 'Account created successfully' } },
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
- description: 'Register a new user account.',
577
- request: { body: { name: 'Jane Doe', email: 'jane@example.com', password: 'secret' } },
578
- response: { status: 201, body: { message: 'Account created successfully' } },
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
- description: 'Return the currently authenticated user.',
647
- response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
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
- description: 'Return the currently authenticated user.',
652
- response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
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
- description: 'Admin dashboard stats. Requires admin role.',
695
- response: { status: 200, body: { stats: { users: 120, revenue: 5000 } } },
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
- description: 'Admin dashboard stats. Requires admin role.',
700
- response: { status: 200, body: { stats: { users: 120, revenue: 5000 } } },
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
- description: 'List all users (admin only).',
746
- response: { status: 200, body: { users: [], total: 0 } },
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
- description: 'List all users (admin only).',
751
- response: { status: 200, body: { users: [], total: 0 } },
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
- description: 'Get, update, or delete a single user by ID (admin only).',
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
- description: 'Get, update, or delete a single user by ID (admin only).',
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
- description: "View or update the authenticated user's profile.",
948
- response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
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
- description: "View or update the authenticated user's profile.",
953
- response: { status: 200, body: { user: { id: '1', role: 'user', email: 'user@example.com' } } },
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, description, body, req) {
1104
+ function mkMeta(opts, methods) {
1023
1105
  if (!opts.routeDocs) return "";
1024
- const reqLine = req ? ` request: { body: ${req} },
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
- description: '${description}',
1030
- ${reqLine} response: { status: 200, body: ${body} },
1125
+ ${blocks}
1031
1126
  };
1032
1127
 
1033
1128
  ` : `export const meta = {
1034
- description: '${description}',
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, "Refresh the JWT and issue a new token.", `{ message: 'Token refreshed' }`);
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, "Verify email address via token or resend verification email.", `{ message: 'Email verified' }`);
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, "Send a password reset email to the given address.", `{ message: 'Reset email sent' }`, `{ email: 'user@example.com' }`);
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, "Reset password using a valid reset token.", `{ message: 'Password reset successfully' }`, `{ token: 'reset-token', password: 'newpassword' }`);
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, "Change password for the authenticated user.", `{ message: 'Password changed successfully' }`, `{ oldPassword: 'current', newPassword: 'newpassword' }`);
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, "Get 2FA QR code (GET) or enable 2FA with a verified TOTP code (POST).", `{ message: '2FA enabled' }`);
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, "Verify a TOTP code during login.", `{ message: '2FA verified' }`);
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, "Disable 2FA for the authenticated user.", `{ message: '2FA disabled' }`);
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, "List all active sessions for the authenticated user.", `{ sessions: [] }`);
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, "Upload (POST) or remove (DELETE) the authenticated user avatar.", `{ message: 'Avatar updated', url: 'https://...' }`);
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, "Get or update account settings (notifications, language, theme, privacy).", `{ settings: { notifications: true, language: 'en', theme: 'system' } }`);
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, "Delete account (DELETE) or download personal data (GET).", `{ message: 'Account deleted' }`);
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, "Personal dashboard: stats, recent activity, and quick actions.", `{ stats: {}, recentActivity: [], quickActions: [] }`);
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, "Paginated activity history for the authenticated user.", `{ activities: [], total: 0, page: 1, limit: 20 }`);
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, "List notifications (GET) or mark all as read (POST).", `{ notifications: [], total: 0, unread: 0 }`);
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, "List uploaded files with storage usage (GET) or upload a new file (POST).", `{ files: [], total: 0, storageUsed: 0 }`);
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, "Search with filters, sort, and pagination.", `{ results: [], total: 0, page: 1, limit: 20 }`);
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, "List API keys (GET) or create a new one (POST).", `{ apiKeys: [] }`);
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, "List all available subscription plans.", `{ plans: [] }`);
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, "Get current subscription (GET), subscribe to a plan (POST), or cancel (DELETE).", `{ subscription: null }`);
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, "List all invoices for the authenticated user.", `{ invoices: [], total: 0 }`);
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, "List own support tickets (GET) or create a new one (POST).", `{ tickets: [], total: 0 }`, `{ subject: 'Issue with login', message: 'I cannot log in', priority: 'normal' }`);
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, "View a support ticket (GET) or add a reply / close it (PUT).", `{ ticket: { id: '1', subject: 'Issue', status: 'open', replies: [] } }`);
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, "Analytics overview: users, revenue, traffic.", `{ users: {}, revenue: {}, traffic: {} }`);
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, "User analytics: registrations, active users, churn.", `{ registrations: [], activeUsers: 0, churn: 0 }`);
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, "Revenue analytics: MRR, ARR, payment history.", `{ mrr: 0, arr: 0, history: [] }`);
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, "Traffic analytics: page views, devices, countries.", `{ pageViews: 0, devices: {}, countries: {} }`);
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, "Export all users as CSV.", `{ csv: '...' }`);
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, "List all admins (GET) or create a new admin (POST).", `{ admins: [], total: 0 }`);
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, "Broadcast a notification to all users.", `{ message: 'Broadcast sent', count: 0 }`, `{ title: 'Announcement', message: 'Hello everyone!', type: 'info' }`);
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, `Paginated ${label} log entries.`, `{ logs: [], total: 0, page: 1, limit: 50 }`);
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, "Get or update system-wide settings.", `{ settings: {} }`);
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, "System health check: DB, queue, cache status.", `{ status: 'ok', db: 'ok', queue: 'ok' }`);
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, "Flush the application cache.", `{ message: 'Cache cleared' }`);
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, "List all support tickets with filters.", `{ tickets: [], total: 0 }`);
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 listMeta = mkMeta(opts, `List ${namePlural} (GET) or create a new ${name} (POST).`, `{ ${namePlural}: [], total: 0 }`);
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', ${name.toLowerCase()}: { id: 'new-id', ${createFields.split(", ")[0]} } });
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', ${name.toLowerCase()}: { id: 'new-id' } });
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({ ${name.toLowerCase()}: { id } });
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', ${name.toLowerCase()}: { id, ...req.body } });
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({ ${name.toLowerCase()}: { id } });
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', ${name.toLowerCase()}: { id, ...req.body } });
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, "List all subscriptions with filters.", `{ subscriptions: [], total: 0 }`);
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) => {