Skip to content

Commit

Permalink
Merge pull request #8 from fiit-tp7-2023/#287-prepare-api-endpoints
Browse files Browse the repository at this point in the history
AB#287 Prepare api endpoints
  • Loading branch information
Kesuera authored Mar 24, 2024
2 parents 1e9b4ab + c4829cd commit 734ea81
Show file tree
Hide file tree
Showing 30 changed files with 555 additions and 60 deletions.
9 changes: 0 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
# REST API configuration
REST_API_URL=

# Auth configuration - required in production
AUTH_ORIGIN=
AUTH_SECRET=

# Google configuration
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REFRESH_TOKEN_URL=

# .NET backend runs locally on HTTPS (must be set to 0), for production must be set to 1
NODE_TLS_REJECT_UNAUTHORIZED=
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
"Lato",
"nuxt",
"nuxtjs",
"ofetch",
"Raleway",
"Roboto",
"sidebase",
"tailwindcss",
"vueuc",
"wght"
"wght",
"dtos"
],
"files.eol": "\n",
"[typescript]": {
Expand Down
6 changes: 3 additions & 3 deletions components/posts/NftPost.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<p>{{ post.description }}</p>
</div>
<div class="mx-4 my-2 flex flex-wrap items-center gap-2">
<template v-for="attribute in post.nft.attributes">
<template v-for="attribute in post.nft.attributes" :key="attribute.traitType">
<p class="bg-slate-600 text-white px-2 py-1 rounded">{{ attribute.traitType }}</p>
</template>
<p class="underline text-gray-300">Show more</p>
Expand All @@ -25,9 +25,9 @@

<script setup lang="ts">
import { defineProps } from 'vue';
import type { NFTPost } from '~/types';
import type { NFTPost } from '~/types/dtos';
const props = defineProps<{
defineProps<{
post: NFTPost;
}>();
</script>
Expand Down
3 changes: 3 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
runtimeConfig: {
restApiUrl: process.env.REST_API_URL,
},
modules: [
'@nuxtjs/tailwindcss',
'nuxt-icon',
Expand Down
3 changes: 0 additions & 3 deletions server/api/auth.ts

This file was deleted.

8 changes: 8 additions & 0 deletions server/api/auth/login.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useAuthService } from '~/server/services/auth.service';
import { VerifyNonce } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { signature, address } = await readBody<VerifyNonce>(event);
const service = useAuthService();
return await service.verifyNonce(signature, address);
});
8 changes: 8 additions & 0 deletions server/api/auth/nonce.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useAuthService } from '~/server/services/auth.service';
import { GetNonceDTO } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { address } = await readBody<GetNonceDTO>(event);
const service = useAuthService();
return await service.getNonce(address);
});
10 changes: 10 additions & 0 deletions server/api/auth/refresh.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useAuthService } from '~/server/services/auth.service';
import { AuthenticatedUser, RefreshTokenRequestDTO } from '~/types/auth';

type Body = RefreshTokenRequestDTO & AuthenticatedUser;

export default defineEventHandler(async (event) => {
const { refreshToken: refToken, jwt } = await readBody<Body>(event);
const service = useAuthService(jwt);
return await service.refreshToken(refToken);
});
16 changes: 16 additions & 0 deletions server/api/comment/[id]/index.delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useCommentService } from '~/server/services/comment.service';
import { AuthenticatedUser } from '~/types/auth';

type Body = AuthenticatedUser;

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<Body>(event);
const commentId = getRouterParam(event, 'id');
if (Number.isNaN(commentId)) {
return createError({
message: 'Invalid comment id',
});
}
const service = useCommentService(jwt);
return await service.remove(Number(commentId));
});
17 changes: 17 additions & 0 deletions server/api/comment/[id]/index.put.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useCommentService } from '~/server/services/comment.service';
import { AuthenticatedUser } from '~/types/auth';
import { UpdateCommentDTO } from '~/types/dtos';

type Body = AuthenticatedUser & UpdateCommentDTO;

export default defineEventHandler(async (event) => {
const { jwt, content } = await readBody<Body>(event);
const commentId = getRouterParam(event, 'id');
if (Number.isNaN(commentId)) {
return createError({
message: 'Invalid comment id',
});
}
const service = useCommentService(jwt);
return await service.update(Number(commentId), content);
});
15 changes: 15 additions & 0 deletions server/api/comment/[id]/like/index.delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useCommentService } from '~/server/services/comment.service';
import { AuthenticatedUser } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<AuthenticatedUser>(event);
const commentId = getRouterParam(event, 'id');
if (Number.isNaN(commentId)) {
return createError({
message: 'Invalid comment id',
});
}

const service = useCommentService(jwt);
return await service.unlike(Number(commentId));
});
15 changes: 15 additions & 0 deletions server/api/comment/[id]/like/index.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useCommentService } from '~/server/services/comment.service';
import { AuthenticatedUser } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<AuthenticatedUser>(event);
const commentId = getRouterParam(event, 'id');
if (Number.isNaN(commentId)) {
return createError({
message: 'Invalid comment id',
});
}

