tetrons 2.2.6 → 2.2.7

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 (81) hide show
  1. package/dist/app/api/export/route.d.ts +1 -0
  2. package/dist/app/api/export/{route.ts → route.js} +2 -2
  3. package/dist/app/api/register/route.d.ts +7 -0
  4. package/dist/app/api/register/route.js +32 -0
  5. package/dist/app/api/save/route.d.ts +6 -0
  6. package/dist/app/api/save/route.js +15 -0
  7. package/dist/app/api/validate/route.d.ts +10 -0
  8. package/dist/app/api/validate/route.js +58 -0
  9. package/dist/app/layout.d.ts +6 -0
  10. package/dist/app/layout.jsx +30 -0
  11. package/dist/app/page.d.ts +2 -0
  12. package/dist/app/page.jsx +51 -0
  13. package/dist/components/UI/Button.d.ts +0 -0
  14. package/dist/components/UI/Button.jsx +1 -0
  15. package/dist/components/UI/Dropdown.d.ts +0 -0
  16. package/dist/components/UI/Dropdown.jsx +1 -0
  17. package/dist/components/tetrons/EditorContent.d.ts +6 -0
  18. package/dist/components/tetrons/EditorContent.jsx +209 -0
  19. package/dist/components/tetrons/ResizableImage.d.ts +1 -0
  20. package/dist/components/tetrons/ResizableImage.js +40 -0
  21. package/dist/components/tetrons/ResizableImageComponent.d.ts +11 -0
  22. package/dist/components/tetrons/ResizableImageComponent.jsx +37 -0
  23. package/dist/components/tetrons/ResizableVideo.d.ts +12 -0
  24. package/dist/components/tetrons/ResizableVideo.js +61 -0
  25. package/dist/components/tetrons/ResizableVideoComponent.d.ts +4 -0
  26. package/dist/components/tetrons/ResizableVideoComponent.jsx +32 -0
  27. package/dist/components/tetrons/helpers.d.ts +0 -0
  28. package/dist/components/tetrons/helpers.js +1 -0
  29. package/dist/components/tetrons/toolbar/ActionGroup.d.ts +7 -0
  30. package/dist/components/tetrons/toolbar/ActionGroup.jsx +167 -0
  31. package/dist/components/tetrons/toolbar/ClipboardGroup.d.ts +5 -0
  32. package/dist/components/tetrons/toolbar/ClipboardGroup.jsx +36 -0
  33. package/dist/components/tetrons/toolbar/FileGroup.d.ts +7 -0
  34. package/dist/components/tetrons/toolbar/FileGroup.jsx +40 -0
  35. package/dist/components/tetrons/toolbar/FontStyleGroup.d.ts +7 -0
  36. package/dist/components/tetrons/toolbar/FontStyleGroup.jsx +104 -0
  37. package/dist/components/tetrons/toolbar/InsertGroup.d.ts +5 -0
  38. package/dist/components/tetrons/toolbar/InsertGroup.jsx +163 -0
  39. package/dist/components/tetrons/toolbar/ListAlignGroup.d.ts +5 -0
  40. package/dist/components/tetrons/toolbar/ListAlignGroup.jsx +16 -0
  41. package/dist/components/tetrons/toolbar/MiscGroup.d.ts +7 -0
  42. package/dist/components/tetrons/toolbar/MiscGroup.jsx +31 -0
  43. package/dist/components/tetrons/toolbar/TableContextMenu.d.ts +7 -0
  44. package/dist/components/tetrons/toolbar/TableContextMenu.jsx +52 -0
  45. package/dist/components/tetrons/toolbar/TetronsToolbar.d.ts +6 -0
  46. package/dist/components/tetrons/toolbar/TetronsToolbar.jsx +46 -0
  47. package/dist/components/tetrons/toolbar/ToolbarButton.d.ts +12 -0
  48. package/dist/components/tetrons/toolbar/ToolbarButton.jsx +8 -0
  49. package/dist/components/tetrons/toolbar/extensions/Comment.d.ts +17 -0
  50. package/dist/components/tetrons/toolbar/extensions/Comment.js +45 -0
  51. package/dist/components/tetrons/toolbar/extensions/Embed.d.ts +2 -0
  52. package/dist/components/tetrons/toolbar/extensions/Embed.js +90 -0
  53. package/dist/components/tetrons/toolbar/extensions/FontFamily.d.ts +9 -0
  54. package/dist/components/tetrons/toolbar/extensions/FontFamily.js +28 -0
  55. package/dist/components/tetrons/toolbar/extensions/FontSize.d.ts +9 -0
  56. package/dist/components/tetrons/toolbar/extensions/FontSize.js +28 -0
  57. package/dist/components/tetrons/toolbar/extensions/ResizableTable.d.ts +1 -0
  58. package/dist/components/tetrons/toolbar/extensions/ResizableTable.js +11 -0
  59. package/dist/components/tetrons/toolbar/marks/Subscript.d.ts +2 -0
  60. package/dist/components/tetrons/toolbar/marks/Subscript.js +35 -0
  61. package/dist/components/tetrons/toolbar/marks/Superscript.d.ts +2 -0
  62. package/dist/components/tetrons/toolbar/marks/Superscript.js +35 -0
  63. package/dist/index.d.ts +7 -8
  64. package/dist/index.js +21 -17207
  65. package/dist/lib/db.d.ts +1 -0
  66. package/dist/lib/db.js +15 -0
  67. package/dist/lib/export.d.ts +0 -0
  68. package/dist/lib/export.js +1 -0
  69. package/dist/lib/tiptap-extensions.d.ts +0 -0
  70. package/dist/lib/tiptap-extensions.js +1 -0
  71. package/dist/models/ApiKey.d.ts +2 -0
  72. package/dist/models/ApiKey.js +14 -0
  73. package/dist/styles/styles/tetrons.css +371 -0
  74. package/dist/utils/apiKeyUtils.d.ts +11 -0
  75. package/dist/utils/apiKeyUtils.js +33 -0
  76. package/dist/utils/loadEmojiPicker.d.ts +1 -0
  77. package/dist/utils/loadEmojiPicker.js +12 -0
  78. package/package.json +4 -3
  79. package/dist/app/api/register/route.ts +0 -36
  80. package/dist/app/api/save/route.ts +0 -18
  81. package/dist/app/api/validate/route.ts +0 -26
