Skip to content

Commit

Permalink
feat(api): comments on blog post (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jayllyz authored Apr 13, 2024
1 parent 5e1856a commit a274127
Show file tree
Hide file tree
Showing 16 changed files with 511 additions and 119 deletions.
13 changes: 10 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ updates:
open-pull-requests-limit: 10
versioning-strategy: 'increase'
- package-ecosystem: 'docker'
directory: '/'
directory: '/apps/client'
schedule:
interval: 'weekly'
open-pull-requests-limit: 10
interval: 'monthly'
- package-ecosystem: 'docker'
directory: '/apps/admin'
schedule:
interval: 'monthly'
- package-ecosystem: 'docker'
directory: '/apps/api'
schedule:
interval: 'monthly'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
Expand Down
4 changes: 2 additions & 2 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
},
"dependencies": {
"@repo/ui": "workspace:*",
"next": "^14.2.0",
"next": "^14.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@repo/biome-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/node": "^20.12.7",
"@types/react": "^18.2.77",
"@types/react": "^18.2.78",
"@types/react-dom": "^18.2.25",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
Expand Down
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@hono/zod-validator": "^0.2.1",
"@repo/types": "workspace:*",
"@supabase/supabase-js": "^2.42.3",
"hono": "^4.2.3",
"hono": "^4.2.4",
"zod": "^3.22.4",
"zod-validation-error": "^3.1.0"
},
Expand Down
30 changes: 28 additions & 2 deletions apps/api/src/handlers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ export const auth = new OpenAPIHono({
});

