-
Notifications
You must be signed in to change notification settings - Fork 4
[feat] API 로직 구현 #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat] API 로직 구현 #19
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| VITE_API_ENDPOINT = https://bootcamp-api.codeit.kr/api/14-1/the-julge |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import type { InternalAxiosRequestConfig } from "axios"; | ||
|
|
||
| export const requestInterceptor = ( | ||
| config: InternalAxiosRequestConfig, | ||
| ): InternalAxiosRequestConfig => { | ||
| const token = localStorage.getItem("token") ?? ""; | ||
|
||
|
|
||
| if (token) { | ||
| config.headers.Authorization = `Bearer ${token}`; | ||
| } | ||
|
|
||
| return config; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import axios, { AxiosError, AxiosInstance } from "axios"; | ||
|
|
||
| import { requestInterceptor } from "./interceptors"; | ||
|
|
||
| const requestor: AxiosInstance = axios.create({ | ||
| baseURL: (import.meta.env.VITE_API_ENDPOINT as string).trim(), | ||
| timeout: 60000, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Accept: "application/json", | ||
| }, | ||
| }); | ||
|
|
||
| requestor.interceptors.request.use(requestInterceptor, (error: AxiosError) => | ||
| Promise.reject(error), | ||
|
||
| ); | ||
|
|
||
| export default requestor; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import type { AxiosResponse } from "axios"; | ||
| import { AlertListResponse, AlertResponse } from "src/types"; | ||
|
|
||
| import requestor from "../client/requestor"; | ||
|
|
||
| class AlertService { | ||
|
||
| getAlerts( | ||
| userId: string, | ||
| offset?: number, | ||
|
||
| limit?: number, | ||
| ): Promise<AxiosResponse<AlertListResponse>> { | ||
| return requestor.get(`/users/${userId}/alerts`, { | ||
| params: { offset, limit }, | ||
| }); | ||
| } | ||
|
|
||
| putAlert( | ||
| userId: string, | ||
| alertId: string, | ||
| ): Promise<AxiosResponse<AlertResponse>> { | ||
| return requestor.put(`/users/${userId}/alerts/${alertId}`); | ||
| } | ||
| } | ||
|
|
||
| export default new AlertService(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import type { AxiosResponse } from "axios"; | ||
| import { | ||
| ApplicationListResponse, | ||
| ApplicationResponse, | ||
| ApplicationStatus, | ||
| } from "src/types"; | ||
|
|
||
| import requestor from "../client/requestor"; | ||
|
|
||
| class ApplicationService { | ||
|
||
| getShopApplications( | ||
| shopId: string, | ||
| noticeId: string, | ||
| offset?: number, | ||
| limit?: number, | ||
| ): Promise<AxiosResponse<ApplicationListResponse>> { | ||
| return requestor.get(`/shops/${shopId}/notices/${noticeId}/applications`, { | ||
| params: { offset, limit }, | ||
| }); | ||
| } | ||
|
|
||
| postApplication( | ||
| shopId: string, | ||
| noticeId: string, | ||
| ): Promise<AxiosResponse<ApplicationResponse>> { | ||
| return requestor.post(`/shops/${shopId}/notices/${noticeId}/applications`); | ||
| } | ||
|
|
||
| putApplication( | ||
| shopId: string, | ||
| noticeId: string, | ||
| applicationId: string, | ||
| status: Exclude<ApplicationStatus, "pending">, | ||
| ): Promise<AxiosResponse<ApplicationResponse>> { | ||
| return requestor.put( | ||
| `/shops/${shopId}/notices/${noticeId}/applications/${applicationId}`, | ||
| { status }, | ||
| ); | ||
| } | ||
|
|
||
| getUserApplications( | ||
| userId: string, | ||
| offset?: number, | ||
| limit?: number, | ||
| ): Promise<AxiosResponse<ApplicationListResponse>> { | ||
| return requestor.get(`/users/${userId}/applications`, { | ||
| params: { offset, limit }, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| export default new ApplicationService(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import type { AxiosResponse } from "axios"; | ||
| import type { UserItem } from "src/types"; | ||
| import { ApiWrapper } from "src/types"; | ||
|
|
||
| import requestor from "../client/requestor"; | ||
|
|
||
| interface LoginItem { | ||
| token: string; | ||
| user: ApiWrapper<UserItem>; | ||
| } | ||
|
|
||
| export type LoginRequest = { email: string; password: string }; | ||
| export type LoginResponse = ApiWrapper<LoginItem>; | ||
|
|
||
| class AuthenticationService { | ||
|
||
| async postAuthentication( | ||
|
||
| payload: LoginRequest, | ||
| ): Promise<AxiosResponse<LoginResponse>> { | ||
| const res = await requestor.post<LoginResponse>("/token", payload); | ||
|
|
||
| const token = res.data.item.token; | ||
| const user = res.data.item.user.item; | ||
|
|
||
| localStorage.setItem("token", token); | ||
| localStorage.setItem("user", JSON.stringify(user)); | ||
|
|
||
| return res; | ||
| } | ||
|
|
||
| logout() { | ||
| localStorage.removeItem("token"); | ||
| localStorage.removeItem("user"); | ||
| } | ||
|
|
||
| getToken() { | ||
| return localStorage.getItem("token"); | ||
| } | ||
| isAuthenticated() { | ||
| return Boolean(this.getToken()); | ||
| } | ||
| getUser(): UserItem | null { | ||
| const raw = localStorage.getItem("user"); | ||
| return raw ? (JSON.parse(raw) as UserItem) : null; | ||
| } | ||
| } | ||
|
|
||
| export default new AuthenticationService(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import axios, { AxiosResponse } from "axios"; | ||
| import { ApiWrapper } from "src/types"; | ||
|
|
||
| import requestor from "../client/requestor"; | ||
|
|
||
| interface PresignedItem { | ||
| url: string; | ||
| } | ||
| type PresignedResponse = ApiWrapper<PresignedItem>; | ||
|
|
||
| class ImageService { | ||
| async postImage(name: string): Promise<string> { | ||
| const res: AxiosResponse<PresignedResponse> = await requestor.post( | ||
| "/images", | ||
| { name }, | ||
| ); | ||
| return res.data.item.url; | ||
| } | ||
|
|
||
| async putImage( | ||
| presignedURL: string, | ||
| file: File | Blob, | ||
| ): Promise<AxiosResponse<void>> { | ||
| return axios.put(presignedURL, file, { | ||
| headers: { "Content-Type": file.type || "application/octet-stream" }, | ||
| }); | ||
| } | ||
|
|
||
| getPublicURL(presignedURL: string) { | ||
| return presignedURL.split("?")[0]; | ||
| } | ||
| getImage(publicURL: string): Promise<AxiosResponse<Blob>> { | ||
| return axios.get(publicURL, { responseType: "blob" }); | ||
| } | ||
| } | ||
|
|
||
| export default new ImageService(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import type { AxiosResponse } from "axios"; | ||
| import { | ||
| NoticeListResponse, | ||
| NoticeResponse, | ||
| SortKey, | ||
| NoticePayload, | ||
| } from "src/types"; | ||
|
|
||
| import requestor from "../client/requestor"; | ||
|
|
||
| class NoticeService { | ||
| getNotices(params?: { | ||
|
||
| offset?: number; | ||
| limit?: number; | ||
| address?: string; | ||
| keyword?: string; | ||
| startsAtGte?: string; | ||
| hourlyPayGte?: number; | ||
| sort?: SortKey; | ||
| }): Promise<AxiosResponse<NoticeListResponse>> { | ||
| return requestor.get("/notices", { params }); | ||
| } | ||
|
|
||
| getShopNotices( | ||
| shopId: string, | ||
| offset?: number, | ||
| limit?: number, | ||
| ): Promise<AxiosResponse<NoticeListResponse>> { | ||
| return requestor.get(`/shops/${shopId}/notices`, { | ||
| params: { offset, limit }, | ||
| }); | ||
| } | ||
|
|
||
| postNotice( | ||
| shopId: string, | ||
| payload: NoticePayload, | ||
| ): Promise<AxiosResponse<NoticeResponse>> { | ||
| return requestor.post(`/shops/${shopId}/notices`, payload); | ||
| } | ||
|
|
||
| getNotice( | ||
| shopId: string, | ||
| noticeId: string, | ||
| ): Promise<AxiosResponse<NoticeResponse>> { | ||
| return requestor.get(`/shops/${shopId}/notices/${noticeId}`); | ||
| } | ||
|
|
||
| putNotice( | ||
| shopId: string, | ||
| noticeId: string, | ||
| payload: NoticePayload, | ||
| ): Promise<AxiosResponse<NoticeResponse>> { | ||
| return requestor.put(`/shops/${shopId}/notices/${noticeId}`, payload); | ||
| } | ||
| } | ||
|
|
||
| export default new NoticeService(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import type { AxiosResponse } from "axios"; | ||
| import { ShopPayload, ShopResponse } from "src/types"; | ||
|
|
||
| import requestor from "../client/requestor"; | ||
|
|
||
| class ShopService { | ||
| postShop(payload: ShopPayload): Promise<AxiosResponse<ShopResponse>> { | ||
| return requestor.post("/shops", payload); | ||
| } | ||
|
|
||
| getShop(shopId: string): Promise<AxiosResponse<ShopResponse>> { | ||
| return requestor.get(`/shops/${shopId}`); | ||
| } | ||
|
|
||
| putShop( | ||
| shopId: string, | ||
| payload: ShopPayload, | ||
| ): Promise<AxiosResponse<ShopResponse>> { | ||
| return requestor.put(`/shops/${shopId}`, payload); | ||
| } | ||
| } | ||
|
|
||
| export default new ShopService(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import type { AxiosResponse } from "axios"; | ||
| import { | ||
| SignupPayload, | ||
| SignupResponse, | ||
| UserResponse, | ||
| UpdateUserPayload, | ||
| } from "src/types"; | ||
|
|
||
| import requestor from "../client/requestor"; | ||
|
|
||
| class UserService { | ||
| postUser(payload: SignupPayload): Promise<AxiosResponse<SignupResponse>> { | ||
| return requestor.post("/users", payload); | ||
| } | ||
|
|
||
| getUser(userId: string): Promise<AxiosResponse<UserResponse>> { | ||
| return requestor.get(`/users/${userId}`); | ||
| } | ||
|
|
||
| putUser( | ||
| userId: string, | ||
| payload: UpdateUserPayload, | ||
| ): Promise<AxiosResponse<UserResponse>> { | ||
| return requestor.put(`/users/${userId}`, payload); | ||
| } | ||
| } | ||
|
|
||
| export default new UserService(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import type { ApplicationStatus } from "./application"; | ||
| import type { ApiWrapper, ApiPaged } from "./common"; | ||
| import type { NoticeSummary } from "./notice"; | ||
| import type { ShopSummary } from "./shop"; | ||
|
|
||
| export type AlertResult = "accepted" | "rejected"; | ||
|
|
||
| export interface ApplicationMini { | ||
| id: string; | ||
| status: Exclude<ApplicationStatus, "canceled">; | ||
| } | ||
|
|
||
| export interface AlertItem { | ||
| id: string; | ||
| createdAt: string; | ||
| result: AlertResult; | ||
| read: boolean; | ||
| application: ApiWrapper<ApplicationMini>; | ||
| shop: ApiWrapper<ShopSummary>; | ||
| notice: ApiWrapper<NoticeSummary>; | ||
|
||
| } | ||
|
|
||
| export type AlertResponse = ApiWrapper<AlertItem>; | ||
| export type AlertListResponse = ApiPaged<AlertItem>; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import type { ApiWrapper, ApiPaged } from "./common"; | ||
| import type { NoticeSummary } from "./notice"; | ||
| import type { ShopSummary } from "./shop"; | ||
| import type { UserSummary } from "./user"; | ||
|
|
||
| export type ApplicationStatus = | ||
| | "pending" | ||
| | "accepted" | ||
| | "rejected" | ||
| | "canceled"; | ||
|
|
||
| export interface ApplicationItem { | ||
| id: string; | ||
| status: ApplicationStatus; | ||
| createdAt: string; | ||
| user: ApiWrapper<UserSummary>; | ||
| shop: ApiWrapper<ShopSummary>; | ||
| notice: ApiWrapper<NoticeSummary>; | ||
| } | ||
|
|
||
| export type ApplicationResponse = ApiWrapper<ApplicationItem>; | ||
| export type ApplicationListResponse = ApiPaged<ApplicationItem>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.env파일은 GitHub 레포지토리에 포함되지 않아야 합니다 🥲노션이나 디스코드를 통해 파일로 따로 공유해주시는 것이 좋을 것 같아요.