@@ -0,0 +1 @@
1
+ export function connectDB(): Promise<void>;
package/dist/lib/db.js ADDED
@@ -0,0 +1,15 @@
1
+ import mongoose from "mongoose";
2
+ const MONGO_URL = process.env.MONGO_URL;
3
+ export const connectDB = async () => {
4
+ if (mongoose.connection.readyState >= 1) {
5
+ return;
6
+ }
7
+ try {
8
+ await mongoose.connect(MONGO_URL);
9
+ console.log("Connected to MongoDB 👍");
10
+ }
11
+ catch (error) {
12
+ console.error("MongoDB connection failed ❌", error);
13
+ process.exit(1);
14
+ }
15
+ };
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,2 @@
1
+ import mongoose from "mongoose";
2
+ export declare const ApiKey: mongoose.Model<any, {}, {}, {}, any, any>;
@@ -0,0 +1,14 @@
1
+ import mongoose from "mongoose";
2
+ const ApiKeySchema = new mongoose.Schema({
3
+ email: { type: String, required: true },
4
+ organization: { type: String, required: true },
5
+ version: {
6
+ type: String,
7
+ enum: ["free", "pro", "premium", "platinum"],
8
+ required: true,
9
+ },
10
+ apiKey: { type: String, required: true, unique: true },
11
+ createdAt: { type: Date, default: Date.now },
12
+ expiresAt: { type: Date },
13
+ });
14
+ export const ApiKey = mongoose.models.ApiKey || mongoose.model("ApiKey", ApiKeySchema);
@@ -0,0 +1,371 @@
1
+ .editor-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ }
6
+
7
+ .editor-toolbar {
8
+ padding: 0.5rem;
9
+ border-bottom: 1px solid #d1d5db;
10
+ background-color: #f9fafb;
11
+ display: flex;
12
+ flex-wrap: wrap;
13
+ align-items: center;
14
+ gap: 0.75rem;
15
+ }
16
+
17
+ .editor-save-btn {
18
+ padding: 0.25rem 0.75rem;
19
+ background-color: #2563eb;
20
+ color: white;
21
+ border-radius: 0.375rem;
22
+ transition: background-color 0.2s;
23
+ }
24
+
25
+ .editor-save-btn:hover {
26
+ background-color: #1d4ed8;
27
+ }
28
+
29
+ .editor-save-btn:disabled {
30
+ opacity: 0.5;
31
+ cursor: not-allowed;
32
+ }
33
+
34
+ .editor-version-btn {
35
+ padding: 0.25rem 0.5rem;
36
+ border: 1px solid #d1d5db;
37
+ color: #374151;
38
+ border-radius: 0.375rem;
39
+ font-size: 0.875rem;
40
+ white-space: nowrap;
41
+ transition: all 0.2s;
42
+ background: none;
43
+ }
44
+
45
+ .editor-version-btn:hover {
46
+ border-color: #4b5563;
47
+ }
48
+
49
+ .editor-version-btn.active {
50
+ border-color: #2563eb;
51
+ color: #2563eb;
52
+ font-weight: 600;
53
+ }
54
+
55
+ .editor-content-wrapper {
56
+ flex-grow: 1;
57
+ padding: 1.5rem;
58
+ background-color: white;
59
+ border: 1px solid #d1d5db;
60
+ border-radius: 0.5rem;
61
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
62
+ overflow: auto;
63
+ position: relative;
64
+ min-height: 0;
65
+ }
66
+
67
+ .editor-loading {
68
+ color: #6b7280;
69
+ font-size: 0.875rem;
70
+ text-align: center;
71
+ padding: 1rem;
72
+ }
73
+
74
+ .editor-content-wrapper .ProseMirror {
75
+ outline: none;
76
+ min-height: 300px;
77
+ font-size: 1rem;
78
+ line-height: 1.75;
79
+ }
80
+
81
+ .editor-content-wrapper .ProseMirror[data-placeholder]:empty::before {
82
+ content: attr(data-placeholder);
83
+ color: #9ca3af;
84
+ float: left;
85
+ height: 0;
86
+ pointer-events: none;
87
+ }
88
+
89
+ .ProseMirror pre {
90
+ background: #f3f4f6;
91
+ padding: 1rem;
92
+ border-radius: 0.375rem;
93
+ font-family: monospace;
94
+ font-size: 0.875rem;
95
+ overflow-x: auto;
96
+ }
97
+
98
+ .editor-versions-wrapper {
99
+ display: flex;
100
+ align-items: center;
101
+ gap: 0.5rem;
102
+ overflow-x: auto;
103
+ max-width: 100%;
104
+ }
105
+
106
+ .editor-no-versions {
107
+ color: #6b7280;
108
+ font-size: 0.875rem;
109
+ }
110
+
111
+ .tetrons-toolbar {
112
+ display: flex;
113
+ flex-wrap: wrap;
114
+ align-items: center;
115
+ gap: 1rem;
116
+ padding: 0.75rem;
117
+ border-bottom: 1px solid #e5e7eb;
118
+ background-color: white;
119
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
120
+ position: relative;
121
+ z-index: 10;
122
+ }
123
+
124
+ .tetrons-toolbar .group {
125
+ display: flex;
126
+ align-items: center;
127
+ gap: 0.5rem;
128
+ border-right: 1px solid #e5e7eb;
129
+ padding-right: 0.75rem;
130
+ }
131
+
132
+ .tetrons-toolbar input[type="checkbox"] {
133
+ width: 1rem;
134
+ height: 1rem;
135
+ }
136
+
137
+ .tetrons-toolbar label {
138
+ font-size: 0.875rem;
139
+ user-select: none;
140
+ -webkit-user-select: none;
141
+ }
142
+
143
+ .misc-group {
144
+ display: flex;
145
+ gap: 0.25rem;
146
+ align-items: center;
147
+ border-right: 1px solid #e5e7eb;
148
+ padding-right: 0.75rem;
149
+ }
150
+
151
+ .list-align-group {
152
+ display: flex;
153
+ gap: 0.25rem;
154
+ border-right: 1px solid #e5e7eb;
155
+ padding-right: 0.75rem;
156
+ align-items: center;
157
+ }
158
+
159
+ .insert-group {
160
+ display: flex;
161
+ gap: 0.25rem;
162
+ border-right: 1px solid #e5e7eb;
163
+ padding-right: 0.75rem;
164
+ position: relative;
165
+ align-items: center;
166
+ }
167
+
168
+ .table-grid-popup {
169
+ position: absolute;
170
+ top: 2.5rem;
171
+ left: 0;
172
+ background-color: white;
173
+ border: 1px solid #d1d5db;
174
+ border-radius: 0.25rem;
175
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
176
+ padding: 0.5rem;
177
+ z-index: 20;
178
+ }
179
+
180
+ .table-grid {
181
+ display: grid;
182
+ grid-template-columns: repeat(10, 1fr);
183
+ gap: 1px;
184
+ }
185
+
186
+ .table-grid-cell {
187
+ width: 1.25rem;
188
+ height: 1.25rem;
189
+ border: 1px solid #d1d5db;
190
+ background-color: #f3f4f6;
191
+ cursor: pointer;
192
+ }
193
+
194
+ .table-grid-cell.selected {
195
+ background-color: #3b82f6;
196
+ }
197
+
198
+ .table-grid-label {
199
+ margin-top: 0.5rem;
200
+ font-size: 0.75rem;
201
+ text-align: center;
202
+ color: #6b7280;
203
+ }
204
+
205
+ .hidden-input {
206
+ display: none;
207
+ }
208
+
209
+ .emoji-picker {
210
+ position: absolute;
211
+ top: 2.5rem;
212
+ left: 0;
213
+ z-index: 50;
214
+ }
215
+
216
+ .font-style-group {
217
+ display: flex;
218
+ gap: 0.25rem;
219
+ border-right: 1px solid #e5e7eb;
220
+ padding-right: 0.75rem;
221
+ align-items: center;
222
+ }
223
+
224
+ .font-style-group select {
225
+ font-size: 0.875rem;
226
+ border: 1px solid #d1d5db;
227
+ border-radius: 0.25rem;
228
+ padding: 0.125rem 0.25rem;
229
+ margin-right: 0.5rem;
230
+ }
231
+
232
+ .color-label {
233
+ position: relative;
234
+ width: 2rem;
235
+ height: 2rem;
236
+ display: flex;
237
+ justify-content: center;
238
+ align-items: center;
239
+ cursor: pointer;
240
+ }
241
+
242
+ .color-indicator {
243
+ content: "";
244
+ position: absolute;
245
+ bottom: 2px;
246
+ left: 50%;
247
+ transform: translateX(-50%);
248
+ width: 12px;
249
+ height: 4px;
250
+ background-color: var(--indicator-color, #000000);
251
+ border-radius: 2px;
252
+ pointer-events: none;
253
+ }
254
+
255
+ .color-label input[type="color"] {
256
+ position: absolute;
257
+ inset: 0;
258
+ opacity: 0;
259
+ cursor: pointer;
260
+ }
261
+
262
+ .file-group {
263
+ display: flex;
264
+ align-items: center;
265
+ gap: 0.25rem;
266
+ border-right: 1px solid #e5e7eb;
267
+ padding-right: 0.75rem;
268
+ }
269
+
270
+ .file-group input[type="file"] {
271
+ display: none;
272
+ }
273
+
274
+ .clipboard-group {
275
+ display: flex;
276
+ gap: 0.25rem;
277
+ border-right: 1px solid #e5e7eb;
278
+ padding-right: 0.75rem;
279
+ }
280
+
281
+ .action-group {
282
+ position: relative;
283
+ display: flex;
284
+ align-items: center;
285
+ gap: 0.25rem;
286
+ }
287
+
288
+ .export-button {
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 0.25rem;
292
+ padding: 0.25rem 0.5rem;
293
+ border-radius: 0.25rem;
294
+ background: transparent;
295
+ cursor: pointer;
296
+ }
297
+
298
+ .export-button:hover {
299
+ background-color: #f3f4f6;
300
+ }
301
+
302
+ .export-button:focus {
303
+ outline: none;
304
+ }
305
+
306
+ .export-dropdown {
307
+ position: absolute;
308
+ z-index: 10;
309
+ margin-top: 0.5rem;
310
+ width: 10rem;
311
+ background-color: #fff;
312
+ border: 1px solid #e5e7eb;
313
+ border-radius: 0.25rem;
314
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
315
+ }
316
+
317
+ .export-dropdown button {
318
+ width: 100%;
319
+ text-align: left;
320
+ padding: 0.5rem 1rem;
321
+ background: none;
322
+ border: none;
323
+ font-size: 0.875rem;
324
+ cursor: pointer;
325
+ }
326
+
327
+ .export-dropdown button:hover {
328
+ background-color: #f3f4f6;
329
+ }
330
+
331
+ .toolbar-button {
332
+ padding: 0.5rem;
333
+ border: none;
334
+ background-color: transparent;
335
+ border-radius: 0.375rem;
336
+ cursor: pointer;
337
+ display: flex;
338
+ align-items: center;
339
+ justify-content: center;
340
+ transition: background-color 0.2s ease;
341
+ }
342
+
343
+ .toolbar-button:hover {
344
+ background-color: #e5e7eb;
345
+ }
346
+
347
+ .toolbar-button.active {
348
+ background-color: #d1d5db;
349
+ }
350
+
351
+ .toolbar-button:disabled {
352
+ opacity: 0.5;
353
+ cursor: not-allowed;
354
+ }
355
+
356
+ .tableWrapper {
357
+ overflow-x: auto;
358
+ margin: 1rem 0;
359
+ }
360
+
361
+ .tableWrapper table {
362
+ width: 100%;
363
+ border-collapse: collapse;
364
+ }
365
+
366
+ .tableWrapper th,
367
+ .tableWrapper td {
368
+ border: 1px solid #d1d5db;
369
+ padding: 0.5rem;
370
+ text-align: left;
371
+ }
@@ -0,0 +1,11 @@
1
+ export declare function getFreeApiKey(): string;
2
+ declare const VERSION_PREFIXES: {
3
+ readonly pro: "PRO";
4
+ readonly premium: "PREMIUM";
5
+ readonly platinum: "PLATINUM";
6
+ };
7
+ export type VersionType = keyof typeof VERSION_PREFIXES;
8
+ export declare function generateUserApiKey(email: string, organization: string, version: VersionType): string;
9
+ export declare function generateApiKey(length?: number): string;
10
+ export declare function generateVersionedKey(prefix?: string): string;
11
+ export {};
@@ -0,0 +1,33 @@
1
+ import crypto from "crypto";
2
+ const FREE_API_KEY = process.env.TETRONS_FREE_KEY || "TETRONS_FREE_KEY";
3
+ const SECRET_KEY = process.env.API_KEY_SECRET || "default-secret-key";
4
+ export function getFreeApiKey() {
5
+ return FREE_API_KEY;
6
+ }
7
+ const VERSION_PREFIXES = {
8
+ pro: "PRO",
9
+ premium: "PREMIUM",
10
+ platinum: "PLATINUM",
11
+ };
12
+ export function generateUserApiKey(email, organization, version) {
13
+ const input = `${email.trim().toLowerCase()}:${organization
14
+ .trim()
15
+ .toLowerCase()}`;
16
+ const hash = crypto
17
+ .createHmac("sha256", SECRET_KEY)
18
+ .update(input)
19
+ .digest("hex")
20
+ .toUpperCase();
21
+ const prefix = VERSION_PREFIXES[version];
22
+ return `${prefix}${hash.slice(0, 48 - prefix.length)}`;
23
+ }
24
+ export function generateApiKey(length = 48) {
25
+ return crypto
26
+ .randomBytes(length / 2)
27
+ .toString("hex")
28
+ .toUpperCase();
29
+ }
30
+ export function generateVersionedKey(prefix = "") {
31
+ const key = generateApiKey(48 - prefix.length);
32
+ return `${prefix}${key}`;
33
+ }
@@ -0,0 +1 @@
1
+ export declare const loadEmojiPicker: () => void;
@@ -0,0 +1,12 @@
1
+ export const loadEmojiPicker = () => {
2
+ if (typeof window === "undefined")
3
+ return;
4
+ if (!window.EmojiButton && !document.getElementById("emoji-picker-script")) {
5
+ const script = document.createElement("script");
6
+ script.id = "emoji-picker-script";
7
+ script.src =
8
+ "https://cdn.jsdelivr.net/npm/@joeattardi/emoji-button@latest/dist/index.min.js";
9
+ script.async = true;
10
+ document.body.appendChild(script);
11
+ }
12
+ };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "tetrons",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "description": "A Next.js project written in TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "dev": "next dev --turbo",
9
- "build": "tsup src/index.ts --format esm,cjs --dts --out-dir dist && copyfiles -u 2 src/styles/*.css dist/styles && copyfiles -u 3 src/app/api/**/*.ts dist/app/api",
9
+ "build": "tsup src/index.ts --format esm,cjs --dts --out-dir dist && copyfiles -u 1 src/styles/*.css dist/styles",
10
10
  "start": "next start",
11
11
  "lint": "next lint"
12
12
  },