auth.openapi(signupUser, async (c) => {
const { email, password } = c.req.valid('json');
const { email, password, first_name, last_name, username } = c.req.valid('json');

const { data: userExist } = await supabase.from('USERS').select('id').eq('email', email);
if (userExist && userExist.length > 0) {
return c.json({ message: 'Email already exists' }, 400);
}

const { data, error } = await supabase.auth.signUp({
email,
Expand All @@ -23,7 +28,28 @@ auth.openapi(signupUser, async (c) => {
cause: error,
});
}
return c.json({ message: 'User created successfully' });

const { data: user, error: insertError } = await supabase
.from('USERS')
.insert({
email: data.user.email,
username: username || '',
first_name: first_name || '',
last_name: last_name || '',
id_referer: null,
id_role: 1,
id_auth: data.user.id,
})
.select()
.single();

if (insertError) {
throw new Error('Error while creating user', {
cause: insertError,
});
}

return c.json(user, 200);
});

auth.openapi(loginUser, async (c) => {
Expand Down
154 changes: 132 additions & 22 deletions apps/api/src/handlers/blog.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
import { OpenAPIHono } from '@hono/zod-openapi';
import { supabase } from '../libs/supabase.js';
import { zodErrorHook } from '../libs/zodError.js';
import { createPost, deletePost, getAllPosts, getPost, updatePost } from '../routes/blog.js';

export const blog = new OpenAPIHono({
import {
commentOnPost,
createPost,
createResponse,
deleteComment,
deletePost,
getAllPosts,
getComments,
getPost,
updateComment,
updatePost,
} from '../routes/blog.js';

type Variables = {
user: {
id: number;
};
};

export const blog = new OpenAPIHono<{ Variables: Variables }>({
defaultHook: zodErrorHook,
});

blog.openapi(getAllPosts, async (c) => {
const { data, error } = await supabase.from('POSTS').select('*');

if (error) {
return c.json({ message: error.message }, 500);
return c.json({ error: error.message }, 500);
}

return c.json(data, 200);
Expand All @@ -21,22 +38,35 @@ blog.openapi(getPost, async (c) => {
const { id } = c.req.valid('param');
const { data, error } = await supabase.from('POSTS').select('*').eq('id', id).single();

if (error) {
return c.json({ message: error.message }, 500);
}
if (!data) {
return c.json({ message: 'Post not found' }, 404);
if (error || !data) {
return c.json({ error: 'Post not found' }, 404);
}

return c.json(data, 200);
});

blog.openapi(createPost, async (c) => {
const { title, content, id_user } = c.req.valid('json');
const { data, error } = await supabase.from('POSTS').insert({ title, content, id_user }).select().single();
const { title, content } = c.req.valid('json');
const user = c.get('user')?.id;

if (error) {
return c.json({ message: error.message }, 500);
if (!user) {
return c.json({ error: 'User not found' }, 404);
}

const { data: idUser } = await supabase.from('USERS').select('id').eq('id_auth', user).single();

if (!idUser) {
return c.json({ error: 'User not found' }, 404);
}

const { data, error } = await supabase
.from('POSTS')
.insert({ title, content, id_user: idUser?.id })
.select()
.single();

if (error || !data) {
return c.json({ error: error?.message || 'Failed to create post' }, 500);
}

return c.json(data, 201);
Expand All @@ -47,11 +77,8 @@ blog.openapi(updatePost, async (c) => {
const { title, content } = c.req.valid('json');
const { data, error } = await supabase.from('POSTS').update({ title, content }).eq('id', id).select().single();

if (error) {
return c.json({ message: error.message }, 500);
}
if (!data) {
return c.json({ message: 'Post not found' }, 404);
if (error || !data) {
return c.json({ error: 'Post not found' }, 404);
}

return c.json(data, 200);
Expand All @@ -61,12 +88,95 @@ blog.openapi(deletePost, async (c) => {
const { id } = c.req.valid('param');
const { error, count } = await supabase.from('POSTS').delete({ count: 'exact' }).eq('id', id);

if (error || count === 0) {
return c.json({ error: 'Post not found' }, 404);
}

return c.json({ message: 'Post deleted successfully' }, 200);
});

blog.openapi(commentOnPost, async (c) => {
const { id } = c.req.valid('param');
const { content } = c.req.valid('json');
const user = c.get('user')?.id;

if (!user) {
return c.json({ error: 'User not found' }, 404);
}

const { data, error } = await supabase
.from('COMMENTS')
.insert({ content, id_post: id, id_user: user })
.select()
.single();

if (error || !data) {
return c.json({ error: 'Failed to create comment' }, 400);
}

return c.json(data, 201);
});

blog.openapi(getComments, async (c) => {
const { id } = c.req.valid('param');
const { data, error } = await supabase.from('COMMENTS').select('*').eq('id_post', id);

if (error) {
return c.json({ message: error.message }, 500);
return c.json({ error: error.message }, 500);
}
if (count === 0) {
return c.json({ message: 'Post not found' }, 404);

return c.json(data, 200);
});

blog.openapi(createResponse, async (c) => {
const { id_post, id_comment } = c.req.valid('param');
const { content } = c.req.valid('json');
const user = c.get('user')?.id;

if (!user) {
return c.json({ error: 'User not found' }, 404);
}

return c.json({ message: 'Post deleted successfully' }, 200);
const { data, error } = await supabase
.from('COMMENTS')
.insert({ content, id_post, id_response: id_comment, id_user: user })
.select()
.single();

if (error || !data) {
return c.json({ error: 'Failed to create response' }, 400);
}

return c.json(data, 201);
});

blog.openapi(updateComment, async (c) => {
const { id_post, id_comment } = c.req.valid('param');
const { content } = c.req.valid('json');
const { data, error } = await supabase
.from('COMMENTS')
.update({ content })
.eq('id, id_post', [id_comment, id_post])
.select()
.single();

if (error || !data) {
return c.json({ error: 'Comment not found' }, 404);
}

return c.json(data, 200);
});

blog.openapi(deleteComment, async (c) => {
const { id_post, id_comment } = c.req.valid('param');
const { error, count } = await supabase
.from('COMMENTS')
.delete({ count: 'exact' })
.eq('id, id_post', [id_comment, id_post]);

if (error || count === 0) {
return c.json({ error: 'Comment not found' }, 404);
}

return c.json({ message: 'Comment deleted successfully' }, 200);
});
20 changes: 15 additions & 5 deletions apps/api/src/middlewares/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ const authMiddleware: MiddlewareHandler = async (c, next) => {
if (!access_token) throw new HTTPException(403, { message: 'No access token' });
const { data, error } = await supabase.auth.getUser(access_token);

if (data.user) {
if (data?.user) {
const { data: idUser } = await supabase.from('USERS').select('id').eq('id_auth', data.user.id).single();

if (!idUser) throw new HTTPException(404, { message: 'User not found' });

c.set('user', {
id: data.user.id,
id: idUser?.id,
email: data.user.email,
created_at: data.user.created_at,
updated_at: data.user.updated_at,
created_at: data.user.created_at,
});
}

if (error) {
const refresh_token = getCookie(c, 'refresh_token');
if (!refresh_token) throw new HTTPException(403, { message: 'No refresh token' });
Expand All @@ -25,13 +30,18 @@ const authMiddleware: MiddlewareHandler = async (c, next) => {
});

if (refreshError) throw new HTTPException(403, { message: 'Error while refreshing token' });
if (!refreshed.user) throw new HTTPException(403, { message: 'No user found' });

const { data: idUser } = await supabase.from('USERS').select('id').eq('id_auth', refreshed.user.id).single();

if (!idUser) throw new HTTPException(404, { message: 'User not found' });

if (refreshed.user) {
c.set('user', {
id: refreshed.user.id,
id: idUser?.id,
email: refreshed.user.email,
created_at: refreshed.user.created_at,
updated_at: refreshed.user.updated_at,
created_at: refreshed.user.created_at,
});
}
}
Expand Down
Loading

0 comments on commit a274127

Please sign in to comment.