next-blogpanel 1.0.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/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/admin/index.cjs +3062 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +51 -0
- package/dist/admin/index.d.ts +51 -0
- package/dist/admin/index.js +3029 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/api/categories.cjs +82 -0
- package/dist/api/categories.cjs.map +1 -0
- package/dist/api/categories.d.cts +27 -0
- package/dist/api/categories.d.ts +27 -0
- package/dist/api/categories.js +77 -0
- package/dist/api/categories.js.map +1 -0
- package/dist/api/media-file.cjs +46 -0
- package/dist/api/media-file.cjs.map +1 -0
- package/dist/api/media-file.d.cts +7 -0
- package/dist/api/media-file.d.ts +7 -0
- package/dist/api/media-file.js +44 -0
- package/dist/api/media-file.js.map +1 -0
- package/dist/api/media.cjs +180 -0
- package/dist/api/media.cjs.map +1 -0
- package/dist/api/media.d.cts +22 -0
- package/dist/api/media.d.ts +22 -0
- package/dist/api/media.js +176 -0
- package/dist/api/media.js.map +1 -0
- package/dist/api/posts.cjs +103 -0
- package/dist/api/posts.cjs.map +1 -0
- package/dist/api/posts.d.cts +27 -0
- package/dist/api/posts.d.ts +27 -0
- package/dist/api/posts.js +98 -0
- package/dist/api/posts.js.map +1 -0
- package/dist/api/rss.cjs +25 -0
- package/dist/api/rss.cjs.map +1 -0
- package/dist/api/rss.d.cts +5 -0
- package/dist/api/rss.d.ts +5 -0
- package/dist/api/rss.js +23 -0
- package/dist/api/rss.js.map +1 -0
- package/dist/api/settings.cjs +40 -0
- package/dist/api/settings.cjs.map +1 -0
- package/dist/api/settings.d.cts +17 -0
- package/dist/api/settings.d.ts +17 -0
- package/dist/api/settings.js +37 -0
- package/dist/api/settings.js.map +1 -0
- package/dist/api/sitemap.cjs +25 -0
- package/dist/api/sitemap.cjs.map +1 -0
- package/dist/api/sitemap.d.cts +5 -0
- package/dist/api/sitemap.d.ts +5 -0
- package/dist/api/sitemap.js +23 -0
- package/dist/api/sitemap.js.map +1 -0
- package/dist/api/tokens.cjs +56 -0
- package/dist/api/tokens.cjs.map +1 -0
- package/dist/api/tokens.d.cts +22 -0
- package/dist/api/tokens.d.ts +22 -0
- package/dist/api/tokens.js +52 -0
- package/dist/api/tokens.js.map +1 -0
- package/dist/chunk-2M46MMWN.cjs +76 -0
- package/dist/chunk-2M46MMWN.cjs.map +1 -0
- package/dist/chunk-4GK22Q3R.cjs +229 -0
- package/dist/chunk-4GK22Q3R.cjs.map +1 -0
- package/dist/chunk-6PZ772NQ.js +595 -0
- package/dist/chunk-6PZ772NQ.js.map +1 -0
- package/dist/chunk-BZXPRGLN.cjs +351 -0
- package/dist/chunk-BZXPRGLN.cjs.map +1 -0
- package/dist/chunk-DAX5C6SC.cjs +86 -0
- package/dist/chunk-DAX5C6SC.cjs.map +1 -0
- package/dist/chunk-DWNMHQ4I.cjs +57 -0
- package/dist/chunk-DWNMHQ4I.cjs.map +1 -0
- package/dist/chunk-FPGVR2BX.cjs +121 -0
- package/dist/chunk-FPGVR2BX.cjs.map +1 -0
- package/dist/chunk-JBP3G4QJ.js +50 -0
- package/dist/chunk-JBP3G4QJ.js.map +1 -0
- package/dist/chunk-JDP4DIHI.js +215 -0
- package/dist/chunk-JDP4DIHI.js.map +1 -0
- package/dist/chunk-LIZOVA4W.js +118 -0
- package/dist/chunk-LIZOVA4W.js.map +1 -0
- package/dist/chunk-QLBTOCT3.js +118 -0
- package/dist/chunk-QLBTOCT3.js.map +1 -0
- package/dist/chunk-UI2KDZ5H.js +344 -0
- package/dist/chunk-UI2KDZ5H.js.map +1 -0
- package/dist/chunk-V7QPRX4A.cjs +125 -0
- package/dist/chunk-V7QPRX4A.cjs.map +1 -0
- package/dist/chunk-VEJFN65Y.js +82 -0
- package/dist/chunk-VEJFN65Y.js.map +1 -0
- package/dist/chunk-WYZFAI6O.js +74 -0
- package/dist/chunk-WYZFAI6O.js.map +1 -0
- package/dist/chunk-XLYTATRQ.cjs +637 -0
- package/dist/chunk-XLYTATRQ.cjs.map +1 -0
- package/dist/cli/index.cjs +2036 -0
- package/dist/components/index.cjs +647 -0
- package/dist/components/index.cjs.map +1 -0
- package/dist/components/index.d.cts +187 -0
- package/dist/components/index.d.ts +187 -0
- package/dist/components/index.js +633 -0
- package/dist/components/index.js.map +1 -0
- package/dist/db-M3KXLBUP.js +3 -0
- package/dist/db-M3KXLBUP.js.map +1 -0
- package/dist/db-TDCZNY3Q.cjs +112 -0
- package/dist/db-TDCZNY3Q.cjs.map +1 -0
- package/dist/editor/index.cjs +1126 -0
- package/dist/editor/index.cjs.map +1 -0
- package/dist/editor/index.d.cts +137 -0
- package/dist/editor/index.d.ts +137 -0
- package/dist/editor/index.js +1093 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/index-BcNka7gK.d.cts +475 -0
- package/dist/index-DPRgaNEE.d.ts +475 -0
- package/dist/index.cjs +433 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +209 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/index.cjs +193 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.d.cts +4 -0
- package/dist/lib/index.d.ts +4 -0
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/styles/admin.css +667 -0
- package/dist/styles/blog.css +851 -0
- package/dist/styles/editor.css +452 -0
- package/dist/styles/globals.css +270 -0
- package/dist/styles/prose.css +299 -0
- package/dist/types-CgrBuig-.d.cts +778 -0
- package/dist/types-CgrBuig-.d.ts +778 -0
- package/package.json +140 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as next_server from 'next/server';
|
|
2
|
+
import { A as ApiErrorResponse } from '../types-CgrBuig-.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'mongodb';
|
|
5
|
+
|
|
6
|
+
declare function GET(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
7
|
+
success: boolean;
|
|
8
|
+
data: unknown;
|
|
9
|
+
meta: Record<string, unknown> | undefined;
|
|
10
|
+
}>>;
|
|
11
|
+
declare function POST(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
12
|
+
success: boolean;
|
|
13
|
+
data: unknown;
|
|
14
|
+
meta: Record<string, unknown> | undefined;
|
|
15
|
+
}>>;
|
|
16
|
+
declare function DELETE(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
data: unknown;
|
|
19
|
+
meta: Record<string, unknown> | undefined;
|
|
20
|
+
}>>;
|
|
21
|
+
|
|
22
|
+
export { DELETE, GET, POST };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { processImage, ImageTooLargeError } from '../chunk-LIZOVA4W.js';
|
|
2
|
+
import { getStorageProvider, rollbackUploads, StorageProviderNotRegisteredError, getStorageProviderByName } from '../chunk-JDP4DIHI.js';
|
|
3
|
+
import { requireAuth, getSearchParams, parseIntParam, jsonSuccess, jsonError } from '../chunk-JBP3G4QJ.js';
|
|
4
|
+
import { listMedia, getConfig, createMedia, ConfigNotInstalledError, getMediaById, deleteMedia } from '../chunk-6PZ772NQ.js';
|
|
5
|
+
|
|
6
|
+
// src/api/media.ts
|
|
7
|
+
async function GET(request) {
|
|
8
|
+
try {
|
|
9
|
+
const authError = await requireAuth(request);
|
|
10
|
+
if (authError) return authError;
|
|
11
|
+
const params = getSearchParams(request);
|
|
12
|
+
const result = await listMedia({
|
|
13
|
+
page: parseIntParam(params.page, 1),
|
|
14
|
+
limit: parseIntParam(params.limit, 20),
|
|
15
|
+
mimeType: params.mimeType
|
|
16
|
+
});
|
|
17
|
+
const page = parseIntParam(params.page, 1);
|
|
18
|
+
const limit = parseIntParam(params.limit, 20);
|
|
19
|
+
return jsonSuccess(result.media, {
|
|
20
|
+
page,
|
|
21
|
+
limit,
|
|
22
|
+
total: result.total,
|
|
23
|
+
totalPages: Math.ceil(result.total / limit)
|
|
24
|
+
});
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("[blogpanel] GET /media error:", error);
|
|
27
|
+
return jsonError("INTERNAL_ERROR", "Failed to fetch media", 500);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function POST(request) {
|
|
31
|
+
try {
|
|
32
|
+
const authError = await requireAuth(request);
|
|
33
|
+
if (authError) return authError;
|
|
34
|
+
const formData = await request.formData();
|
|
35
|
+
const file = formData.get("file");
|
|
36
|
+
if (!file) {
|
|
37
|
+
return jsonError("MISSING_FILE", "File is required");
|
|
38
|
+
}
|
|
39
|
+
const config = getConfig();
|
|
40
|
+
const { maxUploadSize, maxImageSize } = config.editor;
|
|
41
|
+
if (file.size > maxUploadSize) {
|
|
42
|
+
return jsonError(
|
|
43
|
+
"FILE_TOO_LARGE",
|
|
44
|
+
`File must be under ${Math.round(maxUploadSize / 1024 / 1024)}MB`,
|
|
45
|
+
413
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const allowedTypes = [
|
|
49
|
+
"image/jpeg",
|
|
50
|
+
"image/png",
|
|
51
|
+
"image/gif",
|
|
52
|
+
"image/webp",
|
|
53
|
+
"image/svg+xml",
|
|
54
|
+
"image/avif"
|
|
55
|
+
];
|
|
56
|
+
if (!allowedTypes.includes(file.type)) {
|
|
57
|
+
return jsonError("INVALID_TYPE", `Allowed types: ${allowedTypes.join(", ")}`);
|
|
58
|
+
}
|
|
59
|
+
if (file.type === "image/svg+xml" && file.size > maxImageSize) {
|
|
60
|
+
return jsonError(
|
|
61
|
+
"FILE_TOO_LARGE",
|
|
62
|
+
`SVG must be under ${Math.round(maxImageSize / 1024 / 1024)}MB`,
|
|
63
|
+
413
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
67
|
+
const storage = getStorageProvider();
|
|
68
|
+
const isImage = file.type.startsWith("image/") && file.type !== "image/svg+xml";
|
|
69
|
+
let uploadResult;
|
|
70
|
+
let width;
|
|
71
|
+
let height;
|
|
72
|
+
let uploadedKeys;
|
|
73
|
+
if (isImage) {
|
|
74
|
+
try {
|
|
75
|
+
const processed = await processImage(buffer, file.name, storage);
|
|
76
|
+
uploadResult = processed.original;
|
|
77
|
+
width = processed.width;
|
|
78
|
+
height = processed.height;
|
|
79
|
+
uploadedKeys = processed.uploadedKeys;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (error instanceof ImageTooLargeError) {
|
|
82
|
+
return jsonError(
|
|
83
|
+
"IMAGE_TOO_LARGE",
|
|
84
|
+
`Could not compress this image below ${Math.round(error.limitBytes / 1024 / 1024)}MB (best achieved ${Math.round(error.achievedBytes / 1024)}KB). Try a smaller image.`,
|
|
85
|
+
422
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
uploadResult = await storage.upload(buffer, file.name, file.type);
|
|
92
|
+
uploadedKeys = [uploadResult.key];
|
|
93
|
+
}
|
|
94
|
+
let media;
|
|
95
|
+
try {
|
|
96
|
+
media = await createMedia({
|
|
97
|
+
filename: uploadResult.key.split("/").pop() || file.name,
|
|
98
|
+
originalName: file.name,
|
|
99
|
+
mimeType: uploadResult.contentType,
|
|
100
|
+
size: uploadResult.size,
|
|
101
|
+
width,
|
|
102
|
+
height,
|
|
103
|
+
storageKey: uploadResult.key,
|
|
104
|
+
provider: storage.name,
|
|
105
|
+
url: uploadResult.url,
|
|
106
|
+
alt: formData.get("alt") || "",
|
|
107
|
+
caption: formData.get("caption") || "",
|
|
108
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
await rollbackUploads(storage, uploadedKeys, "createMedia failed after upload");
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
return jsonSuccess(media, void 0, 201);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error("[blogpanel] POST /media error:", error);
|
|
117
|
+
if (error instanceof ConfigNotInstalledError) {
|
|
118
|
+
return jsonError("CONFIG_NOT_INSTALLED", error.message, 500);
|
|
119
|
+
}
|
|
120
|
+
if (error instanceof StorageProviderNotRegisteredError) {
|
|
121
|
+
return jsonError("STORAGE_PROVIDER_NOT_REGISTERED", error.message, 500);
|
|
122
|
+
}
|
|
123
|
+
return jsonError("INTERNAL_ERROR", "Failed to upload media", 500);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function DELETE(request) {
|
|
127
|
+
try {
|
|
128
|
+
const authError = await requireAuth(request);
|
|
129
|
+
if (authError) return authError;
|
|
130
|
+
const params = getSearchParams(request);
|
|
131
|
+
const id = params.id;
|
|
132
|
+
if (!id) return jsonError("MISSING_ID", "Media ID is required");
|
|
133
|
+
const existing = await getMediaById(id);
|
|
134
|
+
if (!existing) return jsonError("NOT_FOUND", "Media not found", 404);
|
|
135
|
+
let storage;
|
|
136
|
+
try {
|
|
137
|
+
storage = existing.provider ? getStorageProviderByName(existing.provider) : getStorageProvider();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof StorageProviderNotRegisteredError) {
|
|
140
|
+
const source = existing.provider ? `media ${id} records provider='${error.providerName}'` : `media ${id} predates the provider field, so the backend came from BLOGPANEL_STORAGE, which names '${error.providerName}'`;
|
|
141
|
+
console.error(
|
|
142
|
+
`[blogpanel] DELETE /media refused: ${source}, which is not registered in this runtime (key=${existing.storageKey}). The database record was left intact.`,
|
|
143
|
+
error
|
|
144
|
+
);
|
|
145
|
+
return jsonError("STORAGE_PROVIDER_NOT_REGISTERED", error.message, 500);
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
const media = await deleteMedia(id);
|
|
150
|
+
if (!media) return jsonError("NOT_FOUND", "Media not found", 404);
|
|
151
|
+
let storageDeleted = true;
|
|
152
|
+
await storage.delete(media.storageKey).catch((err) => {
|
|
153
|
+
storageDeleted = false;
|
|
154
|
+
console.error(
|
|
155
|
+
`[blogpanel] Failed to delete stored object (provider=${storage.name}, key=${media.storageKey}):`,
|
|
156
|
+
err
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
return jsonSuccess({
|
|
160
|
+
deleted: true,
|
|
161
|
+
storageDeleted,
|
|
162
|
+
storageKey: media.storageKey,
|
|
163
|
+
provider: storage.name,
|
|
164
|
+
...storageDeleted ? {} : {
|
|
165
|
+
warning: `The database record was removed, but the stored file could not be deleted from ${storage.name} and may require manual cleanup.`
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error("[blogpanel] DELETE /media error:", error);
|
|
170
|
+
return jsonError("INTERNAL_ERROR", "Failed to delete media", 500);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export { DELETE, GET, POST };
|
|
175
|
+
//# sourceMappingURL=media.js.map
|
|
176
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/media.ts"],"names":[],"mappings":"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","file":"media.js","sourcesContent":["import { createMedia, deleteMedia, getMediaById, listMedia } from '../lib/db';\nimport {\n getStorageProvider,\n getStorageProviderByName,\n rollbackUploads,\n StorageProviderNotRegisteredError,\n} from '../lib/storage';\nimport type { StorageProvider } from '../lib/storage';\nimport { processImage, ImageTooLargeError } from '../lib/image';\nimport { getConfig, ConfigNotInstalledError } from '../lib/config';\nimport {\n jsonSuccess,\n jsonError,\n requireAuth,\n getSearchParams,\n parseIntParam,\n} from './middleware';\n\nexport async function GET(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const params = getSearchParams(request);\n const result = await listMedia({\n page: parseIntParam(params.page, 1),\n limit: parseIntParam(params.limit, 20),\n mimeType: params.mimeType,\n });\n\n const page = parseIntParam(params.page, 1);\n const limit = parseIntParam(params.limit, 20);\n\n return jsonSuccess(result.media, {\n page,\n limit,\n total: result.total,\n totalPages: Math.ceil(result.total / limit),\n });\n } catch (error) {\n console.error('[blogpanel] GET /media error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to fetch media', 500);\n }\n}\n\nexport async function POST(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const formData = await request.formData();\n const file = formData.get('file') as File | null;\n\n if (!file) {\n return jsonError('MISSING_FILE', 'File is required');\n }\n\n const config = getConfig();\n const { maxUploadSize, maxImageSize } = config.editor;\n\n if (file.size > maxUploadSize) {\n return jsonError(\n 'FILE_TOO_LARGE',\n `File must be under ${Math.round(maxUploadSize / 1024 / 1024)}MB`,\n 413\n );\n }\n\n const allowedTypes = [\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/svg+xml',\n 'image/avif',\n ];\n if (!allowedTypes.includes(file.type)) {\n return jsonError('INVALID_TYPE', `Allowed types: ${allowedTypes.join(', ')}`);\n }\n\n // SVG bypasses the compression ladder — sharp cannot usefully shrink it.\n if (file.type === 'image/svg+xml' && file.size > maxImageSize) {\n return jsonError(\n 'FILE_TOO_LARGE',\n `SVG must be under ${Math.round(maxImageSize / 1024 / 1024)}MB`,\n 413\n );\n }\n\n const buffer = Buffer.from(await file.arrayBuffer());\n const storage = getStorageProvider();\n\n const isImage = file.type.startsWith('image/') && file.type !== 'image/svg+xml';\n let uploadResult;\n let width: number | undefined;\n let height: number | undefined;\n let uploadedKeys: string[];\n\n if (isImage) {\n try {\n const processed = await processImage(buffer, file.name, storage);\n uploadResult = processed.original;\n width = processed.width;\n height = processed.height;\n uploadedKeys = processed.uploadedKeys;\n } catch (error) {\n if (error instanceof ImageTooLargeError) {\n return jsonError(\n 'IMAGE_TOO_LARGE',\n `Could not compress this image below ${Math.round(error.limitBytes / 1024 / 1024)}MB ` +\n `(best achieved ${Math.round(error.achievedBytes / 1024)}KB). Try a smaller image.`,\n 422\n );\n }\n throw error;\n }\n } else {\n uploadResult = await storage.upload(buffer, file.name, file.type);\n uploadedKeys = [uploadResult.key];\n }\n\n let media;\n try {\n media = await createMedia({\n filename: uploadResult.key.split('/').pop() || file.name,\n originalName: file.name,\n mimeType: uploadResult.contentType,\n size: uploadResult.size,\n width,\n height,\n storageKey: uploadResult.key,\n provider: storage.name,\n url: uploadResult.url,\n alt: (formData.get('alt') as string) || '',\n caption: (formData.get('caption') as string) || '',\n createdAt: new Date(),\n });\n } catch (error) {\n // Every object above already landed in storage; without this, a failed\n // DB write leaves them orphaned exactly like an unhandled derivative\n // failure would — nothing will ever reference them again.\n await rollbackUploads(storage, uploadedKeys, 'createMedia failed after upload');\n throw error;\n }\n\n return jsonSuccess(media, undefined, 201);\n } catch (error) {\n console.error('[blogpanel] POST /media error:', error);\n\n // A missing runtime config is an installation mistake with an exact fix,\n // and this endpoint is authenticated, so pass the message through instead\n // of flattening it to \"Failed to upload media\" — otherwise the developer\n // has to go digging in the server log to find out what to do. Matched by\n // class, not by a message-prefix check: a prefix check silently stops\n // working the moment the message wording changes for any reason.\n if (error instanceof ConfigNotInstalledError) {\n return jsonError('CONFIG_NOT_INSTALLED', error.message, 500);\n }\n\n // Same treatment, same reasoning: BLOGPANEL_STORAGE naming a provider this\n // runtime never registered is an installation mistake with an exact fix,\n // and nothing was written, so say what to do rather than \"Failed to upload\n // media\".\n if (error instanceof StorageProviderNotRegisteredError) {\n return jsonError('STORAGE_PROVIDER_NOT_REGISTERED', error.message, 500);\n }\n\n return jsonError('INTERNAL_ERROR', 'Failed to upload media', 500);\n }\n}\n\nexport async function DELETE(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const params = getSearchParams(request);\n const id = params.id;\n if (!id) return jsonError('MISSING_ID', 'Media ID is required');\n\n // Resolve the backend BEFORE the row is removed.\n //\n // Delete from whichever backend actually holds it — not whichever backend\n // is currently configured. Those can diverge after a storage migration\n // (e.g. GridFS -> R2), and generateKey() produces identically-shaped keys\n // on every provider, so asking the wrong one to delete a key \"succeeds\"\n // by silently finding nothing. `provider` is absent only on documents\n // that predate the field and somehow escaped migration.\n //\n // Resolution can now fail — `provider` is a free-form string naming a\n // registered custom backend, and this process may not have registered it\n // (a provider removed from the setup module, a rename, a typo). Doing the\n // lookup first means that failure is fully recoverable: the row is still\n // there, the request 500s with an actionable message, and retrying after\n // registering the provider deletes the object properly. Resolving after\n // the delete would destroy the only record of which backend held the\n // object and what its key was.\n const existing = await getMediaById(id);\n if (!existing) return jsonError('NOT_FOUND', 'Media not found', 404);\n\n let storage: StorageProvider;\n try {\n storage = existing.provider\n ? getStorageProviderByName(existing.provider)\n : getStorageProvider();\n } catch (error) {\n if (error instanceof StorageProviderNotRegisteredError) {\n // Attribute the unresolvable name to where it actually came from. On\n // the legacy path the document has no `provider`, so the throw\n // originates in getStorageProvider() reading BLOGPANEL_STORAGE — not\n // from the document — and saying \"the document records it\" would send\n // an operator looking for a field that is not there.\n const source = existing.provider\n ? `media ${id} records provider='${error.providerName}'`\n : `media ${id} predates the provider field, so the backend came from ` +\n `BLOGPANEL_STORAGE, which names '${error.providerName}'`;\n\n console.error(\n `[blogpanel] DELETE /media refused: ${source}, which is not registered in ` +\n `this runtime (key=${existing.storageKey}). The database record was left intact.`,\n error\n );\n return jsonError('STORAGE_PROVIDER_NOT_REGISTERED', error.message, 500);\n }\n throw error;\n }\n\n const media = await deleteMedia(id);\n if (!media) return jsonError('NOT_FOUND', 'Media not found', 404);\n\n let storageDeleted = true;\n await storage.delete(media.storageKey).catch((err) => {\n storageDeleted = false;\n // The DB row is already gone by this point, so a failure here leaves\n // the object permanently unreachable through the UI — log enough detail\n // for an operator to clean it up by hand rather than letting this\n // disappear into a generic message. Uses storage.name (the provider\n // actually resolved and attempted), not media.provider directly — when\n // `provider` is absent and the fallback itself fails, media.provider\n // would print as \"unknown\" even though a concrete provider was tried.\n console.error(\n `[blogpanel] Failed to delete stored object (provider=${storage.name}, ` +\n `key=${media.storageKey}):`,\n err\n );\n });\n\n // The DB half of this operation genuinely succeeded — the row is gone,\n // the item will never appear in the media library again — so this stays\n // HTTP 200 rather than a false 500 even when storageDeleted is false.\n // But collapsing that into a flat `{ deleted: true }` would tell the\n // caller the object was fully removed when it is, in fact, still sitting\n // in storage: unreferenced by any row, invisible to every UI path that\n // could otherwise clean it up, and still counted against quota. The\n // response has to be able to say that out loud rather than let a caller\n // assume full success from a 200.\n return jsonSuccess({\n deleted: true,\n storageDeleted,\n storageKey: media.storageKey,\n provider: storage.name,\n ...(storageDeleted\n ? {}\n : {\n warning:\n `The database record was removed, but the stored file could not be deleted ` +\n `from ${storage.name} and may require manual cleanup.`,\n }),\n });\n } catch (error) {\n console.error('[blogpanel] DELETE /media error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to delete media', 500);\n }\n}\n"]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkV7QPRX4A_cjs = require('../chunk-V7QPRX4A.cjs');
|
|
4
|
+
var chunkDWNMHQ4I_cjs = require('../chunk-DWNMHQ4I.cjs');
|
|
5
|
+
var chunkXLYTATRQ_cjs = require('../chunk-XLYTATRQ.cjs');
|
|
6
|
+
|
|
7
|
+
// src/api/posts.ts
|
|
8
|
+
async function GET(request) {
|
|
9
|
+
try {
|
|
10
|
+
const params = chunkDWNMHQ4I_cjs.getSearchParams(request);
|
|
11
|
+
if (params.slug) {
|
|
12
|
+
const post = await chunkXLYTATRQ_cjs.getPostBySlug(params.slug);
|
|
13
|
+
if (!post) return chunkDWNMHQ4I_cjs.jsonError("NOT_FOUND", "Post not found", 404);
|
|
14
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess(post);
|
|
15
|
+
}
|
|
16
|
+
if (params.id) {
|
|
17
|
+
const post = await chunkXLYTATRQ_cjs.getPostById(params.id);
|
|
18
|
+
if (!post) return chunkDWNMHQ4I_cjs.jsonError("NOT_FOUND", "Post not found", 404);
|
|
19
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess(post);
|
|
20
|
+
}
|
|
21
|
+
const result = await chunkXLYTATRQ_cjs.listPosts({
|
|
22
|
+
page: chunkDWNMHQ4I_cjs.parseIntParam(params.page, 1),
|
|
23
|
+
limit: chunkDWNMHQ4I_cjs.parseIntParam(params.limit, 10),
|
|
24
|
+
category: params.category,
|
|
25
|
+
tag: params.tag,
|
|
26
|
+
status: params.status,
|
|
27
|
+
search: params.q || params.search,
|
|
28
|
+
sortBy: params.sortBy || "publishedAt",
|
|
29
|
+
sortOrder: params.sortOrder || "desc"
|
|
30
|
+
});
|
|
31
|
+
const page = chunkDWNMHQ4I_cjs.parseIntParam(params.page, 1);
|
|
32
|
+
const limit = chunkDWNMHQ4I_cjs.parseIntParam(params.limit, 10);
|
|
33
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess(result.posts, {
|
|
34
|
+
page,
|
|
35
|
+
limit,
|
|
36
|
+
total: result.total,
|
|
37
|
+
totalPages: Math.ceil(result.total / limit)
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("[blogpanel] GET /posts error:", error);
|
|
41
|
+
return chunkDWNMHQ4I_cjs.jsonError("INTERNAL_ERROR", "Failed to fetch posts", 500);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function POST(request) {
|
|
45
|
+
try {
|
|
46
|
+
const authError = await chunkDWNMHQ4I_cjs.requireAuth(request);
|
|
47
|
+
if (authError) return authError;
|
|
48
|
+
const body = await request.json();
|
|
49
|
+
const parsed = chunkV7QPRX4A_cjs.CreatePostSchema.safeParse(body);
|
|
50
|
+
if (!parsed.success) {
|
|
51
|
+
const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
|
|
52
|
+
return chunkDWNMHQ4I_cjs.jsonError("VALIDATION_ERROR", errors.join("; "));
|
|
53
|
+
}
|
|
54
|
+
const post = await chunkXLYTATRQ_cjs.createPost(parsed.data);
|
|
55
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess(post, void 0, 201);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error("[blogpanel] POST /posts error:", error);
|
|
58
|
+
return chunkDWNMHQ4I_cjs.jsonError("INTERNAL_ERROR", "Failed to create post", 500);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function PUT(request) {
|
|
62
|
+
try {
|
|
63
|
+
const authError = await chunkDWNMHQ4I_cjs.requireAuth(request);
|
|
64
|
+
if (authError) return authError;
|
|
65
|
+
const params = chunkDWNMHQ4I_cjs.getSearchParams(request);
|
|
66
|
+
const id = params.id;
|
|
67
|
+
if (!id) return chunkDWNMHQ4I_cjs.jsonError("MISSING_ID", "Post ID is required");
|
|
68
|
+
const body = await request.json();
|
|
69
|
+
const parsed = chunkV7QPRX4A_cjs.UpdatePostSchema.safeParse(body);
|
|
70
|
+
if (!parsed.success) {
|
|
71
|
+
const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
|
|
72
|
+
return chunkDWNMHQ4I_cjs.jsonError("VALIDATION_ERROR", errors.join("; "));
|
|
73
|
+
}
|
|
74
|
+
const post = await chunkXLYTATRQ_cjs.updatePost(id, parsed.data);
|
|
75
|
+
if (!post) return chunkDWNMHQ4I_cjs.jsonError("NOT_FOUND", "Post not found", 404);
|
|
76
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess(post);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("[blogpanel] PUT /posts error:", error);
|
|
79
|
+
return chunkDWNMHQ4I_cjs.jsonError("INTERNAL_ERROR", "Failed to update post", 500);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function DELETE(request) {
|
|
83
|
+
try {
|
|
84
|
+
const authError = await chunkDWNMHQ4I_cjs.requireAuth(request);
|
|
85
|
+
if (authError) return authError;
|
|
86
|
+
const params = chunkDWNMHQ4I_cjs.getSearchParams(request);
|
|
87
|
+
const id = params.id;
|
|
88
|
+
if (!id) return chunkDWNMHQ4I_cjs.jsonError("MISSING_ID", "Post ID is required");
|
|
89
|
+
const deleted = await chunkXLYTATRQ_cjs.deletePost(id);
|
|
90
|
+
if (!deleted) return chunkDWNMHQ4I_cjs.jsonError("NOT_FOUND", "Post not found", 404);
|
|
91
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess({ deleted: true });
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error("[blogpanel] DELETE /posts error:", error);
|
|
94
|
+
return chunkDWNMHQ4I_cjs.jsonError("INTERNAL_ERROR", "Failed to delete post", 500);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
exports.DELETE = DELETE;
|
|
99
|
+
exports.GET = GET;
|
|
100
|
+
exports.POST = POST;
|
|
101
|
+
exports.PUT = PUT;
|
|
102
|
+
//# sourceMappingURL=posts.cjs.map
|
|
103
|
+
//# sourceMappingURL=posts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/posts.ts"],"names":["getSearchParams","getPostBySlug","jsonError","jsonSuccess","getPostById","listPosts","parseIntParam","requireAuth","CreatePostSchema","createPost","UpdatePostSchema","updatePost","deletePost"],"mappings":"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","file":"posts.cjs","sourcesContent":["import {\n listPosts,\n getPostBySlug,\n getPostById,\n createPost,\n updatePost,\n deletePost,\n} from '../lib/db';\nimport { CreatePostSchema, UpdatePostSchema, type PostStatus } from '../lib/types';\nimport {\n jsonSuccess,\n jsonError,\n requireAuth,\n getSearchParams,\n parseIntParam,\n} from './middleware';\n\nexport async function GET(request: Request) {\n try {\n const params = getSearchParams(request);\n\n // Single post by slug\n if (params.slug) {\n const post = await getPostBySlug(params.slug);\n if (!post) return jsonError('NOT_FOUND', 'Post not found', 404);\n return jsonSuccess(post);\n }\n\n // Single post by id\n if (params.id) {\n const post = await getPostById(params.id);\n if (!post) return jsonError('NOT_FOUND', 'Post not found', 404);\n return jsonSuccess(post);\n }\n\n // List posts\n const result = await listPosts({\n page: parseIntParam(params.page, 1),\n limit: parseIntParam(params.limit, 10),\n category: params.category,\n tag: params.tag,\n status: params.status as PostStatus | undefined,\n search: params.q || params.search,\n sortBy: (params.sortBy as 'publishedAt' | 'createdAt' | 'title') || 'publishedAt',\n sortOrder: (params.sortOrder as 'asc' | 'desc') || 'desc',\n });\n\n const page = parseIntParam(params.page, 1);\n const limit = parseIntParam(params.limit, 10);\n\n return jsonSuccess(result.posts, {\n page,\n limit,\n total: result.total,\n totalPages: Math.ceil(result.total / limit),\n });\n } catch (error) {\n console.error('[blogpanel] GET /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to fetch posts', 500);\n }\n}\n\nexport async function POST(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const body = await request.json();\n const parsed = CreatePostSchema.safeParse(body);\n\n if (!parsed.success) {\n const errors = parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`);\n return jsonError('VALIDATION_ERROR', errors.join('; '));\n }\n\n const post = await createPost(parsed.data);\n return jsonSuccess(post, undefined, 201);\n } catch (error) {\n console.error('[blogpanel] POST /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to create post', 500);\n }\n}\n\nexport async function PUT(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const params = getSearchParams(request);\n const id = params.id;\n if (!id) return jsonError('MISSING_ID', 'Post ID is required');\n\n const body = await request.json();\n const parsed = UpdatePostSchema.safeParse(body);\n\n if (!parsed.success) {\n const errors = parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`);\n return jsonError('VALIDATION_ERROR', errors.join('; '));\n }\n\n const post = await updatePost(id, parsed.data);\n if (!post) return jsonError('NOT_FOUND', 'Post not found', 404);\n\n return jsonSuccess(post);\n } catch (error) {\n console.error('[blogpanel] PUT /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to update post', 500);\n }\n}\n\nexport async function DELETE(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const params = getSearchParams(request);\n const id = params.id;\n if (!id) return jsonError('MISSING_ID', 'Post ID is required');\n\n const deleted = await deletePost(id);\n if (!deleted) return jsonError('NOT_FOUND', 'Post not found', 404);\n\n return jsonSuccess({ deleted: true });\n } catch (error) {\n console.error('[blogpanel] DELETE /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to delete post', 500);\n }\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as next_server from 'next/server';
|
|
2
|
+
import { A as ApiErrorResponse } from '../types-CgrBuig-.cjs';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'mongodb';
|
|
5
|
+
|
|
6
|
+
declare function GET(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
7
|
+
success: boolean;
|
|
8
|
+
data: unknown;
|
|
9
|
+
meta: Record<string, unknown> | undefined;
|
|
10
|
+
}>>;
|
|
11
|
+
declare function POST(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
12
|
+
success: boolean;
|
|
13
|
+
data: unknown;
|
|
14
|
+
meta: Record<string, unknown> | undefined;
|
|
15
|
+
}>>;
|
|
16
|
+
declare function PUT(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
data: unknown;
|
|
19
|
+
meta: Record<string, unknown> | undefined;
|
|
20
|
+
}>>;
|
|
21
|
+
declare function DELETE(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
data: unknown;
|
|
24
|
+
meta: Record<string, unknown> | undefined;
|
|
25
|
+
}>>;
|
|
26
|
+
|
|
27
|
+
export { DELETE, GET, POST, PUT };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as next_server from 'next/server';
|
|
2
|
+
import { A as ApiErrorResponse } from '../types-CgrBuig-.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'mongodb';
|
|
5
|
+
|
|
6
|
+
declare function GET(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
7
|
+
success: boolean;
|
|
8
|
+
data: unknown;
|
|
9
|
+
meta: Record<string, unknown> | undefined;
|
|
10
|
+
}>>;
|
|
11
|
+
declare function POST(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
12
|
+
success: boolean;
|
|
13
|
+
data: unknown;
|
|
14
|
+
meta: Record<string, unknown> | undefined;
|
|
15
|
+
}>>;
|
|
16
|
+
declare function PUT(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
17
|
+
success: boolean;
|
|
18
|
+
data: unknown;
|
|
19
|
+
meta: Record<string, unknown> | undefined;
|
|
20
|
+
}>>;
|
|
21
|
+
declare function DELETE(request: Request): Promise<next_server.NextResponse<ApiErrorResponse> | next_server.NextResponse<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
data: unknown;
|
|
24
|
+
meta: Record<string, unknown> | undefined;
|
|
25
|
+
}>>;
|
|
26
|
+
|
|
27
|
+
export { DELETE, GET, POST, PUT };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { CreatePostSchema, UpdatePostSchema } from '../chunk-QLBTOCT3.js';
|
|
2
|
+
import { getSearchParams, jsonError, jsonSuccess, parseIntParam, requireAuth } from '../chunk-JBP3G4QJ.js';
|
|
3
|
+
import { getPostBySlug, getPostById, listPosts, createPost, updatePost, deletePost } from '../chunk-6PZ772NQ.js';
|
|
4
|
+
|
|
5
|
+
// src/api/posts.ts
|
|
6
|
+
async function GET(request) {
|
|
7
|
+
try {
|
|
8
|
+
const params = getSearchParams(request);
|
|
9
|
+
if (params.slug) {
|
|
10
|
+
const post = await getPostBySlug(params.slug);
|
|
11
|
+
if (!post) return jsonError("NOT_FOUND", "Post not found", 404);
|
|
12
|
+
return jsonSuccess(post);
|
|
13
|
+
}
|
|
14
|
+
if (params.id) {
|
|
15
|
+
const post = await getPostById(params.id);
|
|
16
|
+
if (!post) return jsonError("NOT_FOUND", "Post not found", 404);
|
|
17
|
+
return jsonSuccess(post);
|
|
18
|
+
}
|
|
19
|
+
const result = await listPosts({
|
|
20
|
+
page: parseIntParam(params.page, 1),
|
|
21
|
+
limit: parseIntParam(params.limit, 10),
|
|
22
|
+
category: params.category,
|
|
23
|
+
tag: params.tag,
|
|
24
|
+
status: params.status,
|
|
25
|
+
search: params.q || params.search,
|
|
26
|
+
sortBy: params.sortBy || "publishedAt",
|
|
27
|
+
sortOrder: params.sortOrder || "desc"
|
|
28
|
+
});
|
|
29
|
+
const page = parseIntParam(params.page, 1);
|
|
30
|
+
const limit = parseIntParam(params.limit, 10);
|
|
31
|
+
return jsonSuccess(result.posts, {
|
|
32
|
+
page,
|
|
33
|
+
limit,
|
|
34
|
+
total: result.total,
|
|
35
|
+
totalPages: Math.ceil(result.total / limit)
|
|
36
|
+
});
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error("[blogpanel] GET /posts error:", error);
|
|
39
|
+
return jsonError("INTERNAL_ERROR", "Failed to fetch posts", 500);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function POST(request) {
|
|
43
|
+
try {
|
|
44
|
+
const authError = await requireAuth(request);
|
|
45
|
+
if (authError) return authError;
|
|
46
|
+
const body = await request.json();
|
|
47
|
+
const parsed = CreatePostSchema.safeParse(body);
|
|
48
|
+
if (!parsed.success) {
|
|
49
|
+
const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
|
|
50
|
+
return jsonError("VALIDATION_ERROR", errors.join("; "));
|
|
51
|
+
}
|
|
52
|
+
const post = await createPost(parsed.data);
|
|
53
|
+
return jsonSuccess(post, void 0, 201);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error("[blogpanel] POST /posts error:", error);
|
|
56
|
+
return jsonError("INTERNAL_ERROR", "Failed to create post", 500);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function PUT(request) {
|
|
60
|
+
try {
|
|
61
|
+
const authError = await requireAuth(request);
|
|
62
|
+
if (authError) return authError;
|
|
63
|
+
const params = getSearchParams(request);
|
|
64
|
+
const id = params.id;
|
|
65
|
+
if (!id) return jsonError("MISSING_ID", "Post ID is required");
|
|
66
|
+
const body = await request.json();
|
|
67
|
+
const parsed = UpdatePostSchema.safeParse(body);
|
|
68
|
+
if (!parsed.success) {
|
|
69
|
+
const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
|
|
70
|
+
return jsonError("VALIDATION_ERROR", errors.join("; "));
|
|
71
|
+
}
|
|
72
|
+
const post = await updatePost(id, parsed.data);
|
|
73
|
+
if (!post) return jsonError("NOT_FOUND", "Post not found", 404);
|
|
74
|
+
return jsonSuccess(post);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("[blogpanel] PUT /posts error:", error);
|
|
77
|
+
return jsonError("INTERNAL_ERROR", "Failed to update post", 500);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function DELETE(request) {
|
|
81
|
+
try {
|
|
82
|
+
const authError = await requireAuth(request);
|
|
83
|
+
if (authError) return authError;
|
|
84
|
+
const params = getSearchParams(request);
|
|
85
|
+
const id = params.id;
|
|
86
|
+
if (!id) return jsonError("MISSING_ID", "Post ID is required");
|
|
87
|
+
const deleted = await deletePost(id);
|
|
88
|
+
if (!deleted) return jsonError("NOT_FOUND", "Post not found", 404);
|
|
89
|
+
return jsonSuccess({ deleted: true });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("[blogpanel] DELETE /posts error:", error);
|
|
92
|
+
return jsonError("INTERNAL_ERROR", "Failed to delete post", 500);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { DELETE, GET, POST, PUT };
|
|
97
|
+
//# sourceMappingURL=posts.js.map
|
|
98
|
+
//# sourceMappingURL=posts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/posts.ts"],"names":[],"mappings":"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","file":"posts.js","sourcesContent":["import {\n listPosts,\n getPostBySlug,\n getPostById,\n createPost,\n updatePost,\n deletePost,\n} from '../lib/db';\nimport { CreatePostSchema, UpdatePostSchema, type PostStatus } from '../lib/types';\nimport {\n jsonSuccess,\n jsonError,\n requireAuth,\n getSearchParams,\n parseIntParam,\n} from './middleware';\n\nexport async function GET(request: Request) {\n try {\n const params = getSearchParams(request);\n\n // Single post by slug\n if (params.slug) {\n const post = await getPostBySlug(params.slug);\n if (!post) return jsonError('NOT_FOUND', 'Post not found', 404);\n return jsonSuccess(post);\n }\n\n // Single post by id\n if (params.id) {\n const post = await getPostById(params.id);\n if (!post) return jsonError('NOT_FOUND', 'Post not found', 404);\n return jsonSuccess(post);\n }\n\n // List posts\n const result = await listPosts({\n page: parseIntParam(params.page, 1),\n limit: parseIntParam(params.limit, 10),\n category: params.category,\n tag: params.tag,\n status: params.status as PostStatus | undefined,\n search: params.q || params.search,\n sortBy: (params.sortBy as 'publishedAt' | 'createdAt' | 'title') || 'publishedAt',\n sortOrder: (params.sortOrder as 'asc' | 'desc') || 'desc',\n });\n\n const page = parseIntParam(params.page, 1);\n const limit = parseIntParam(params.limit, 10);\n\n return jsonSuccess(result.posts, {\n page,\n limit,\n total: result.total,\n totalPages: Math.ceil(result.total / limit),\n });\n } catch (error) {\n console.error('[blogpanel] GET /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to fetch posts', 500);\n }\n}\n\nexport async function POST(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const body = await request.json();\n const parsed = CreatePostSchema.safeParse(body);\n\n if (!parsed.success) {\n const errors = parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`);\n return jsonError('VALIDATION_ERROR', errors.join('; '));\n }\n\n const post = await createPost(parsed.data);\n return jsonSuccess(post, undefined, 201);\n } catch (error) {\n console.error('[blogpanel] POST /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to create post', 500);\n }\n}\n\nexport async function PUT(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const params = getSearchParams(request);\n const id = params.id;\n if (!id) return jsonError('MISSING_ID', 'Post ID is required');\n\n const body = await request.json();\n const parsed = UpdatePostSchema.safeParse(body);\n\n if (!parsed.success) {\n const errors = parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`);\n return jsonError('VALIDATION_ERROR', errors.join('; '));\n }\n\n const post = await updatePost(id, parsed.data);\n if (!post) return jsonError('NOT_FOUND', 'Post not found', 404);\n\n return jsonSuccess(post);\n } catch (error) {\n console.error('[blogpanel] PUT /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to update post', 500);\n }\n}\n\nexport async function DELETE(request: Request) {\n try {\n const authError = await requireAuth(request);\n if (authError) return authError;\n\n const params = getSearchParams(request);\n const id = params.id;\n if (!id) return jsonError('MISSING_ID', 'Post ID is required');\n\n const deleted = await deletePost(id);\n if (!deleted) return jsonError('NOT_FOUND', 'Post not found', 404);\n\n return jsonSuccess({ deleted: true });\n } catch (error) {\n console.error('[blogpanel] DELETE /posts error:', error);\n return jsonError('INTERNAL_ERROR', 'Failed to delete post', 500);\n }\n}\n"]}
|
package/dist/api/rss.cjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkDAX5C6SC_cjs = require('../chunk-DAX5C6SC.cjs');
|
|
4
|
+
require('../chunk-XLYTATRQ.cjs');
|
|
5
|
+
var server = require('next/server');
|
|
6
|
+
|
|
7
|
+
async function GET() {
|
|
8
|
+
try {
|
|
9
|
+
const xml = await chunkDAX5C6SC_cjs.generateRSSFeed();
|
|
10
|
+
return new server.NextResponse(xml, {
|
|
11
|
+
status: 200,
|
|
12
|
+
headers: {
|
|
13
|
+
"Content-Type": "application/rss+xml",
|
|
14
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600"
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error("[blogpanel] RSS generation error:", error);
|
|
19
|
+
return new server.NextResponse("RSS generation failed", { status: 500 });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exports.GET = GET;
|
|
24
|
+
//# sourceMappingURL=rss.cjs.map
|
|
25
|
+
//# sourceMappingURL=rss.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/rss.ts"],"names":["generateRSSFeed","NextResponse"],"mappings":";;;;;;AAGA,eAAsB,GAAA,GAAM;AAC1B,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAMA,iCAAA,EAAgB;AAClC,IAAA,OAAO,IAAIC,oBAAa,GAAA,EAAK;AAAA,MAC3B,MAAA,EAAQ,GAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,qBAAA;AAAA,QAChB,eAAA,EAAiB;AAAA;AACnB,KACD,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,qCAAqC,KAAK,CAAA;AACxD,IAAA,OAAO,IAAIA,mBAAA,CAAa,uBAAA,EAAyB,EAAE,MAAA,EAAQ,KAAK,CAAA;AAAA,EAClE;AACF","file":"rss.cjs","sourcesContent":["import { NextResponse } from 'next/server';\nimport { generateRSSFeed } from '../lib/rss';\n\nexport async function GET() {\n try {\n const xml = await generateRSSFeed();\n return new NextResponse(xml, {\n status: 200,\n headers: {\n 'Content-Type': 'application/rss+xml',\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600',\n },\n });\n } catch (error) {\n console.error('[blogpanel] RSS generation error:', error);\n return new NextResponse('RSS generation failed', { status: 500 });\n }\n}\n"]}
|
package/dist/api/rss.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { generateRSSFeed } from '../chunk-VEJFN65Y.js';
|
|
2
|
+
import '../chunk-6PZ772NQ.js';
|
|
3
|
+
import { NextResponse } from 'next/server';
|
|
4
|
+
|
|
5
|
+
async function GET() {
|
|
6
|
+
try {
|
|
7
|
+
const xml = await generateRSSFeed();
|
|
8
|
+
return new NextResponse(xml, {
|
|
9
|
+
status: 200,
|
|
10
|
+
headers: {
|
|
11
|
+
"Content-Type": "application/rss+xml",
|
|
12
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600"
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error("[blogpanel] RSS generation error:", error);
|
|
17
|
+
return new NextResponse("RSS generation failed", { status: 500 });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { GET };
|
|
22
|
+
//# sourceMappingURL=rss.js.map
|
|
23
|
+
//# sourceMappingURL=rss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/rss.ts"],"names":[],"mappings":";;;;AAGA,eAAsB,GAAA,GAAM;AAC1B,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,eAAA,EAAgB;AAClC,IAAA,OAAO,IAAI,aAAa,GAAA,EAAK;AAAA,MAC3B,MAAA,EAAQ,GAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,qBAAA;AAAA,QAChB,eAAA,EAAiB;AAAA;AACnB,KACD,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,qCAAqC,KAAK,CAAA;AACxD,IAAA,OAAO,IAAI,YAAA,CAAa,uBAAA,EAAyB,EAAE,MAAA,EAAQ,KAAK,CAAA;AAAA,EAClE;AACF","file":"rss.js","sourcesContent":["import { NextResponse } from 'next/server';\nimport { generateRSSFeed } from '../lib/rss';\n\nexport async function GET() {\n try {\n const xml = await generateRSSFeed();\n return new NextResponse(xml, {\n status: 200,\n headers: {\n 'Content-Type': 'application/rss+xml',\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600',\n },\n });\n } catch (error) {\n console.error('[blogpanel] RSS generation error:', error);\n return new NextResponse('RSS generation failed', { status: 500 });\n }\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkV7QPRX4A_cjs = require('../chunk-V7QPRX4A.cjs');
|
|
4
|
+
var chunkDWNMHQ4I_cjs = require('../chunk-DWNMHQ4I.cjs');
|
|
5
|
+
var chunkXLYTATRQ_cjs = require('../chunk-XLYTATRQ.cjs');
|
|
6
|
+
|
|
7
|
+
// src/api/settings.ts
|
|
8
|
+
async function GET(request) {
|
|
9
|
+
try {
|
|
10
|
+
const authError = await chunkDWNMHQ4I_cjs.requireAuth(request);
|
|
11
|
+
if (authError) return authError;
|
|
12
|
+
const settings = await chunkXLYTATRQ_cjs.getSettings();
|
|
13
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess(settings);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error("[blogpanel] GET /settings error:", error);
|
|
16
|
+
return chunkDWNMHQ4I_cjs.jsonError("INTERNAL_ERROR", "Failed to fetch settings", 500);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function PUT(request) {
|
|
20
|
+
try {
|
|
21
|
+
const authError = await chunkDWNMHQ4I_cjs.requireAuth(request);
|
|
22
|
+
if (authError) return authError;
|
|
23
|
+
const body = await request.json();
|
|
24
|
+
const parsed = chunkV7QPRX4A_cjs.BlogSettingsSchema.partial().safeParse(body);
|
|
25
|
+
if (!parsed.success) {
|
|
26
|
+
const errors = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
|
|
27
|
+
return chunkDWNMHQ4I_cjs.jsonError("VALIDATION_ERROR", errors.join("; "));
|
|
28
|
+
}
|
|
29
|
+
const settings = await chunkXLYTATRQ_cjs.updateSettings(parsed.data);
|
|
30
|
+
return chunkDWNMHQ4I_cjs.jsonSuccess(settings);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error("[blogpanel] PUT /settings error:", error);
|
|
33
|
+
return chunkDWNMHQ4I_cjs.jsonError("INTERNAL_ERROR", "Failed to update settings", 500);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.GET = GET;
|
|
38
|
+
exports.PUT = PUT;
|
|
39
|
+
//# sourceMappingURL=settings.cjs.map
|
|
40
|
+
//# sourceMappingURL=settings.cjs.map
|