feat: 添加文章管理的 RESTful API 接口及 Token 认证机制
This commit is contained in:
1 parent
70db079379
commit
fe08cd27ff
3 files changed
+239
No files matched your search
@@ -23,6 +23,47 @@ function accessNotConfigured(): Response {
|
||||
}
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
if (event.url.pathname.startsWith('/api/v1/')) {
|
||||
const authHeader = event.request.headers.get('Authorization');
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return new Response(JSON.stringify({ error: 'Unauthorized: Missing or invalid token' }), {
|
||||
status: 401,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
const token = authHeader.slice('Bearer '.length).trim();
|
||||
const db = event.platform?.env.DB;
|
||||
if (!db) {
|
||||
return new Response(JSON.stringify({ error: 'Database not available' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await db
|
||||
.prepare("SELECT value FROM settings WHERE key = 'agent.api_token'")
|
||||
.first<{ value: string }>();
|
||||
|
||||
const validToken = result?.value;
|
||||
if (!validToken || token !== validToken) {
|
||||
return new Response(JSON.stringify({ error: 'Unauthorized: Invalid token' }), {
|
||||
status: 401,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('API token verification failed', err);
|
||||
return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
if (!event.url.pathname.startsWith('/admin')) {
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const GET: RequestHandler = async ({ platform, url }) => {
|
||||
const db = platform?.env.DB;
|
||||
if (!db) return json({ error: 'Database not connected' }, { status: 500 });
|
||||
|
||||
const limit = parseInt(url.searchParams.get('limit') || '10', 10);
|
||||
const offset = parseInt(url.searchParams.get('offset') || '0', 10);
|
||||
|
||||
try {
|
||||
const { results } = await db
|
||||
.prepare(
|
||||
`
|
||||
SELECT p.id, p.slug, p.title, p.published_at, p.updated_at, c.slug as category_slug
|
||||
FROM posts p
|
||||
LEFT JOIN categories c ON p.category_id = c.id
|
||||
ORDER BY p.published_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
)
|
||||
.bind(limit, offset)
|
||||
.all();
|
||||
|
||||
const { results: countResult } = await db.prepare('SELECT COUNT(*) as total FROM posts').all();
|
||||
const total = (countResult[0] as { total: number })?.total || 0;
|
||||
|
||||
return json({
|
||||
data: results,
|
||||
meta: {
|
||||
total,
|
||||
limit,
|
||||
offset
|
||||
}
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
console.error('Failed to get posts', err);
|
||||
return json({ error: 'Failed to retrieve posts' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
export const POST: RequestHandler = async ({ request, platform }) => {
|
||||
const db = platform?.env.DB;
|
||||
if (!db) return json({ error: 'Database not connected' }, { status: 500 });
|
||||
|
||||
try {
|
||||
const data = (await request.json()) as Record<string, unknown>;
|
||||
const { slug, title, content, category_slug } = data as {
|
||||
slug?: string;
|
||||
title?: string;
|
||||
content?: string;
|
||||
category_slug?: string;
|
||||
};
|
||||
|
||||
if (!slug || !title || !content) {
|
||||
return json({ error: 'slug, title, and content are required' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Get default admin user id (assuming id=1 for simplicity as a bot)
|
||||
const authorId = 1;
|
||||
|
||||
let categoryId = null;
|
||||
if (category_slug) {
|
||||
const catResult = await db
|
||||
.prepare('SELECT id FROM categories WHERE slug = ?')
|
||||
.bind(category_slug)
|
||||
.first<{ id: number }>();
|
||||
if (catResult) categoryId = catResult.id;
|
||||
}
|
||||
|
||||
const result = await db
|
||||
.prepare(
|
||||
`
|
||||
INSERT INTO posts (slug, title, content, author_id, category_id, published_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
`
|
||||
)
|
||||
.bind(slug, title, content, authorId, categoryId)
|
||||
.run();
|
||||
|
||||
if (result.success) {
|
||||
return json({ success: true, slug, title });
|
||||
} else {
|
||||
return json({ error: 'Failed to insert post' }, { status: 500 });
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error('Failed to create post', err);
|
||||
// Duplicate slug error handling
|
||||
if (err instanceof Error && err.message.includes('UNIQUE constraint failed')) {
|
||||
return json({ error: 'Slug already exists' }, { status: 409 });
|
||||
}
|
||||
return json({ error: 'Invalid request data or server error' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const GET: RequestHandler = async ({ params, platform }) => {
|
||||
const db = platform?.env.DB;
|
||||
if (!db) return json({ error: 'Database not connected' }, { status: 500 });
|
||||
|
||||
const { slug } = params;
|
||||
|
||||
try {
|
||||
const post = await db
|
||||
.prepare(
|
||||
`
|
||||
SELECT p.id, p.slug, p.title, p.content, p.published_at, p.updated_at, c.slug as category_slug
|
||||
FROM posts p
|
||||
LEFT JOIN categories c ON p.category_id = c.id
|
||||
WHERE p.slug = ?
|
||||
`
|
||||
)
|
||||
.bind(slug)
|
||||
.first();
|
||||
|
||||
if (!post) {
|
||||
return json({ error: 'Post not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return json({ data: post });
|
||||
} catch (err: unknown) {
|
||||
console.error('Failed to get post', err);
|
||||
return json({ error: 'Failed to retrieve post' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
export const PUT: RequestHandler = async ({ params, request, platform }) => {
|
||||
const db = platform?.env.DB;
|
||||
if (!db) return json({ error: 'Database not connected' }, { status: 500 });
|
||||
|
||||
const { slug } = params;
|
||||
|
||||
try {
|
||||
const data = (await request.json()) as Record<string, unknown>;
|
||||
const { title, content, category_slug } = data as {
|
||||
title?: string;
|
||||
content?: string;
|
||||
category_slug?: string;
|
||||
};
|
||||
|
||||
if (!title && !content && !category_slug) {
|
||||
return json({ error: 'No fields to update' }, { status: 400 });
|
||||
}
|
||||
|
||||
// First, check if post exists
|
||||
const existingPost = await db
|
||||
.prepare('SELECT id FROM posts WHERE slug = ?')
|
||||
.bind(slug)
|
||||
.first<{ id: number }>();
|
||||
if (!existingPost) {
|
||||
return json({ error: 'Post not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const updates: string[] = [];
|
||||
const bindings: unknown[] = [];
|
||||
|
||||
if (title) {
|
||||
updates.push('title = ?');
|
||||
bindings.push(title);
|
||||
}
|
||||
if (content) {
|
||||
updates.push('content = ?');
|
||||
bindings.push(content);
|
||||
}
|
||||
if (category_slug !== undefined) {
|
||||
let categoryId = null;
|
||||
if (category_slug) {
|
||||
const catResult = await db
|
||||
.prepare('SELECT id FROM categories WHERE slug = ?')
|
||||
.bind(category_slug)
|
||||
.first<{ id: number }>();
|
||||
if (catResult) categoryId = catResult.id;
|
||||
}
|
||||
updates.push('category_id = ?');
|
||||
bindings.push(categoryId);
|
||||
}
|
||||
|
||||
updates.push('updated_at = CURRENT_TIMESTAMP');
|
||||
bindings.push(slug); // for WHERE clause
|
||||
|
||||
const query = `UPDATE posts SET ${updates.join(', ')} WHERE slug = ?`;
|
||||
|
||||
const result = await db
|
||||
.prepare(query)
|
||||
.bind(...bindings)
|
||||
.run();
|
||||
|
||||
if (result.success) {
|
||||
return json({ success: true, slug });
|
||||
} else {
|
||||
return json({ error: 'Failed to update post' }, { status: 500 });
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error('Failed to update post', err);
|
||||
return json({ error: 'Invalid request data or server error' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user