claudmax 2.0.0 → 2.0.1

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.
Files changed (124) hide show
  1. package/claudmax-1.0.16.tgz +0 -0
  2. package/{packages/cli/index.js → index.js} +2 -0
  3. package/package.json +27 -55
  4. package/.claude/settings.local.json +0 -7
  5. package/.env.example +0 -24
  6. package/.github/workflows/publish.yml +0 -31
  7. package/README.md +0 -178
  8. package/claudmax-mcp-1.0.2.tgz +0 -0
  9. package/help +0 -0
  10. package/help-wal +0 -0
  11. package/next-env.d.ts +0 -6
  12. package/next.config.mjs +0 -43
  13. package/packages/cli/claudmax-1.0.16.tgz +0 -0
  14. package/packages/cli/package.json +0 -33
  15. package/packages/mcp/claudmax-mcp-1.0.0.tgz +0 -0
  16. package/packages/mcp/claudmax-mcp-1.0.1.tgz +0 -0
  17. package/packages/mcp/claudmax-mcp-1.0.2.tgz +0 -0
  18. package/packages/mcp/claudmax-mcp-1.0.3.tgz +0 -0
  19. package/packages/mcp/index.js +0 -129
  20. package/packages/mcp/package-lock.json +0 -1146
  21. package/packages/mcp/package.json +0 -32
  22. package/postcss.config.mjs +0 -6
  23. package/prisma/schema.prisma +0 -130
  24. package/prisma/seed.ts +0 -27
  25. package/public/favicon.svg +0 -10
  26. package/public/robots.txt +0 -10
  27. package/run_build.sh +0 -4
  28. package/scripts/migrate-plans.js +0 -98
  29. package/scripts/seed-blog.ts +0 -1014
  30. package/src/app/admin/dashboard/AdminDashboardClient.tsx +0 -1546
  31. package/src/app/admin/dashboard/page.tsx +0 -13
  32. package/src/app/admin/page.tsx +0 -132
  33. package/src/app/api/admin/auth/me/route.ts +0 -34
  34. package/src/app/api/admin/health/route.ts +0 -110
  35. package/src/app/api/admin/keys/[id]/route.ts +0 -116
  36. package/src/app/api/admin/keys/route.ts +0 -192
  37. package/src/app/api/admin/keys-list/route.ts +0 -81
  38. package/src/app/api/admin/login/route.ts +0 -72
  39. package/src/app/api/admin/logout/route.ts +0 -8
  40. package/src/app/api/admin/migrate/route.ts +0 -133
  41. package/src/app/api/admin/plans/[id]/route.ts +0 -65
  42. package/src/app/api/admin/plans/route.ts +0 -66
  43. package/src/app/api/admin/posts/[id]/route.ts +0 -81
  44. package/src/app/api/admin/posts/route.ts +0 -83
  45. package/src/app/api/admin/seed/route.ts +0 -145
  46. package/src/app/api/admin/settings/route.ts +0 -44
  47. package/src/app/api/admin/stats/route.ts +0 -74
  48. package/src/app/api/admin/users/[id]/route.ts +0 -166
  49. package/src/app/api/admin/users/plans/route.ts +0 -45
  50. package/src/app/api/admin/users/route.ts +0 -202
  51. package/src/app/api/blog/[slug]/route.ts +0 -22
  52. package/src/app/api/blog/route.ts +0 -40
  53. package/src/app/api/cron/daily-status/route.ts +0 -208
  54. package/src/app/api/support/chat/route.ts +0 -55
  55. package/src/app/api/support/chat/session/route.ts +0 -62
  56. package/src/app/api/support/chat/stream/route.ts +0 -44
  57. package/src/app/api/support/email/route.ts +0 -63
  58. package/src/app/api/tools/understand_image/route.ts +0 -113
  59. package/src/app/api/tools/upload/route.ts +0 -179
  60. package/src/app/api/tools/web_search/route.ts +0 -99
  61. package/src/app/api/v1/audio/route.ts +0 -67
  62. package/src/app/api/v1/audio/speech/route.ts +0 -73
  63. package/src/app/api/v1/chat/completions/route.ts +0 -3
  64. package/src/app/api/v1/chat/route.ts +0 -1079
  65. package/src/app/api/v1/images/generations/route.ts +0 -93
  66. package/src/app/api/v1/info/route.ts +0 -30
  67. package/src/app/api/v1/key-status/route.ts +0 -109
  68. package/src/app/api/v1/key-status/stream/route.ts +0 -135
  69. package/src/app/api/v1/messages/count_tokens/route.ts +0 -22
  70. package/src/app/api/v1/messages/route.ts +0 -807
  71. package/src/app/api/v1/models/route.ts +0 -14
  72. package/src/app/api/v1/route.ts +0 -18
  73. package/src/app/blog/BlogClient.tsx +0 -193
  74. package/src/app/blog/[slug]/page.tsx +0 -117
  75. package/src/app/blog/page.tsx +0 -20
  76. package/src/app/check-usage/CheckUsageClient.tsx +0 -186
  77. package/src/app/check-usage/layout.tsx +0 -11
  78. package/src/app/check-usage/page.tsx +0 -15
  79. package/src/app/docs/layout.tsx +0 -16
  80. package/src/app/docs/page.tsx +0 -1055
  81. package/src/app/faq/FAQClient.tsx +0 -227
  82. package/src/app/faq/page.tsx +0 -21
  83. package/src/app/globals.css +0 -75
  84. package/src/app/layout.tsx +0 -80
  85. package/src/app/page.tsx +0 -256
  86. package/src/app/reseller/ResellerClient.tsx +0 -435
  87. package/src/app/reseller/page.tsx +0 -15
  88. package/src/app/setup.ps1/route.ts +0 -79
  89. package/src/app/setup.sh/route.ts +0 -113
  90. package/src/app/sitemap.ts +0 -50
  91. package/src/app/status/StatusClient.tsx +0 -103
  92. package/src/app/status/layout.tsx +0 -11
  93. package/src/app/status/page.tsx +0 -15
  94. package/src/app/support/SupportClient.tsx +0 -411
  95. package/src/app/support/page.tsx +0 -25
  96. package/src/app/v1/chat/completions/route.ts +0 -3
  97. package/src/app/v1/chat/route.ts +0 -4
  98. package/src/app/v1/messages/route.ts +0 -3
  99. package/src/components/Footer.tsx +0 -120
  100. package/src/components/Header.tsx +0 -131
  101. package/src/components/landing/features.tsx +0 -99
  102. package/src/components/ui/badge.tsx +0 -32
  103. package/src/components/ui/button.tsx +0 -46
  104. package/src/components/ui/card.tsx +0 -50
  105. package/src/components/ui/dialog.tsx +0 -97
  106. package/src/components/ui/dropdown-menu.tsx +0 -156
  107. package/src/components/ui/input.tsx +0 -21
  108. package/src/components/ui/label.tsx +0 -15
  109. package/src/components/ui/separator.tsx +0 -22
  110. package/src/components/ui/switch.tsx +0 -27
  111. package/src/components/ui/tabs.tsx +0 -51
  112. package/src/components/ui/toast.tsx +0 -103
  113. package/src/lib/auth.ts +0 -45
  114. package/src/lib/prisma.ts +0 -20
  115. package/src/lib/providers.ts +0 -158
  116. package/src/lib/security.ts +0 -165
  117. package/src/lib/utils.ts +0 -14
  118. package/src/middleware.ts +0 -30
  119. package/tailwind.config.ts +0 -53
  120. package/tsconfig.json +0 -41
  121. package/tsconfig.tsbuildinfo +0 -1
  122. package/vercel.json +0 -8
  123. /package/{packages/cli/bin → bin}/claudmax.js +0 -0
  124. /package/{packages/cli/claudmax-1.0.17.tgz → claudmax-1.0.17.tgz} +0 -0