@@ -65,7 +65,8 @@
65
65
  "exports": {
66
66
  ".": {
67
67
  "import": "./dist/index.js",
68
- "require": "./dist/index.js"
68
+ "require": "./dist/index.js",
69
+ "types": "./dist/index.d.ts"
69
70
  },
70
71
  "./style.css": "./dist/styles/tetrons.css"
71
72
  },
@@ -1,36 +0,0 @@
1
- import { NextRequest, NextResponse } from "next/server";
2
- import { connectDB } from "../../../lib/db";
3
- import { ApiKey } from "../../../models/ApiKey";
4
- import { generateApiKey } from "../../../utils/apiKeyUtils";
5
-
6
- export async function POST(req: NextRequest) {
7
- const { email, organization, version } = await req.json();
8
-
9
- if (!email || !organization || !version)
10
- return NextResponse.json({ error: "Missing fields" }, { status: 400 });
11
-
12
- await connectDB();
13
-
14
- if (version === "free") {
15
- const expiresAt = new Date(Date.now() + 14 * 24 * 60 * 60 * 1000);
16
- const apiKey = generateApiKey(48);
17
-
18
- await ApiKey.deleteMany({ email, version });
19
-
20
- await ApiKey.create({
21
- email,
22
- organization,
23
- version,
24
- apiKey,
25
- expiresAt,
26
- });
27
-
28
- return NextResponse.json({ apiKey, expiresAt });
29
- }
30
-
31
-
32
- const apiKey = generateApiKey(48);
33
- await ApiKey.create({ email, organization, version, apiKey });
34
-
35
- return NextResponse.json({ apiKey });
36
- }
@@ -1,18 +0,0 @@
1
- import { NextRequest, NextResponse } from "next/server";
2
- import path from "path";
3
- import fs from "fs/promises";
4
-
5
- export async function POST(request: NextRequest) {
6
- try {
7
- const json = await request.json();
8
-
9
- const publicDir = path.join(process.cwd(), "public");
10
- const filePath = path.join(publicDir, "editor-content.json");
11
-
12
- await fs.writeFile(filePath, JSON.stringify(json, null, 2), "utf-8");
13
-
14
- return NextResponse.json({ message: "File saved successfully" });
15
- } catch {
16
- return NextResponse.json({ error: "Failed to save file" }, { status: 500 });
17
- }
18
- }
@@ -1,26 +0,0 @@
1
- import { NextRequest, NextResponse } from "next/server";
2
- import { connectDB } from "../../../lib/db";
3
- import { ApiKey } from "../../../models/ApiKey";
4
-
5
- export async function POST(req: NextRequest) {
6
- const { apiKey } = await req.json();
7
-
8
- if (!apiKey)
9
- return NextResponse.json({ error: "API key required" }, { status: 400 });
10
-
11
- await connectDB();
12
-
13
- const keyEntry = await ApiKey.findOne({ apiKey });
14
- if (!keyEntry)
15
- return NextResponse.json({ error: "Invalid key" }, { status: 401 });
16
-
17
- if (
18
- keyEntry.version === "free" &&
19
- keyEntry.expiresAt &&
20
- new Date() > new Date(keyEntry.expiresAt)
21
- ) {
22
- return NextResponse.json({ error: "Free trial expired" }, { status: 403 });
23
- }
24
-
25
- return NextResponse.json({ valid: true, version: keyEntry.version });
26
- }