-
Notifications
You must be signed in to change notification settings - Fork 7.4k
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
a902bd1
commit d06f225
Showing
41 changed files
with
1,203 additions
and
8,519 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 |
---|---|---|
@@ -1,4 +1,8 @@ | ||
//https://nitro.unjs.io/config | ||
export default defineNitroConfig({ | ||
srcDir: "server" | ||
srcDir: "server", | ||
preset: 'heroku', | ||
routeRules: { | ||
'/api/**': { cors: true, headers: { 'access-control-allow-methods': '*' } }, | ||
} | ||
}); |
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
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
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,57 @@ | ||
import {default as jwt} from "jsonwebtoken"; | ||
|
||
export interface PrivateContext { | ||
auth: { | ||
id: number; | ||
} | ||
} | ||
|
||
export function definePrivateEventHandler<T>( | ||
handler: (event: H3Event, cxt: PrivateContext) => T, | ||
options: { requireAuth: boolean } = {requireAuth: true} | ||
) { | ||
return defineEventHandler(async (event) => { | ||
// you can check request hmac, user, token, etc.. | ||
const header = getHeader(event, 'authorization'); | ||
let token; | ||
|
||
if ( | ||
(header && header.split(' ')[0] === 'Token') || | ||
(header && header.split(' ')[0] === 'Bearer') | ||
) { | ||
token = header.split(' ')[1]; | ||
} | ||
|
||
if (options.requireAuth && !token) { | ||
throw createError({ | ||
status: 401, | ||
statusMessage: 'Unauthorized', | ||
message: 'Missing authentication token' | ||
}); | ||
} | ||
|
||
if (token) { | ||
const verified = jwt.verify(token, process.env.JWT_SECRET); | ||
|
||
if (!verified) { | ||
throw createError({ | ||
status: 403, | ||
statusMessage: 'Unauthorized', | ||
message: 'Invalid authentication token' | ||
}); | ||
} | ||
|
||
return handler(event, { | ||
auth: { | ||
id: Number(verified.user.id) | ||
}, | ||
}) | ||
} else { | ||
return handler(event, { | ||
auth: null, | ||
}) | ||
} | ||
|
||
|
||
}) | ||
} |
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,10 @@ | ||
import { Comment } from './comment.model'; | ||
|
||
export interface Article { | ||
id: number; | ||
title: string; | ||
slug: string; | ||
description: string; | ||
comments: Comment[]; | ||
favorited: boolean; | ||
} |
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,9 @@ | ||
import { Article } from './article.model'; | ||
|
||
export interface Comment { | ||
id: number; | ||
createdAt: Date; | ||
updatedAt: Date; | ||
body: string; | ||
article?: Article; | ||
} |
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,12 @@ | ||
class HttpException extends Error { | ||
errorCode: number; | ||
constructor( | ||
errorCode: number, | ||
public readonly message: string | any, | ||
) { | ||
super(message); | ||
this.errorCode = errorCode; | ||
} | ||
} | ||
|
||
export default HttpException; |
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,6 @@ | ||
export interface Profile { | ||
username: string; | ||
bio: string; | ||
image: string; | ||
following: boolean; | ||
} |
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,3 @@ | ||
export interface Tag { | ||
name: string; | ||
} |
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,17 @@ | ||
import { Article } from './article.model'; | ||
import { Comment } from './comment.model'; | ||
|
||
export interface User { | ||
id: number; | ||
username: string; | ||
email: string; | ||
password: string; | ||
bio: string | null; | ||
image: any | null; | ||
articles: Article[]; | ||
favorites: Article[]; | ||
followedBy: User[]; | ||
following: User[]; | ||
comments: Comment[]; | ||
demo: boolean; | ||
} |
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,4 @@ | ||
export default defineEventHandler(async (event) => { | ||
setResponseStatus(event, 200); | ||
return ""; | ||
}); |
39 changes: 39 additions & 0 deletions
39
apps/api/server/routes/api/articles/[slug]/comments/[id].delete.ts
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,39 @@ | ||
import HttpException from "~/models/http-exception.model"; | ||
import {definePrivateEventHandler} from "~/auth-event-handler"; | ||
|
||
export default definePrivateEventHandler(async (event, {auth}) => { | ||
const id = Number(getRouterParam(event, 'id')); | ||
|
||
const comment = await usePrisma().comment.findFirst({ | ||
where: { | ||
id, | ||
author: { | ||
id: auth.id, | ||
}, | ||
}, | ||
select: { | ||
author: { | ||
select: { | ||
id: true, | ||
username: true, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
if (!comment) { | ||
throw new HttpException(404, {}); | ||
} | ||
|
||
if (comment.author.id !== auth.id) { | ||
throw new HttpException(403, { | ||
message: 'You are not authorized to delete this comment', | ||
}); | ||
} | ||
|
||
await usePrisma().comment.delete({ | ||
where: { | ||
id, | ||
}, | ||
}); | ||
}); |
60 changes: 60 additions & 0 deletions
60
apps/api/server/routes/api/articles/[slug]/comments/index.get.ts
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,60 @@ | ||
import {definePrivateEventHandler} from "~/auth-event-handler"; | ||
|
||
export default definePrivateEventHandler(async (event, {auth}) => { | ||
const slug = getRouterParam(event, 'slug'); | ||
|
||
const queries = []; | ||
|
||
queries.push({ | ||
author: { | ||
demo: true, | ||
}, | ||
}); | ||
|
||
if (auth?.id) { | ||
queries.push({ | ||
author: { | ||
id: auth.id, | ||
}, | ||
}); | ||
} | ||
|
||
const comments = await usePrisma().article.findUnique({ | ||
where: { | ||
slug, | ||
}, | ||
include: { | ||
comments: { | ||
where: { | ||
OR: queries, | ||
}, | ||
select: { | ||
id: true, | ||
createdAt: true, | ||
updatedAt: true, | ||
body: true, | ||
author: { | ||
select: { | ||
username: true, | ||
bio: true, | ||
image: true, | ||
followedBy: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const result = comments?.comments.map((comment: any) => ({ | ||
...comment, | ||
author: { | ||
username: comment.author.username, | ||
bio: comment.author.bio, | ||
image: comment.author.image, | ||
following: comment.author.followedBy.some((follow: any) => follow.id === auth.id), | ||
}, | ||
})); | ||
|
||
return {comments: result}; | ||
}, {requireAuth: false}); |
61 changes: 61 additions & 0 deletions
61
apps/api/server/routes/api/articles/[slug]/comments/index.post.ts
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,61 @@ | ||
import HttpException from "~/models/http-exception.model"; | ||
import {definePrivateEventHandler} from "~/auth-event-handler"; | ||
|
||
export default definePrivateEventHandler(async (event, {auth}) => { | ||
const {comment} = await readBody(event); | ||
const slug = getRouterParam(event, 'slug'); | ||
|
||
if (!comment.body) { | ||
throw new HttpException(422, {errors: {body: ["can't be blank"]}}); | ||
} | ||
|
||
const article = await usePrisma().article.findUnique({ | ||
where: { | ||
slug, | ||
}, | ||
select: { | ||
id: true, | ||
}, | ||
}); | ||
|
||
const createdComment = await usePrisma().comment.create({ | ||
data: { | ||
body: comment.body, | ||
article: { | ||
connect: { | ||
id: article?.id, | ||
}, | ||
}, | ||
author: { | ||
connect: { | ||
id: auth.id, | ||
}, | ||
}, | ||
}, | ||
include: { | ||
author: { | ||
select: { | ||
username: true, | ||
bio: true, | ||
image: true, | ||
followedBy: true, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
return { | ||
comment: { | ||
id: createdComment.id, | ||
createdAt: createdComment.createdAt, | ||
updatedAt: createdComment.updatedAt, | ||
body: createdComment.body, | ||
author: { | ||
username: createdComment.author.username, | ||
bio: createdComment.author.bio, | ||
image: createdComment.author.image, | ||
following: createdComment.author.followedBy.some((follow: any) => follow.id === auth.id), | ||
}, | ||
} | ||
}; | ||
}); |
51 changes: 51 additions & 0 deletions
51
apps/api/server/routes/api/articles/[slug]/favorite/index.delete.ts
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,51 @@ | ||
import profileMapper from "~/utils/profile.utils"; | ||
import {Tag} from "~/models/tag.model"; | ||
import {definePrivateEventHandler} from "~/auth-event-handler"; | ||
|
||
export default definePrivateEventHandler(async (event, {auth}) => { | ||
const slug = getRouterParam(event, "slug"); | ||
|
||
const { _count, ...article } = await usePrisma().article.update({ | ||
where: { | ||
slug, | ||
}, | ||
data: { | ||
favoritedBy: { | ||
disconnect: { | ||
id: auth.id, | ||
}, | ||
}, | ||
}, | ||
include: { | ||
tagList: { | ||
select: { | ||
name: true, | ||
}, | ||
}, | ||
author: { | ||
select: { | ||
username: true, | ||
bio: true, | ||
image: true, | ||
followedBy: true, | ||
}, | ||
}, | ||
favoritedBy: true, | ||
_count: { | ||
select: { | ||
favoritedBy: true, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const result = { | ||
...article, | ||
author: profileMapper(article.author, auth.id), | ||
tagList: article?.tagList.map((tag: Tag) => tag.name), | ||
favorited: article.favoritedBy.some((favorited: any) => favorited.id === auth.id), | ||
favoritesCount: _count?.favoritedBy, | ||
}; | ||
|
||
return {article: result}; | ||
}); |
Oops, something went wrong.