diff --git a/.gitignore b/.gitignore index 7332353..f23ba7c 100644 --- a/.gitignore +++ b/.gitignore @@ -142,7 +142,7 @@ yarn-error.log* .pnpm-debug.log* # local env files -.env*.local +.env.development # vercel .vercel @@ -226,7 +226,7 @@ web_modules/ .env.development.local .env.test.local .env.production.local -.env.local +.env.development # parcel-bundler cache (https://parceljs.org/) .cache diff --git a/.woodpecker/pipeline.yaml b/.woodpecker/pipeline.yaml index c3422aa..6d1bfb1 100644 --- a/.woodpecker/pipeline.yaml +++ b/.woodpecker/pipeline.yaml @@ -1,30 +1,61 @@ -# .woodpecker.yml +# .pipeline.yml +# ----------------- -# Run pipeline on pushes to main and pull requests when: - event: [push, pull_request] - branch: [main, develop, feat/*, feature/*] + event: [ push ] -# Define environment variables -environment: - NODE_ENV: production - -# Pipeline steps steps: - # Install dependencies - - name: install - image: node:18-alpine - commands: - - echo "🔧 Installing dependencies..." - - npm ci --silent + - name: publish-image + image: woodpeckerci/plugin-docker-buildx + settings: + platforms: linux/amd64 + repo: git.badger-pirarucu.ts.net/mangamochi/frontend + registry: git.badger-pirarucu.ts.net + dockerfile: Dockerfile + context: . + username: + from_secret: DOCKER_USER + password: + from_secret: DOCKER_PASSWORD + build_args: + NEXT_PUBLIC_API_BASE_URL: + from_secret: NEXT_PUBLIC_API_BASE_URL + NEXT_PUBLIC_OMV_BASE_URL: + from_secret: NEXT_PUBLIC_OMV_BASE_URL + tags: + - latest + - ${CI_COMMIT_SHA} when: - event: [push, pull_request] + event: [ push ] + branch: [ main ] - # Build application - - name: build - image: node:18-alpine + - name: deploy + depends_on: [ publish-image ] + image: alpine:3.20 + environment: + DEPLOY_USER: rov + DEPLOY_HOST: mangamochi.badger-pirarucu.ts.net + DEPLOY_PORT: 22 + IMAGE: git.badger-pirarucu.ts.net/mangamochi/frontend:${CI_COMMIT_SHA} + DEPLOY_SSH_KEY: + from_secret: DEPLOY_SSH_KEY commands: - - echo "🏗️ Building application..." - - npm run build + - echo "🚀 Deploying Next.js app to $DEPLOY_HOST...." + - apk add --no-cache openssh-client docker-cli + - mkdir -p ~/.ssh + - echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - ssh-keyscan -p $DEPLOY_PORT $DEPLOY_HOST >> ~/.ssh/known_hosts + - > + ssh -p $DEPLOY_PORT $DEPLOY_USER@$DEPLOY_HOST " + docker pull $IMAGE && + docker stop mangamochi-frontend 2>/dev/null || true && + docker rm mangamochi-frontend 2>/dev/null || true && + docker run -d --name mangamochi-frontend \ + --restart always \ + -p 80:3000 \ + $IMAGE + " when: - event: [push, pull_request] + event: [ push ] + branch: [ main ] diff --git a/Dockerfile b/Dockerfile index 92bccdc..4f6c7e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,12 @@ RUN npm ci COPY . . # Build the application +ARG NEXT_PUBLIC_API_BASE_URL +ENV NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL} + +ARG NEXT_PUBLIC_OMV_BASE_URL +ENV NEXT_PUBLIC_OMV_BASE_URL=${NEXT_PUBLIC_OMV_BASE_URL} + RUN npm run build # Stage 2: Production stage diff --git a/api/api.ts b/api/api.ts index 33ba9e9..8fefde1 100644 --- a/api/api.ts +++ b/api/api.ts @@ -3,7 +3,7 @@ import { User } from "@/contexts/auth-context"; import { toast } from "sonner"; export const Api = axios.create({ - baseURL: "http://localhost:8080", + baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, responseType: "json", }); diff --git a/api/mangamochi.ts b/api/mangamochi.ts index 2277b67..0372a64 100644 --- a/api/mangamochi.ts +++ b/api/mangamochi.ts @@ -109,18 +109,18 @@ export interface PageMangaListDTO { content?: MangaListDTO[]; number?: number; pageable?: PageableObject; - sort?: SortObject; first?: boolean; last?: boolean; + sort?: SortObject; numberOfElements?: number; empty?: boolean; } export interface PageableObject { offset?: number; + paged?: boolean; pageNumber?: number; pageSize?: number; - paged?: boolean; sort?: SortObject; unpaged?: boolean; } @@ -279,7 +279,7 @@ export const sendRecord = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/records`, method: 'POST', + {url: `/records`, method: 'POST', headers: {'Content-Type': 'application/json', }, data: updateMangaDataCommand, signal }, @@ -342,7 +342,7 @@ export const fetchMangaChapters = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${encodeURIComponent(String(mangaProviderId))}/fetch-chapters`, method: 'POST', signal + {url: `/mangas/${encodeURIComponent(String(mangaProviderId))}/fetch-chapters`, method: 'POST', signal }, options); } @@ -406,7 +406,7 @@ export const fetchAllChapters = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${encodeURIComponent(String(mangaProviderId))}/fetch-all-chapters`, method: 'POST', signal + {url: `/mangas/${encodeURIComponent(String(mangaProviderId))}/fetch-all-chapters`, method: 'POST', signal }, options); } @@ -470,7 +470,7 @@ export const setUnfavorite = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${encodeURIComponent(String(id))}/unfavorite`, method: 'POST', signal + {url: `/mangas/${encodeURIComponent(String(id))}/unfavorite`, method: 'POST', signal }, options); } @@ -534,7 +534,7 @@ export const setFavorite = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${encodeURIComponent(String(id))}/favorite`, method: 'POST', signal + {url: `/mangas/${encodeURIComponent(String(id))}/favorite`, method: 'POST', signal }, options); } @@ -598,7 +598,7 @@ export const markAsRead = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/chapters/${encodeURIComponent(String(chapterId))}/mark-as-read`, method: 'POST', signal + {url: `/mangas/chapters/${encodeURIComponent(String(chapterId))}/mark-as-read`, method: 'POST', signal }, options); } @@ -662,7 +662,7 @@ export const fetchChapter = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/chapters/${encodeURIComponent(String(chapterId))}/fetch`, method: 'POST', signal + {url: `/mangas/chapters/${encodeURIComponent(String(chapterId))}/fetch`, method: 'POST', signal }, options); } @@ -727,7 +727,7 @@ export const downloadChapterArchive = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/chapters/${encodeURIComponent(String(chapterId))}/download`, method: 'POST', + {url: `/mangas/chapters/${encodeURIComponent(String(chapterId))}/download`, method: 'POST', params, responseType: 'blob', signal }, @@ -796,7 +796,7 @@ formData.append(`malId`, importMultipleFilesBody.malId) importMultipleFilesBody.files.forEach(value => formData.append(`files`, value)); return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/manga/import/upload`, method: 'POST', + {url: `/manga/import/upload`, method: 'POST', headers: {'Content-Type': 'multipart/form-data', }, data: formData, signal }, @@ -862,7 +862,7 @@ export const getImportReviews = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/manga/import/review`, method: 'GET', signal + {url: `/manga/import/review`, method: 'GET', signal }, options); } @@ -872,7 +872,7 @@ export const getImportReviews = ( export const getGetImportReviewsQueryKey = () => { return [ - `http://mangamochi.badger-pirarucu.ts.net:8080/manga/import/review` + `/manga/import/review` ] as const; } @@ -955,7 +955,7 @@ export const resolveImportReview = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/manga/import/review`, method: 'POST', + {url: `/manga/import/review`, method: 'POST', params, signal }, options); @@ -1020,7 +1020,7 @@ export const importFromMangaDex = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/manga/import/manga-dex`, method: 'POST', + {url: `/manga/import/manga-dex`, method: 'POST', headers: {'Content-Type': 'application/json', }, data: importMangaDexRequestDTO, signal }, @@ -1086,7 +1086,7 @@ export const registerUser = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/auth/register`, method: 'POST', + {url: `/auth/register`, method: 'POST', headers: {'Content-Type': 'application/json', }, data: registrationRequestDTO, signal }, @@ -1152,7 +1152,7 @@ export const authenticateUser = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/auth/login`, method: 'POST', + {url: `/auth/login`, method: 'POST', headers: {'Content-Type': 'application/json', }, data: authenticationRequestDTO, signal }, @@ -1218,7 +1218,7 @@ export const getMangas = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas`, method: 'GET', + {url: `/mangas`, method: 'GET', params, signal }, options); @@ -1229,7 +1229,7 @@ export const getMangas = ( export const getGetMangasQueryKey = (params?: GetMangasParams,) => { return [ - `http://mangamochi.badger-pirarucu.ts.net:8080/mangas`, ...(params ? [params]: []) + `/mangas`, ...(params ? [params]: []) ] as const; } @@ -1312,7 +1312,7 @@ export const getMangaChapters = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${encodeURIComponent(String(mangaProviderId))}/chapters`, method: 'GET', signal + {url: `/mangas/${encodeURIComponent(String(mangaProviderId))}/chapters`, method: 'GET', signal }, options); } @@ -1322,7 +1322,7 @@ export const getMangaChapters = ( export const getGetMangaChaptersQueryKey = (mangaProviderId?: number,) => { return [ - `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${mangaProviderId}/chapters` + `/mangas/${mangaProviderId}/chapters` ] as const; } @@ -1405,7 +1405,7 @@ export const getManga = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${encodeURIComponent(String(mangaId))}`, method: 'GET', signal + {url: `/mangas/${encodeURIComponent(String(mangaId))}`, method: 'GET', signal }, options); } @@ -1415,7 +1415,7 @@ export const getManga = ( export const getGetMangaQueryKey = (mangaId?: number,) => { return [ - `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/${mangaId}` + `/mangas/${mangaId}` ] as const; } @@ -1498,7 +1498,7 @@ export const getMangaChapterImages = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/chapters/${encodeURIComponent(String(chapterId))}/images`, method: 'GET', signal + {url: `/mangas/chapters/${encodeURIComponent(String(chapterId))}/images`, method: 'GET', signal }, options); } @@ -1508,7 +1508,7 @@ export const getMangaChapterImages = ( export const getGetMangaChapterImagesQueryKey = (chapterId?: number,) => { return [ - `http://mangamochi.badger-pirarucu.ts.net:8080/mangas/chapters/${chapterId}/images` + `/mangas/chapters/${chapterId}/images` ] as const; } @@ -1591,7 +1591,7 @@ export const getGenres = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/genres`, method: 'GET', signal + {url: `/genres`, method: 'GET', signal }, options); } @@ -1601,7 +1601,7 @@ export const getGenres = ( export const getGetGenresQueryKey = () => { return [ - `http://mangamochi.badger-pirarucu.ts.net:8080/genres` + `/genres` ] as const; } @@ -1683,7 +1683,7 @@ export const deleteImportReview = ( return customInstance( - {url: `http://mangamochi.badger-pirarucu.ts.net:8080/manga/import/review/${encodeURIComponent(String(id))}`, method: 'DELETE' + {url: `/manga/import/review/${encodeURIComponent(String(id))}`, method: 'DELETE' }, options); } diff --git a/app/manga/[id]/chapter/[chapterNumber]/page.tsx b/app/manga/[id]/chapter/[chapterNumber]/page.tsx index 1f5a3ab..79e4285 100644 --- a/app/manga/[id]/chapter/[chapterNumber]/page.tsx +++ b/app/manga/[id]/chapter/[chapterNumber]/page.tsx @@ -142,8 +142,7 @@ export default function ChapterReaderPage() { {/* Manga Page */}