const service = useCommentService(jwt);
return await service.getLikes(Number(commentId));
});
15 changes: 15 additions & 0 deletions server/api/comment/[id]/like/index.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useCommentService } from '~/server/services/comment.service';
import { AuthenticatedUser } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<AuthenticatedUser>(event);
const commentId = getRouterParam(event, 'id');
if (Number.isNaN(commentId)) {
return createError({
message: 'Invalid comment id',
});
}

const service = useCommentService(jwt);
return await service.like(Number(commentId));
});
11 changes: 11 additions & 0 deletions server/api/comment/index.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useCommentService } from '~/server/services/comment.service';
import { AuthenticatedUser } from '~/types/auth';
import { AddCommentDTO, NFTAddress } from '~/types/dtos';

type Body = AuthenticatedUser & AddCommentDTO & NFTAddress;

export default defineEventHandler(async (event) => {
const { jwt, content, parentCommentId, address } = await readBody<Body>(event);
const service = useCommentService(jwt);
return await service.add(address, content, parentCommentId);
});
15 changes: 15 additions & 0 deletions server/api/post/[id]/like/index.delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { usePostService } from '~/server/services/post.service';
import { AuthenticatedUser } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<AuthenticatedUser>(event);
const nftAddress = getRouterParam(event, 'id');
if (!nftAddress) {
return createError({
message: 'Invalid NFT address',
});
}

const service = usePostService(jwt);
return await service.unlike(nftAddress);
});
15 changes: 15 additions & 0 deletions server/api/post/[id]/like/index.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { usePostService } from '~/server/services/post.service';
import { AuthenticatedUser } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<AuthenticatedUser>(event);
const nftAddress = getRouterParam(event, 'id');
if (!nftAddress) {
return createError({
message: 'Invalid NFT address',
});
}

const service = usePostService(jwt);
return await service.getLikes(nftAddress);
});
15 changes: 15 additions & 0 deletions server/api/post/[id]/like/index.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { usePostService } from '~/server/services/post.service';
import { AuthenticatedUser } from '~/types/auth';

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<AuthenticatedUser>(event);
const nftAddress = getRouterParam(event, 'id');
if (!nftAddress) {
return createError({
message: 'Invalid NFT address',
});
}

const service = usePostService(jwt);
return await service.like(nftAddress);
});
11 changes: 11 additions & 0 deletions server/api/post/my.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { usePostService } from '~/server/services/post.service';
import { AuthenticatedUser } from '~/types/auth';
import { PaginationDTO } from '~/types/dtos';

export default defineEventHandler(async (event) => {
const { jwt } = await readBody<AuthenticatedUser>(event);
const { pageNumber, pageSize } = getQuery<PaginationDTO>(event);

const service = usePostService(jwt);
return await service.getMyPosts(pageNumber, pageSize);
});
55 changes: 55 additions & 0 deletions server/errors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ServerErrorType } from '~/types/error';

export class ServerError {
static notFound(_reason: string) {
return createError({
status: ServerErrorType.NOT_FOUND,
statusText: 'Not found',
statusMessage: _reason,
});
}

static unauthorized(_reason: string) {
return createError({
status: ServerErrorType.UNAUTHORIZED,
statusText: 'Unauthorized',
statusMessage: _reason,
});
}

static forbidden(_reason: string) {
return createError({
status: ServerErrorType.FORBIDDEN,
statusText: 'Forbidden',
statusMessage: _reason,
});
}

static internalServerError(_reason: string) {
return createError({
status: ServerErrorType.INTERNAL_SERVER_ERROR,
statusText: 'Internal server error',
statusMessage: _reason,
});
}

static unavailable() {
return createError({
status: ServerErrorType.UNAVAILABLE,
statusText: 'Service unavailable',
});
}

static fromCode(code: number, _reason: string) {
switch (code) {
case 401:
return ServerError.unauthorized(_reason);
case 403:
return ServerError.forbidden(_reason);
case 404:
return ServerError.notFound(_reason);
default:
return ServerError.internalServerError(_reason);
}
}
}
42 changes: 42 additions & 0 deletions server/services/auth.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useApi } from '../utils/api';
import { GetNonceDTO, RefreshTokenDTO, RefreshTokenRequestDTO, VerifyNonce } from '~/types/auth';

export function useAuthService(token?: string) {
const getNonce = async (address: string): Promise<string> =>
await useApi<string, GetNonceDTO>('auth/nonce-message', undefined, {
method: 'POST',
body: {
address,
},
});

const verifyNonce = async (signature: string, address: string): Promise<RefreshTokenDTO> => {
const response = await useApi<RefreshTokenDTO, VerifyNonce>('auth/login', undefined, {
method: 'POST',
body: {
signature,
address,
},
});
return response;
};

const refreshToken = async (refreshToken: string): Promise<RefreshTokenDTO> => {
if (!token) {
throw new Error('Missing user token');
}
const response = await useApi<RefreshTokenDTO, RefreshTokenRequestDTO>('auth/refresh', token, {
method: 'POST',
body: {
refreshToken,
},
});
return response;
};

return {
getNonce,
verifyNonce,
refreshToken,
};
}
Loading

0 comments on commit 734ea81

Please sign in to comment.