-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8c35ec2
commit 8e50264
Showing
37 changed files
with
5,527 additions
and
335 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
import { getDb } from '@/app/lib/db'; | ||
import { getUser } from '@/app/lib/auth'; | ||
|
||
export async function DELETE( | ||
request: NextRequest, | ||
{ params }: { params: { slug: string; id: string } } | ||
) { | ||
try { | ||
const currentUser = await getUser(); | ||
if (!currentUser) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Authorization required'] } }), | ||
{ status: 401 } | ||
); | ||
} | ||
|
||
const db = await getDb(); | ||
const { slug, id } = params; | ||
|
||
const article = await db.get('SELECT id FROM articles WHERE slug = ?', [slug]); | ||
if (!article) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Article not found'] } }), | ||
{ status: 404 } | ||
); | ||
} | ||
|
||
const comment = await db.get( | ||
'SELECT * FROM comments WHERE id = ? AND article_id = ? AND author_id = ?', | ||
[id, article.id, currentUser.id] | ||
); | ||
|
||
if (!comment) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Comment not found or unauthorized'] } }), | ||
{ status: 404 } | ||
); | ||
} | ||
|
||
await db.run('DELETE FROM comments WHERE id = ?', [id]); | ||
|
||
return new NextResponse(null, { status: 204 }); | ||
} catch (error) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Could not delete comment'] } }), | ||
{ status: 422 } | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
import { getDb } from '@/app/lib/db'; | ||
import { getUser } from '@/app/lib/auth'; | ||
|
||
export async function GET( | ||
request: NextRequest, | ||
{ params }: { params: { slug: string } } | ||
) { | ||
try { | ||
const db = await getDb(); | ||
const { slug } = params; | ||
|
||
const article = await db.get('SELECT id FROM articles WHERE slug = ?', [slug]); | ||
if (!article) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Article not found'] } }), | ||
{ status: 404 } | ||
); | ||
} | ||
|
||
const comments = await db.all(` | ||
SELECT | ||
c.id, c.body, c.created_at as createdAt, c.updated_at as updatedAt, | ||
u.username, u.bio, u.image | ||
FROM comments c | ||
JOIN users u ON c.author_id = u.id | ||
WHERE c.article_id = ? | ||
ORDER BY c.created_at DESC | ||
`, [article.id]); | ||
|
||
const currentUser = await getUser(); | ||
const commentsWithAuthor = await Promise.all( | ||
comments.map(async (comment: any) => { | ||
let following = false; | ||
if (currentUser) { | ||
const follow = await db.get( | ||
'SELECT 1 FROM follows WHERE follower_id = ? AND followed_id = (SELECT id FROM users WHERE username = ?)', | ||
[currentUser.id, comment.username] | ||
); | ||
following = !!follow; | ||
} | ||
|
||
return { | ||
id: comment.id, | ||
createdAt: comment.createdAt, | ||
updatedAt: comment.updatedAt, | ||
body: comment.body, | ||
author: { | ||
username: comment.username, | ||
bio: comment.bio, | ||
image: comment.image, | ||
following | ||
} | ||
}; | ||
}) | ||
); | ||
|
||
return new NextResponse( | ||
JSON.stringify({ comments: commentsWithAuthor }) | ||
); | ||
} catch (error) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Could not get comments'] } }), | ||
{ status: 422 } | ||
); | ||
} | ||
} | ||
|
||
export async function POST( | ||
request: NextRequest, | ||
{ params }: { params: { slug: string } } | ||
) { | ||
try { | ||
const currentUser = await getUser(); | ||
if (!currentUser) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Authorization required'] } }), | ||
{ status: 401 } | ||
); | ||
} | ||
|
||
const db = await getDb(); | ||
const { slug } = params; | ||
const { comment } = await request.json(); | ||
|
||
if (!comment.body) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Comment body is required'] } }), | ||
{ status: 422 } | ||
); | ||
} | ||
|
||
const article = await db.get('SELECT id FROM articles WHERE slug = ?', [slug]); | ||
if (!article) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Article not found'] } }), | ||
{ status: 404 } | ||
); | ||
} | ||
|
||
const result = await db.run( | ||
'INSERT INTO comments (body, article_id, author_id) VALUES (?, ?, ?)', | ||
[comment.body, article.id, currentUser.id] | ||
); | ||
|
||
const createdComment = await db.get(` | ||
SELECT | ||
c.id, c.body, c.created_at as createdAt, c.updated_at as updatedAt, | ||
u.username, u.bio, u.image | ||
FROM comments c | ||
JOIN users u ON c.author_id = u.id | ||
WHERE c.id = ? | ||
`, [result.lastID]); | ||
|
||
return new NextResponse( | ||
JSON.stringify({ | ||
comment: { | ||
id: createdComment.id, | ||
createdAt: createdComment.createdAt, | ||
updatedAt: createdComment.updatedAt, | ||
body: createdComment.body, | ||
author: { | ||
username: createdComment.username, | ||
bio: createdComment.bio, | ||
image: createdComment.image, | ||
following: false | ||
} | ||
} | ||
}), | ||
{ status: 201 } | ||
); | ||
} catch (error) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Could not create comment'] } }), | ||
{ status: 422 } | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
import { getDb } from '@/app/lib/db'; | ||
import { getUser } from '@/app/lib/auth'; | ||
|
||
async function getArticleResponse(db: any, slug: string, currentUser: any) { | ||
const article = await db.get(` | ||
SELECT articles.*, users.username, users.bio, users.image, | ||
(SELECT COUNT(*) FROM favorites WHERE article_id = articles.id) as favoritesCount, | ||
EXISTS(SELECT 1 FROM favorites WHERE article_id = articles.id AND user_id = ?) as favorited | ||
FROM articles | ||
LEFT JOIN users ON articles.author_id = users.id | ||
WHERE slug = ? | ||
`, [currentUser.id, slug]); | ||
|
||
if (!article) return null; | ||
|
||
return { | ||
article: { | ||
...article, | ||
favorited: Boolean(article.favorited), | ||
author: { | ||
username: article.username, | ||
bio: article.bio, | ||
image: article.image, | ||
} | ||
} | ||
}; | ||
} | ||
|
||
export async function POST( | ||
request: NextRequest, | ||
{ params }: { params: { slug: string } } | ||
) { | ||
try { | ||
const currentUser = await getUser(); | ||
if (!currentUser) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Authorization required'] } }), | ||
{ status: 401 } | ||
); | ||
} | ||
|
||
const db = await getDb(); | ||
const { slug } = params; | ||
|
||
const article = await db.get('SELECT * FROM articles WHERE slug = ?', [slug]); | ||
if (!article) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Article not found'] } }), | ||
{ status: 404 } | ||
); | ||
} | ||
|
||
await db.run( | ||
'INSERT OR IGNORE INTO favorites (user_id, article_id) VALUES (?, ?)', | ||
[currentUser.id, article.id] | ||
); | ||
|
||
const response = await getArticleResponse(db, slug, currentUser); | ||
return NextResponse.json(response); | ||
} catch (error) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Could not favorite article'] } }), | ||
{ status: 422 } | ||
); | ||
} | ||
} | ||
|
||
export async function DELETE( | ||
request: NextRequest, | ||
{ params }: { params: { slug: string } } | ||
) { | ||
try { | ||
const currentUser = await getUser(); | ||
if (!currentUser) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Authorization required'] } }), | ||
{ status: 401 } | ||
); | ||
} | ||
|
||
const db = await getDb(); | ||
const { slug } = params; | ||
|
||
const article = await db.get('SELECT * FROM articles WHERE slug = ?', [slug]); | ||
if (!article) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Article not found'] } }), | ||
{ status: 404 } | ||
); | ||
} | ||
|
||
await db.run( | ||
'DELETE FROM favorites WHERE user_id = ? AND article_id = ?', | ||
[currentUser.id, article.id] | ||
); | ||
|
||
const response = await getArticleResponse(db, slug, currentUser); | ||
return NextResponse.json(response); | ||
} catch (error) { | ||
return new NextResponse( | ||
JSON.stringify({ errors: { body: ['Could not unfavorite article'] } }), | ||
{ status: 422 } | ||
); | ||
} | ||
} |
Oops, something went wrong.