Images now uploading
This commit is contained in:
@@ -192,7 +192,7 @@ export default function Editor() {
|
||||
placeholder="Add tags (press Enter)"
|
||||
value={tagInput}
|
||||
onChange={(e) => setTagInput(e.target.value)}
|
||||
onKeyPress={handleAddTag}
|
||||
onKeyDown={handleAddTag}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// File: src/routes/images.js
|
||||
// src/routes/images.js
|
||||
import { pipeline } from 'stream/promises';
|
||||
import { createWriteStream } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import slugify from 'slugify';
|
||||
import { db } from '../db.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -19,7 +20,88 @@ const authenticate = async (request, reply) => {
|
||||
};
|
||||
|
||||
export default async function imageRoutes(fastify) {
|
||||
// Upload image for a post
|
||||
// Get all images
|
||||
fastify.get('/api/images', {
|
||||
onRequest: [authenticate],
|
||||
handler: async (request, reply) => {
|
||||
try {
|
||||
const images = await db.any(
|
||||
`SELECT i.* FROM images i
|
||||
JOIN posts p ON i.post_id = p.id
|
||||
WHERE p.author_id = $1
|
||||
ORDER BY i.uploaded_at DESC`,
|
||||
[request.user.id]
|
||||
);
|
||||
reply.send(images);
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
reply.code(500).send({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Upload image without post association
|
||||
fastify.post('/api/images/upload', {
|
||||
onRequest: [authenticate],
|
||||
handler: async (request, reply) => {
|
||||
try {
|
||||
// Handle file upload
|
||||
const data = await request.file();
|
||||
|
||||
if (!data) {
|
||||
reply.code(400).send({ error: 'No file uploaded' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file type
|
||||
const allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
if (!allowedMimeTypes.includes(data.mimetype)) {
|
||||
reply.code(400).send({ error: 'Invalid file type' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Create unique filename
|
||||
const fileExt = data.filename.split('.').pop();
|
||||
const filename = `${Date.now()}-${Math.random().toString(36).slice(2)}.${fileExt}`;
|
||||
const uploadDir = join(__dirname, '../../uploads');
|
||||
const filepath = join(uploadDir, filename);
|
||||
|
||||
// Save file to disk
|
||||
await pipeline(data.file, createWriteStream(filepath));
|
||||
|
||||
// First create a draft post to associate the image with
|
||||
const tempTitle = `Image Upload ${new Date().toISOString()}`;
|
||||
const post = await db.one(
|
||||
`INSERT INTO posts (title, slug, markdown_content, compiled_content, status, author_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id`,
|
||||
[
|
||||
tempTitle,
|
||||
slugify(tempTitle, { lower: true }),
|
||||
'',
|
||||
'',
|
||||
'draft',
|
||||
request.user.id
|
||||
]
|
||||
);
|
||||
|
||||
// Save image record in database
|
||||
const image = await db.one(
|
||||
`INSERT INTO images (filename, url, post_id)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *`,
|
||||
[filename, `/uploads/${filename}`, post.id]
|
||||
);
|
||||
|
||||
reply.code(201).send(image);
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
reply.code(500).send({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Upload image for a specific post
|
||||
fastify.post('/api/posts/:postId/images', {
|
||||
onRequest: [authenticate],
|
||||
handler: async (request, reply) => {
|
||||
@@ -99,9 +181,6 @@ export default async function imageRoutes(fastify) {
|
||||
// Delete from database
|
||||
await db.none('DELETE FROM images WHERE id = $1', [id]);
|
||||
|
||||
// We could also delete the file from disk here, but we might want to keep it
|
||||
// for backup purposes or to handle undo operations
|
||||
|
||||
reply.code(204).send();
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
@@ -109,27 +188,4 @@ export default async function imageRoutes(fastify) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Get images for a post
|
||||
fastify.get('/api/posts/:postId/images', {
|
||||
onRequest: [authenticate],
|
||||
handler: async (request, reply) => {
|
||||
const { postId } = request.params;
|
||||
|
||||
try {
|
||||
const images = await db.any(
|
||||
`SELECT i.* FROM images i
|
||||
JOIN posts p ON i.post_id = p.id
|
||||
WHERE p.id = $1 AND p.author_id = $2
|
||||
ORDER BY i.uploaded_at DESC`,
|
||||
[postId, request.user.id]
|
||||
);
|
||||
|
||||
reply.send(images);
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
reply.code(500).send({ error: 'Internal server error' });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user