@@ -1,72 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
- import bcrypt from 'bcryptjs';
4
- import { signToken } from '@/lib/auth';
5
-
6
- export async function POST(req: Request) {
7
- try {
8
- const { username, password } = await req.json();
9
-
10
- if (!username || !password) {
11
- return NextResponse.json({ error: 'Username and password are required' }, { status: 400 });
12
- }
13
-
14
- const admin = await prisma.admin.findUnique({ where: { username } });
15
-
16
- if (!admin) {
17
- return NextResponse.json({ error: 'Invalid username or password' }, { status: 401 });
18
- }
19
-
20
- if (!admin.isActive) {
21
- return NextResponse.json({ error: 'Invalid username or password' }, { status: 401 });
22
- }
23
-
24
- const valid = await bcrypt.compare(password, admin.password);
25
- if (!valid) {
26
- return NextResponse.json({ error: 'Invalid username or password' }, { status: 401 });
27
- }
28
-
29
- const token = signToken({
30
- id: admin.id,
31
- username: admin.username,
32
- name: admin.name,
33
- role: admin.role as 'super_admin' | 'admin' | 'reseller',
34
- isActive: admin.isActive,
35
- canCreateKey: admin.canCreateKey,
36
- canDeleteKey: admin.canDeleteKey,
37
- canBlockKey: admin.canBlockKey,
38
- canManageTokens: admin.canManageTokens,
39
- canCreateReseller: admin.canCreateReseller,
40
- canManageResellers: admin.canManageResellers,
41
- canManageUsers: admin.canManageUsers,
42
- });
43
-
44
- const { cookies } = await import('next/headers');
45
- const cookieStore = await cookies();
46
- cookieStore.set('admin_token', token, {
47
- httpOnly: true,
48
- secure: process.env.NODE_ENV === 'production',
49
- sameSite: 'lax',
50
- maxAge: 60 * 60 * 24,
51
- path: '/',
52
- });
53
-
54
- return NextResponse.json({
55
- id: admin.id,
56
- username: admin.username,
57
- name: admin.name,
58
- role: admin.role,
59
- isActive: admin.isActive,
60
- canCreateKey: admin.canCreateKey,
61
- canDeleteKey: admin.canDeleteKey,
62
- canBlockKey: admin.canBlockKey,
63
- canManageTokens: admin.canManageTokens,
64
- canCreateReseller: admin.canCreateReseller,
65
- canManageResellers: admin.canManageResellers,
66
- canManageUsers: admin.canManageUsers,
67
- });
68
- } catch (err) {
69
- console.error('Admin login error:', err);
70
- return NextResponse.json({ error: 'Login failed' }, { status: 500 });
71
- }
72
- }
@@ -1,8 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { cookies } from 'next/headers';
3
-
4
- export async function POST() {
5
- const cookieStore = await cookies();
6
- cookieStore.delete('admin_token');
7
- return NextResponse.json({ success: true });
8
- }
@@ -1,133 +0,0 @@
1
- /**
2
- * Migration route: Fix Plan.tokensPerWindow from TEXT/BigInt to INTEGER
3
- * Dynamically inspects existing schema and migrates data safely.
4
- * Call: POST /api/admin/migrate with { migrate: true }
5
- */
6
- import { NextResponse } from 'next/server';
7
- import { createClient } from '@libsql/client';
8
- import { getAuthUser } from '@/lib/auth';
9
-
10
- export async function POST(req: Request) {
11
- const me = await getAuthUser();
12
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
13
- if (me.role !== 'super_admin') return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
14
-
15
- const { migrate } = await req.json();
16
- if (!migrate) return NextResponse.json({ error: 'migrate flag required' }, { status: 400 });
17
-
18
- const databaseUrl = process.env.DATABASE_URL;
19
- if (!databaseUrl) {
20
- return NextResponse.json({ error: 'DATABASE_URL not configured' }, { status: 500 });
21
- }
22
-
23
- // Parse auth token from DATABASE_URL (format: libsql://host?authToken=xxx)
24
- const url = new URL(databaseUrl);
25
- const authToken = url.searchParams.get('authToken') ?? undefined;
26
- const dbUrl = `${url.protocol}//${url.host}${url.pathname}`;
27
- const db = createClient({ url: dbUrl, authToken });
28
-
29
- try {
30
- // Step 1: Inspect actual table schema
31
- const tableInfo = await db.execute("PRAGMA table_info(Plan)");
32
- const cols = tableInfo.rows as any[];
33
- const colMap: Record<string, any> = {};
34
- for (const c of cols) colMap[c.name] = c;
35
- console.log('Current Plan columns:', Object.keys(colMap));
36
-
37
- if (!colMap['tokensPerWindow']) {
38
- return NextResponse.json({ error: 'tokensPerWindow column not found' }, { status: 500 });
39
- }
40
- console.log(`tokensPerWindow type: ${colMap['tokensPerWindow'].type}`);
41
-
42
- // Step 2: Read all existing plan data
43
- const plans = await db.execute("SELECT * FROM Plan");
44
- console.log(`Found ${plans.rows.length} plan(s)`);
45
-
46
- for (const row of plans.rows as any[]) {
47
- const raw = String(row.tokensPerWindow ?? '5000000').replace('.0', '');
48
- const cleaned = isNaN(parseInt(raw)) ? 500000 : parseInt(raw);
49
- console.log(` "${row.name}": tokensPerWindow="${row.tokensPerWindow}" -> ${cleaned}`);
50
- }
51
-
52
- // Step 3: Get all column names from existing table
53
- const existingCols = Object.keys(colMap);
54
- const newCols = ['id', 'name', 'tier', 'tokensPerWindow', 'requestsPerWindow',
55
- 'durationDays', 'displayMultiplier', 'minDurationDays', 'maxDurationDays',
56
- 'isActive', 'createdAt', 'updatedAt', 'createdByAdminId', 'adminsAllowed'];
57
-
58
- // Build new table
59
- await db.execute("DROP TABLE IF EXISTS _Plan_new");
60
- await db.execute(`
61
- CREATE TABLE _Plan_new (
62
- id TEXT PRIMARY KEY,
63
- name TEXT NOT NULL,
64
- tier TEXT NOT NULL,
65
- tokensPerWindow INTEGER NOT NULL,
66
- requestsPerWindow INTEGER NOT NULL,
67
- durationDays INTEGER NOT NULL,
68
- displayMultiplier REAL DEFAULT 1.0,
69
- minDurationDays INTEGER DEFAULT 0,
70
- maxDurationDays INTEGER DEFAULT -1,
71
- isActive INTEGER DEFAULT 1,
72
- createdAt TEXT NOT NULL,
73
- updatedAt TEXT NOT NULL,
74
- createdByAdminId TEXT,
75
- adminsAllowed TEXT DEFAULT '[]'
76
- )
77
- `);
78
-
79
- // Step 4: Copy rows, converting tokensPerWindow to clean integer
80
- for (const row of plans.rows as any[]) {
81
- const raw = String(row.tokensPerWindow ?? '5000000').replace('.0', '');
82
- const cleaned = isNaN(parseInt(raw)) ? 500000 : parseInt(raw);
83
-
84
- const vals: any[] = [];
85
- const usedCols: string[] = [];
86
- for (const c of newCols) {
87
- usedCols.push(c);
88
- if (c === 'tokensPerWindow') {
89
- vals.push(cleaned);
90
- } else if (c === 'isActive') {
91
- vals.push(row.isActive ?? 1);
92
- } else if (row[c] !== undefined) {
93
- vals.push(row[c]);
94
- } else if (c === 'createdByAdminId') {
95
- vals.push(null);
96
- } else if (c === 'adminsAllowed') {
97
- vals.push('[]');
98
- } else {
99
- vals.push(null);
100
- }
101
- }
102
-
103
- try {
104
- await db.execute({
105
- sql: `INSERT INTO _Plan_new (${usedCols.join(', ')}) VALUES (${usedCols.map(() => '?').join(', ')})`,
106
- args: vals,
107
- });
108
- } catch (insertErr: any) {
109
- console.error(`Failed to insert plan "${row.name}":`, insertErr);
110
- }
111
- }
112
-
113
- // Step 5: Swap tables
114
- await db.execute("DROP TABLE Plan");
115
- await db.execute("ALTER TABLE _Plan_new RENAME TO Plan");
116
-
117
- // Step 6: Verify
118
- const verify = await db.execute("SELECT id, name, tokensPerWindow, typeof(tokensPerWindow) as type FROM Plan");
119
- console.log('\nPost-migration:');
120
- for (const row of verify.rows as any[]) {
121
- console.log(` ${row.name}: ${row.tokensPerWindow} (type: ${row.type})`);
122
- }
123
-
124
- return NextResponse.json({
125
- success: true,
126
- message: 'Plan.tokensPerWindow migrated to INTEGER',
127
- plansMigrated: verify.rows.length,
128
- });
129
- } catch (err: any) {
130
- console.error('Migration error:', err);
131
- return NextResponse.json({ error: 'Migration failed', detail: String(err) }, { status: 500 });
132
- }
133
- }
@@ -1,65 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
- import { getAuthUser } from '@/lib/auth';
4
-
5
- type Params = { params: Promise<{ id: string }> };
6
-
7
- export async function GET(req: Request, { params }: Params) {
8
- const me = await getAuthUser();
9
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
10
- if (!['super_admin', 'admin'].includes(me.role)) {
11
- return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
12
- }
13
-
14
- const { id } = await params;
15
- const plan = await prisma.plan.findUnique({ where: { id } });
16
- if (!plan) return NextResponse.json({ error: 'Not found' }, { status: 404 });
17
-
18
- return NextResponse.json({ ...plan, tokensPerWindow: Number(plan.tokensPerWindow) });
19
- }
20
-
21
- export async function PUT(req: Request, { params }: Params) {
22
- try {
23
- const me = await getAuthUser();
24
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
25
- if (me.role !== 'super_admin') {
26
- return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
27
- }
28
-
29
- const { id } = await params;
30
- const body = await req.json();
31
- const { name, tier, tokensPerWindow, requestsPerWindow, durationDays, displayMultiplier, minDurationDays, maxDurationDays, isActive } = body;
32
-
33
- const plan = await prisma.plan.update({
34
- where: { id },
35
- data: {
36
- name: name ?? undefined,
37
- tier: tier ?? undefined,
38
- tokensPerWindow: tokensPerWindow !== undefined ? tokensPerWindow : undefined,
39
- requestsPerWindow: requestsPerWindow ?? undefined,
40
- durationDays: durationDays ?? undefined,
41
- displayMultiplier: displayMultiplier ?? undefined,
42
- minDurationDays: minDurationDays ?? undefined,
43
- maxDurationDays: maxDurationDays ?? undefined,
44
- isActive: isActive ?? undefined,
45
- },
46
- });
47
-
48
- return NextResponse.json({ ...plan, tokensPerWindow: Number(plan.tokensPerWindow) });
49
- } catch (err) {
50
- console.error('[/api/admin/plans/[id] PUT]', err);
51
- return NextResponse.json({ error: 'Internal error', detail: String(err) }, { status: 500 });
52
- }
53
- }
54
-
55
- export async function DELETE(req: Request, { params }: Params) {
56
- const me = await getAuthUser();
57
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
58
- if (me.role !== 'super_admin') {
59
- return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
60
- }
61
-
62
- const { id } = await params;
63
- await prisma.plan.delete({ where: { id } });
64
- return NextResponse.json({ success: true });
65
- }
@@ -1,66 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
- import { getAuthUser } from '@/lib/auth';
4
-
5
- export async function GET(req: Request) {
6
- try {
7
- const me = await getAuthUser();
8
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
9
- if (!['super_admin', 'admin'].includes(me.role)) {
10
- return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
11
- }
12
-
13
- const plans = await prisma.plan.findMany({
14
- where: { isActive: true },
15
- orderBy: { tokensPerWindow: 'asc' },
16
- select: {
17
- id: true, name: true, tier: true,
18
- tokensPerWindow: true, requestsPerWindow: true,
19
- durationDays: true, displayMultiplier: true,
20
- minDurationDays: true, maxDurationDays: true,
21
- isActive: true, createdAt: true,
22
- },
23
- });
24
-
25
- return NextResponse.json({ plans: plans.map(p => ({ ...p, tokensPerWindow: Number(p.tokensPerWindow) })) });
26
- } catch (err) {
27
- console.error('[/api/admin/plans GET]', err);
28
- return NextResponse.json({ error: 'Internal error', detail: String(err) }, { status: 500 });
29
- }
30
- }
31
-
32
- export async function POST(req: Request) {
33
- try {
34
- const me = await getAuthUser();
35
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
36
- if (me.role !== 'super_admin') {
37
- return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
38
- }
39
-
40
- const body = await req.json();
41
- const { name, tier, tokensPerWindow, requestsPerWindow, durationDays, displayMultiplier, minDurationDays, maxDurationDays } = body;
42
-
43
- if (!name || !tier) {
44
- return NextResponse.json({ error: 'Name and tier are required' }, { status: 400 });
45
- }
46
-
47
- const plan = await prisma.plan.create({
48
- data: {
49
- name,
50
- tier: tier ?? 'free',
51
- tokensPerWindow: tokensPerWindow ?? 500_000,
52
- requestsPerWindow: requestsPerWindow ?? 100,
53
- durationDays: durationDays ?? 30,
54
- displayMultiplier: displayMultiplier ?? 1.0,
55
- minDurationDays: minDurationDays ?? 0,
56
- maxDurationDays: maxDurationDays ?? -1,
57
- createdByAdminId: me.id,
58
- },
59
- });
60
-
61
- return NextResponse.json({ ...plan, tokensPerWindow: Number(plan.tokensPerWindow) }, { status: 201 });
62
- } catch (err) {
63
- console.error('[/api/admin/plans POST]', err);
64
- return NextResponse.json({ error: 'Internal error', detail: String(err) }, { status: 500 });
65
- }
66
- }
@@ -1,81 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
- import { getAuthUser } from '@/lib/auth';
4
- import { randomBytes } from 'crypto';
5
-
6
- function slugify(text: string): string {
7
- return text
8
- .toLowerCase()
9
- .replace(/[^\w\s-]/g, '')
10
- .replace(/\s+/g, '-')
11
- .replace(/-+/g, '-')
12
- .trim();
13
- }
14
-
15
- type Params = { params: Promise<{ id: string }> };
16
-
17
- export async function GET(req: Request, { params }: Params) {
18
- const me = await getAuthUser();
19
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
20
- if (!['super_admin', 'admin'].includes(me.role)) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
21
- const { id } = await params;
22
-
23
- const post = await prisma.post.findUnique({
24
- where: { id },
25
- include: { author: { select: { name: true } } },
26
- });
27
- if (!post) return NextResponse.json({ error: 'Not found' }, { status: 404 });
28
-
29
- return NextResponse.json(post);
30
- }
31
-
32
- export async function PUT(req: Request, { params }: Params) {
33
- const me = await getAuthUser();
34
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
35
- if (!['super_admin', 'admin'].includes(me.role)) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
36
- const { id } = await params;
37
-
38
- const post = await prisma.post.findUnique({ where: { id } });
39
- if (!post) return NextResponse.json({ error: 'Not found' }, { status: 404 });
40
-
41
- const body = await req.json();
42
- const { title, content, excerpt, coverImage, tags, seoTitle, seoDesc, published } = body;
43
-
44
- let slug = post.slug;
45
- if (title && slugify(title) !== post.slug) {
46
- const newSlug = slugify(title);
47
- const existing = await prisma.post.findUnique({ where: { slug: newSlug } });
48
- slug = existing ? `${newSlug}-${randomBytes(4).toString('hex').slice(0, 6)}` : newSlug;
49
- }
50
-
51
- const wasPublished = post.published;
52
- const nowPublishing = published && !wasPublished;
53
-
54
- const updated = await prisma.post.update({
55
- where: { id },
56
- data: {
57
- title: title ?? post.title,
58
- slug,
59
- content: content ?? post.content,
60
- excerpt: excerpt !== undefined ? excerpt : post.excerpt,
61
- coverImage: coverImage !== undefined ? coverImage : post.coverImage,
62
- tags: tags !== undefined ? tags : post.tags,
63
- seoTitle: seoTitle !== undefined ? seoTitle : post.seoTitle,
64
- seoDesc: seoDesc !== undefined ? seoDesc : post.seoDesc,
65
- published: published !== undefined ? published : post.published,
66
- publishedAt: nowPublishing ? new Date() : post.publishedAt,
67
- },
68
- });
69
-
70
- return NextResponse.json(updated);
71
- }
72
-
73
- export async function DELETE(req: Request, { params }: Params) {
74
- const me = await getAuthUser();
75
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
76
- if (!['super_admin', 'admin'].includes(me.role)) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
77
- const { id } = await params;
78
-
79
- await prisma.post.delete({ where: { id } });
80
- return NextResponse.json({ success: true });
81
- }
@@ -1,83 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { prisma } from '@/lib/prisma';
3
- import { getAuthUser } from '@/lib/auth';
4
- import { randomBytes } from 'crypto';
5
-
6
- function slugify(text: string): string {
7
- return text
8
- .toLowerCase()
9
- .replace(/[^\w\s-]/g, '')
10
- .replace(/\s+/g, '-')
11
- .replace(/-+/g, '-')
12
- .trim();
13
- }
14
-
15
- export async function GET(req: Request) {
16
- const me = await getAuthUser();
17
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
18
- if (!['super_admin', 'admin'].includes(me.role)) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
19
-
20
- const { searchParams } = new URL(req.url);
21
- const page = parseInt(searchParams.get('page') ?? '1');
22
- const limit = parseInt(searchParams.get('limit') ?? '10');
23
- const status = searchParams.get('status'); // published | draft
24
- const skip = (page - 1) * limit;
25
-
26
- const where: any = {};
27
- if (status === 'published') where.published = true;
28
- if (status === 'draft') where.published = false;
29
-
30
- const [posts, total] = await Promise.all([
31
- prisma.post.findMany({
32
- where,
33
- orderBy: { createdAt: 'desc' },
34
- skip,
35
- take: limit,
36
- select: {
37
- id: true, title: true, slug: true, excerpt: true, coverImage: true,
38
- published: true, publishedAt: true, tags: true, views: true, seoTitle: true, seoDesc: true,
39
- createdAt: true, updatedAt: true,
40
- author: { select: { name: true } },
41
- },
42
- }),
43
- prisma.post.count({ where }),
44
- ]);
45
-
46
- return NextResponse.json({ posts, total, pages: Math.ceil(total / limit), page });
47
- }
48
-
49
- export async function POST(req: Request) {
50
- const me = await getAuthUser();
51
- if (!me) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
52
- if (!['super_admin', 'admin'].includes(me.role)) return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
53
-
54
- const body = await req.json();
55
- const { title, content, excerpt, coverImage, tags, seoTitle, seoDesc, published } = body;
56
-
57
- if (!title || !content) {
58
- return NextResponse.json({ error: 'Title and content are required' }, { status: 400 });
59
- }
60
-
61
- let slug = slugify(title);
62
- // Ensure unique slug
63
- const existing = await prisma.post.findUnique({ where: { slug } });
64
- if (existing) slug = `${slug}-${randomBytes(4).toString('hex').slice(0, 6)}`;
65
-
66
- const post = await prisma.post.create({
67
- data: {
68
- title,
69
- slug,
70
- content,
71
- excerpt: excerpt ?? null,
72
- coverImage: coverImage ?? null,
73
- tags: tags ?? '',
74
- seoTitle: seoTitle ?? null,
75
- seoDesc: seoDesc ?? null,
76
- published: published ?? false,
77
- publishedAt: published ? new Date() : null,
78
- authorId: me.id,
79
- },
80
- });
81
-
82
- return NextResponse.json(post, { status: 201 });
83
- }
@@ -1,145 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { Resend } from 'resend';
3
- import { prisma } from '@/lib/prisma';
4
- import bcrypt from 'bcryptjs';
5
- import { randomBytes } from 'crypto';
6
-
7
- function getResendClient() {
8
- if (!process.env.RESEND_API_KEY) return null;
9
- return new Resend(process.env.RESEND_API_KEY);
10
- }
11
-
12
- const DEFAULT_SUPERADMIN_PASSWORD = '@Monu123';
13
-
14
- const DEFAULT_PLANS = [
15
- {
16
- name: '5x Max Trial',
17
- tier: '5x',
18
- tokensPerWindow: 5000000,
19
- requestsPerWindow: 18000,
20
- durationDays: 7,
21
- displayMultiplier: 3.0,
22
- minDurationDays: 7,
23
- maxDurationDays: 90,
24
- },
25
- {
26
- name: '5x Max',
27
- tier: '5x',
28
- tokensPerWindow: 5000000,
29
- requestsPerWindow: 18000,
30
- durationDays: 30,
31
- displayMultiplier: 3.0,
32
- minDurationDays: 7,
33
- maxDurationDays: 90,
34
- },
35
- {
36
- name: '20x Max Trial',
37
- tier: '20x',
38
- tokensPerWindow: 20000000,
39
- requestsPerWindow: 18000,
40
- durationDays: 7,
41
- displayMultiplier: 3.0,
42
- minDurationDays: 7,
43
- maxDurationDays: 90,
44
- },
45
- {
46
- name: '20x Max',
47
- tier: '20x',
48
- tokensPerWindow: 20000000,
49
- requestsPerWindow: 18000,
50
- durationDays: 30,
51
- displayMultiplier: 3.0,
52
- minDurationDays: 7,
53
- maxDurationDays: 90,
54
- },
55
- ];
56
-
57
- export async function POST(req: Request) {
58
- try {
59
- const { secret } = await req.json();
60
-
61
- if (secret !== (process.env.SUPER_ADMIN_PASSWORD || DEFAULT_SUPERADMIN_PASSWORD)) {
62
- return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
63
- }
64
-
65
- let plansCreated = 0;
66
- await prisma.plan.deleteMany({});
67
- for (const plan of DEFAULT_PLANS) {
68
- await prisma.plan.create({ data: plan });
69
- plansCreated++;
70
- }
71
-
72
- // Create or update superadmin — always use DEFAULT_SUPERADMIN_PASSWORD
73
- const allPlans = await prisma.plan.findMany();
74
- const existing = await prisma.admin.findUnique({ where: { username: 'superadmin' } });
75
- const passwordHash = await bcrypt.hash(DEFAULT_SUPERADMIN_PASSWORD, 12);
76
-
77
- if (existing) {
78
- // Update password to the new credential
79
- const updateData: any = {
80
- password: passwordHash,
81
- allowedPlanIds: JSON.stringify(allPlans.map(p => p.id)),
82
- defaultPlanId: allPlans[0]?.id ?? null,
83
- supportEnabled: true,
84
- };
85
- await prisma.admin.update({ where: { username: 'superadmin' }, data: updateData });
86
- } else {
87
- const createData: any = {
88
- username: 'superadmin',
89
- password: passwordHash,
90
- name: 'Super Admin',
91
- role: 'super_admin',
92
- isActive: true,
93
- canCreateKey: true,
94
- canDeleteKey: true,
95
- canBlockKey: true,
96
- canManageTokens: true,
97
- canCreateReseller: true,
98
- canManageResellers: true,
99
- canManageUsers: true,
100
- allowedPlanIds: JSON.stringify(allPlans.map(p => p.id)),
101
- defaultPlanId: allPlans[0]?.id ?? null,
102
- supportEnabled: true,
103
- };
104
- await prisma.admin.create({ data: createData });
105
- }
106
-
107
- // Create a default API key if none exist
108
- const keyCount = await prisma.apiKey.count();
109
- if (keyCount === 0) {
110
- const rawKey = randomBytes(32).toString('hex');
111
- const key = `sk-cmx_${rawKey}`;
112
- const defaultPlan = await prisma.plan.findFirst({ where: { name: '20x Max' } });
113
- const expiresAt = defaultPlan && defaultPlan.durationDays > 0
114
- ? new Date(Date.now() + defaultPlan.durationDays * 24 * 60 * 60 * 1000)
115
- : null;
116
- await prisma.apiKey.create({
117
- data: {
118
- key,
119
- name: 'ClaudMax Default Key',
120
- prefix: `sk-cmx_${rawKey.slice(0, 6)}`,
121
- tier: '20x',
122
- isActive: true,
123
- windowStartAt: new Date(),
124
- windowTokensUsed: 0,
125
- windowRequestsUsed: 0,
126
- totalTokensUsed: 0,
127
- displayMultiplier: 3.0,
128
- planId: defaultPlan?.id,
129
- expiresAt,
130
- },
131
- });
132
- }
133
-
134
- return NextResponse.json({
135
- success: true,
136
- message: 'Database seeded successfully',
137
- superadmin: existing ? 'password updated' : 'created',
138
- plansCreated,
139
- apiKey: keyCount === 0 ? 'generated and created' : 'already exists',
140
- });
141
- } catch (err) {
142
- console.error('Seed error:', err);
143
- return NextResponse.json({ error: 'Seed failed', detail: String(err) }, { status: 500 });
144
- }
145
- }