mirror of
https://github.com/r-freeman/portfolio.git
synced 2025-04-04 17:04:32 +00:00
Add comment notifications
All checks were successful
Build And Publish / BuildAndPublish (push) Successful in 3m12s
All checks were successful
Build And Publish / BuildAndPublish (push) Successful in 3m12s
This commit is contained in:
parent
400e199c15
commit
053a5d3768
@ -8,4 +8,9 @@ GITHUB_CLIENT_ID=
|
||||
GITHUB_SECRET=
|
||||
NEXT_PUBLIC_SUPABASE_URL=
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
||||
SUPABASE_SERVICE_ROLE_KEY=
|
||||
SUPABASE_SERVICE_ROLE_KEY=
|
||||
AUTH_SECRET=
|
||||
AUTH_TRUST_HOST=
|
||||
AUTH_REDIRECT_PROXY_URL=
|
||||
NTFY_URL=
|
||||
NTFY_TOKEN=
|
@ -3,65 +3,80 @@
|
||||
import {auth, signIn} from '@/auth'
|
||||
import {createClient} from '@/lib/supabase/server'
|
||||
import {z} from 'zod'
|
||||
import {sendNotification} from '@/lib/ntfy'
|
||||
|
||||
export async function loginWithGitHub() {
|
||||
await signIn('github')
|
||||
}
|
||||
|
||||
const notificationBody = (comment: any, user: any) => {
|
||||
return {
|
||||
topic: 'comments',
|
||||
message: `You have a new comment from ${user.name}:\n${comment.content}\n`,
|
||||
actions: [
|
||||
{
|
||||
action: 'http',
|
||||
label: 'Approve comment',
|
||||
url: `${process.env.NEXT_PUBLIC_SITE_URL}/api/comments/moderate/${comment.id}`,
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.NTFY_TOKEN}`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export async function addComment(prevState: { message: string }, formData: FormData) {
|
||||
const general_error = 'There was an error with your comment, please try again later.'
|
||||
const authorisation_error = 'Error, you must be logged in to post a comment.'
|
||||
const success_message = 'Your comment was submitted and is awaiting approval.'
|
||||
|
||||
const schema = z.object({
|
||||
comment: z.string().min(3).max(255),
|
||||
slug: z.string()
|
||||
})
|
||||
|
||||
const parse = schema.safeParse({
|
||||
const {comment, slug} = {
|
||||
comment: formData.get('comment'),
|
||||
slug: formData.get('slug')
|
||||
})
|
||||
|
||||
let message = ''
|
||||
if (!parse.success) {
|
||||
message = 'There was an error with your comment, please try again later.'
|
||||
return {message: message}
|
||||
}
|
||||
|
||||
const supabase = await createClient()
|
||||
const session = await auth()
|
||||
const slug = formData.get('slug')
|
||||
const content = formData.get('comment')
|
||||
const parse = schema.safeParse({comment, slug})
|
||||
if (!parse.success) {
|
||||
return {message: general_error}
|
||||
}
|
||||
|
||||
try {
|
||||
const supabase = await createClient()
|
||||
const session = await auth()
|
||||
|
||||
if (!session?.user) {
|
||||
return {message: authorisation_error}
|
||||
}
|
||||
|
||||
if (session?.user) {
|
||||
const {name, email, image} = session.user
|
||||
|
||||
const [{data: user}, {data: article}] = await Promise.all([
|
||||
supabase.from('users')
|
||||
.upsert({name, email, image}, {onConflict: 'email'})
|
||||
.select('id')
|
||||
.single(),
|
||||
supabase.from('articles')
|
||||
.select('id')
|
||||
.eq('slug', slug)
|
||||
.single()
|
||||
supabase.from('users').upsert({name, email, image}, {onConflict: 'email'}).select('*').single(),
|
||||
supabase.from('articles').select('id').eq('slug', slug).single()
|
||||
])
|
||||
|
||||
if (user?.id && article?.id) {
|
||||
const {data: comment} = await supabase
|
||||
.from('comments')
|
||||
.insert({content: content, article_id: article.id, user_id: user.id})
|
||||
.select('id')
|
||||
.single()
|
||||
const {data: newComment, error} = await supabase
|
||||
.from('comments')
|
||||
.insert({content: comment, article_id: article?.id, user_id: user?.id})
|
||||
.select('*')
|
||||
.single()
|
||||
|
||||
if (comment?.id === null) {
|
||||
message = 'There was an error with your comment, please try again later.'
|
||||
return {
|
||||
message: message
|
||||
}
|
||||
}
|
||||
if (error || newComment?.id === null) {
|
||||
return {message: general_error}
|
||||
}
|
||||
}
|
||||
|
||||
message = 'Your comment was posted successfully.'
|
||||
return {
|
||||
message
|
||||
await sendNotification(notificationBody(newComment, user))
|
||||
|
||||
return {message: success_message}
|
||||
} catch (error) {
|
||||
console.error('Error posting comment:', error)
|
||||
return {message: general_error}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ export async function GET(request: Request, {params}: { params: Promise<{ slug:
|
||||
article:articles!inner(id, slug)
|
||||
`)
|
||||
.eq('article.slug', slug)
|
||||
// .eq('published', 'true')
|
||||
.eq('published', true)
|
||||
.order('created_at', {ascending: false})
|
||||
|
||||
if (comments !== null && comments?.length > 0) {
|
||||
|
23
app/api/comments/moderate/[id]/route.ts
Normal file
23
app/api/comments/moderate/[id]/route.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {createClient} from '@/lib/supabase/server'
|
||||
import {headers} from 'next/headers'
|
||||
|
||||
export async function PATCH(request: Request, {params}: { params: Promise<{ id: number }> }) {
|
||||
const {id} = await params
|
||||
const headersList = await headers()
|
||||
const authorizationHeader = headersList.get('authorization')
|
||||
|
||||
if (authorizationHeader === `Bearer ${process.env.NTFY_TOKEN}`) {
|
||||
if (typeof id !== 'undefined') {
|
||||
const supabase = await createClient()
|
||||
|
||||
await supabase.from('comments')
|
||||
.update({published: true})
|
||||
.eq('id', id)
|
||||
|
||||
return new Response(null, {status: 204})
|
||||
}
|
||||
return new Response(JSON.stringify({status: 'Not Found'}), {status: 404})
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({status: 'Unauthorized'}), {status: 401})
|
||||
}
|
15
lib/ntfy.ts
Normal file
15
lib/ntfy.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import fetcher from '@/lib/fetcher'
|
||||
|
||||
const NTFY_URL = process.env.NTFY_URL || ''
|
||||
|
||||
export async function sendNotification(notificationBody: any) {
|
||||
if (NTFY_URL !== '') {
|
||||
await fetcher(NTFY_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(notificationBody)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user