diff --git a/.dockerignore b/.dockerignore index 89eb1d4..e01080f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,7 +9,7 @@ README.md certs/ docs/ notes/ - +docker/Dockerfile* # pass env vars through docker-compose # or mount .env* in dev .env* @@ -29,4 +29,11 @@ node_modules/ tests-e2e/node_modules/ # jest coverage -coverage/ \ No newline at end of file +coverage/ + +.git +.gitignore +.github +.gitattributes +READMETEMPLATE.md +README.md \ No newline at end of file diff --git a/.env.example b/.env.example index b544982..f89e0dd 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ # Application -APP_PORT=3000 +KAIZOKU_PORT=3000 # Database DATABASE_USER=kaizoku @@ -14,6 +14,6 @@ REDIS_HOST=localhost REDIS_PORT=6379 # Notification -TELEGRAM_TOKEN="***REMOVED***" -TELEGRAM_CHAT_ID="***REMOVED***" +TELEGRAM_TOKEN= +TELEGRAM_CHAT_ID= TELEGRAM_SEND_SILENTLY=0 \ No newline at end of file diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..6e74dd8 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,51 @@ +on: + push: + branches: + - main +name: release-please +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - name: Release + uses: google-github-actions/release-please-action@v3 + id: release + with: + command: manifest + + - name: Checkout + uses: actions/checkout@v3 + if: ${{ steps.release.outputs.release_created }} + + - name: Set up Docker Build + id: buildx + uses: docker/setup-buildx-action@v2 + if: ${{ steps.release.outputs.release_created }} + + - name: Login to Github Packages + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + if: ${{ steps.release.outputs.release_created }} + + - name: Login to Docker hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + if: ${{ steps.release.outputs.release_created }} + + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + file: docker/Dockerfile + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/kaizoku:latest + ${{ secrets.DOCKERHUB_USERNAME }}/kaizoku:v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} + ghcr.io/${{ github.repository }}:latest + ghcr.io/${{ github.repository }}:v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}.${{ steps.release.outputs.patch }} + if: ${{ steps.release.outputs.release_created }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..46b1b67 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.0" +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2be63ba..0000000 --- a/Dockerfile +++ /dev/null @@ -1,72 +0,0 @@ -##### DEPENDENCIES - -FROM node:18-alpine AS deps -RUN apk add --no-cache libc6-compat openssl -WORKDIR /app - -# Install Prisma Client - remove if not using Prisma - -COPY prisma ./ - -# Install dependencies based on the preferred package manager - -COPY package.json yarn.lock ./ - -RUN yarn --frozen-lockfile - -##### BUILDER - -FROM node:18-alpine AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . - -ENV NEXT_TELEMETRY_DISABLED 1 - -RUN yarn build - -##### RUNNER - -FROM node:18-alpine AS runner - -WORKDIR /tmp - -RUN wget https://github.com/metafates/mangal/releases/download/v3.12.0/mangal_3.12.0_Linux_x86_64.tar.gz && \ - tar xf mangal_3.12.0_Linux_x86_64.tar.gz && \ - mv mangal /usr/bin/mangal && \ - chmod +x /usr/bin/mangal - -WORKDIR /app - -ENV NODE_ENV production - -ENV NEXT_TELEMETRY_DISABLED 1 - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -COPY --from=builder --chown=nextjs:nodejs /app/next.config.mjs ./ -COPY --from=builder --chown=nextjs:nodejs /app/public ./public -COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json - -COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next -COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma -COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist -COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules -COPY --from=builder --chown=nextjs:nodejs /app/mangal /home/nextjs/.config/mangal/sources - -RUN mkdir /data -RUN chown -R nextjs:nodejs /app -RUN chown -R nextjs:nodejs /data - -USER nextjs -EXPOSE 3000 -ENV PORT 3000 -ENV MANGAL_METADATA_COMIC_INFO_XML=true -ENV MANGAL_METADATA_FETCH_ANILIST=true -ENV MANGAL_METADATA_SERIES_JSON=true -ENV MANGAL_FORMATS_USE=cbz -ENV MANGAL_DOWNLOADER_DOWNLOAD_COVER=true -ENV MANGAL_DOWNLOADER_REDOWNLOAD_EXISTING=true - -CMD ["yarn", "prod"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5eee3f6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Alperen Elhan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index c87e042..92b76d9 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,91 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +# Kaizoku Kaizoku -## Getting Started +Kaizoku is self-hosted manga downloader. -First, run the development server: +![Home Page](https://i.imgur.com/KT9LrtX.png) -```bash -npm run dev -# or -yarn dev -``` +| Detail Page | Search | +| :---------------------------------------------: | :----------------------------------------: | +| ![Detail Page](https://i.imgur.com/uWgZ9KA.png) | ![Search](https://i.imgur.com/XP4coVD.png) | -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +## Deployment -You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. +You can deploy Kaizoku with following docker-compose file -[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. +```yaml +version: '3' -The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. +volumes: + db: + redis: + +services: + app: + container_name: kaizoku + image: + environment: + - DATABASE_URL=postgresql://kaizoku:kaizoku@db:5432/kaizoku + - KAIZOKU_PORT=3000 + - REDIS_HOST=redis + - REDIS_HOST=6379 + - TELEGRAM_TOKEN= # Don't set if you don't want telegram notifications. + - TELEGRAM_CHAT_ID= + - TELEGRAM_SEND_SILENTLY=0 + - PUID= + - PGID= + - TZ=Europe/Istanbul + volumes: + - :/data + - :/config + - :/logs + depends_on: + db: + condition: service_healthy + ports: + - '3000:3000' + redis: + image: redis:7-alpine + volumes: + - redis:/data + db: + image: postgres:alpine + restart: unless-stopped + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U kaizoku'] + interval: 5s + timeout: 5s + retries: 5 + environment: + - POSTGRES_USER=kaizoku + - POSTGRES_DB=kaizoku + - POSTGRES_PASSWORD=kaizoku + volumes: + - db:/var/lib/postgresql/data +``` -## Learn More +## Development -To learn more about Next.js, take a look at the following resources: +### Requirements -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +- node 18 +- yarn +- docker +- [mangal](https://github.com/metafates/mangal) -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! +### Start the Kaizoku + +```bash +git clone https://github.com/oae/kaizoku.git +cd ./kaizoku/ +cp .env.example .env +yarn install +docker compose up -d redis db +yarn run prisma migrate deploy +yarn dev:server +``` -## Deploy on Vercel +Open [http://localhost:3000](http://localhost:3000) with your browser to see the page. -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +## Credits -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +Kaizoku uses amazing [mangal](https://github.com/metafates/mangal) by [@metafates](https://github.com/metafates) as it's downloader. diff --git a/docker-compose.yml b/docker-compose.yml index 22caa51..e2d288a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,23 +7,30 @@ volumes: services: app: container_name: kaizoku - build: . + build: + context: . + dockerfile: docker/Dockerfile environment: - DATABASE_URL: postgresql://${DATABASE_USER}:${DATABASE_PASSWORD}@db:${DATABASE_PORT}/${DATABASE_SCHEMA} - REDIS_HOST: redis + - DATABASE_URL=postgresql://${DATABASE_USER:-kaizoku}:${DATABASE_PASSWORD:-kaizoku}@db:${DATABASE_PORT:-5432}/${DATABASE_SCHEMA:-kaizoku} + - REDIS_HOST=redis + - PUID=1000 + - PGID=1000 + - TZ=Europe/Istanbul env_file: - .env volumes: - - /home/alperen/Manga:/data/manga + - ./kaizoku/data:/data + - ./kaizoku/config:/config + - ./kaizoku/logs:/logs depends_on: db: condition: service_healthy ports: - - "${APP_PORT}:3000" + - "${KAIZOKU_PORT:-3000}:3000" redis: image: redis:7-alpine ports: - - "${REDIS_PORT}:6379" + - "${REDIS_PORT:-6379}:6379" db: image: postgres:alpine restart: unless-stopped @@ -35,10 +42,10 @@ services: env_file: - .env environment: - POSTGRES_USER: ${DATABASE_USER} - POSTGRES_DB: ${DATABASE_SCHEMA} - POSTGRES_PASSWORD: ${DATABASE_PASSWORD} + - POSTGRES_USER=${DATABASE_USER:-kaizoku} + - POSTGRES_DB=${DATABASE_SCHEMA:-kaizoku} + - POSTGRES_PASSWORD=${DATABASE_PASSWORD:-kaizoku} volumes: - db:/var/lib/postgresql/data ports: - - "${DATABASE_PORT}:5432" + - "${DATABASE_PORT:-5432}:5432" diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..59bdc9e --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,65 @@ +FROM ghcr.io/linuxserver/baseimage-ubuntu:jammy as base + +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - +RUN apt install nodejs && npm i -g yarn + +##### DEPENDENCIES + +FROM base AS deps + +WORKDIR /app + +COPY prisma ./ +COPY package.json yarn.lock ./ + +RUN yarn --frozen-lockfile + +##### BUILDER + +FROM base AS builder + +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +ENV NEXT_TELEMETRY_DISABLED 1 + +RUN yarn build + +##### RUNNER + +FROM base as runner + +WORKDIR /tmp + +RUN curl -L https://github.com/metafates/mangal/releases/download/v3.12.0/mangal_3.12.0_Linux_x86_64.tar.gz -o mangal_3.12.0_Linux_x86_64.tar.gz && \ + tar xf mangal_3.12.0_Linux_x86_64.tar.gz && \ + mv mangal /usr/bin/mangal && \ + chmod +x /usr/bin/mangal + +WORKDIR /app + +ENV NODE_ENV production +ENV NEXT_TELEMETRY_DISABLED 1 +ENV HOME="/config" +ENV KAIZOKU_LOG_PATH="/logs" +ENV MANGAL_METADATA_COMIC_INFO_XML=true +ENV MANGAL_METADATA_FETCH_ANILIST=true +ENV MANGAL_METADATA_SERIES_JSON=true +ENV MANGAL_FORMATS_USE=cbz +ENV MANGAL_DOWNLOADER_DOWNLOAD_COVER=true +ENV MANGAL_DOWNLOADER_REDOWNLOAD_EXISTING=true + +COPY --from=builder /app/next.config.mjs ./ +COPY --from=builder /app/public ./public +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/node_modules ./node_modules + +COPY docker/mangal /mangal +COPY docker/root/ / + +VOLUME [ "/logs", "/config", "/data" ] \ No newline at end of file diff --git a/mangal/MangaDex.lua b/docker/mangal/sources/MangaDex.lua similarity index 100% rename from mangal/MangaDex.lua rename to docker/mangal/sources/MangaDex.lua diff --git a/mangal/Manganato.lua b/docker/mangal/sources/Manganato.lua similarity index 100% rename from mangal/Manganato.lua rename to docker/mangal/sources/Manganato.lua diff --git a/mangal/Mangasee.lua b/docker/mangal/sources/Mangasee.lua similarity index 100% rename from mangal/Mangasee.lua rename to docker/mangal/sources/Mangasee.lua diff --git a/docker/root/etc/cont-init.d/40-config b/docker/root/etc/cont-init.d/40-config new file mode 100644 index 0000000..b46fffd --- /dev/null +++ b/docker/root/etc/cont-init.d/40-config @@ -0,0 +1,16 @@ +#!/usr/bin/with-contenv bash + +cd /app + +/usr/bin/yarn run prisma migrate deploy + +mkdir -p /config/.config + +cp -r /mangal /config/.config + +# permissions +chown -R abc:abc \ + /app \ + /config \ + /logs \ + /data \ No newline at end of file diff --git a/docker/root/etc/services.d/kaizoku/run b/docker/root/etc/services.d/kaizoku/run new file mode 100644 index 0000000..54b1a9a --- /dev/null +++ b/docker/root/etc/services.d/kaizoku/run @@ -0,0 +1,10 @@ +#!/usr/bin/with-contenv bash + +cd /app + +shopt -s globstar + +umask 022 + +exec 2>&1 +exec s6-setuidgid abc /usr/bin/node dist/server/index.js \ No newline at end of file diff --git a/package.json b/package.json index 8ed13c3..394b35a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kaizoku", - "version": "0.1.0", + "version": "0.0.0", "author": "Alperen Elhan ", "private": true, "scripts": { @@ -12,8 +12,8 @@ "build:next": "next build", "build": "yarn run build:next && yarn run build:server", "build:analyze": "ANALYZE=true yarn run build:next && ANALYZE=false yarn run build:server", - "start": "next start", "lint": "next lint", + "start": "next start", "postinstall": "prisma generate" }, "commitlint": { @@ -70,7 +70,6 @@ "prop-types": "^15.8.1", "react": "18.2.0", "react-dom": "18.2.0", - "react-icons": "^4.6.0", "sharp": "^0.31.1", "string-to-color": "^2.2.2", "superjson": "^1.10.1", diff --git a/public/kaizoku.png b/public/kaizoku.png index b474c7b..0c268a5 100644 Binary files a/public/kaizoku.png and b/public/kaizoku.png differ diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..161d1ad --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,13 @@ +{ + "packages": { + ".": { + "changelog-path": "CHANGELOG.md", + "release-type": "node", + "bump-minor-pre-major": false, + "bump-patch-for-minor-pre-major": false, + "draft": false, + "prerelease": false + } + }, + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" +} \ No newline at end of file diff --git a/src/components/addLibrary.tsx b/src/components/addLibrary.tsx index 45ce012..54b952e 100644 --- a/src/components/addLibrary.tsx +++ b/src/components/addLibrary.tsx @@ -2,9 +2,8 @@ import { Box, Button, Code, LoadingOverlay, Text, TextInput } from '@mantine/cor import { useForm } from '@mantine/form'; import { useModals } from '@mantine/modals'; import { showNotification } from '@mantine/notifications'; -import { IconCheck, IconX } from '@tabler/icons'; +import { IconCheck, IconFolderPlus, IconX } from '@tabler/icons'; import { useState } from 'react'; -import { MdOutlineCreateNewFolder } from 'react-icons/md'; import { trpc } from '../utils/trpc'; function Form({ onClose }: { onClose: () => void }) { @@ -69,7 +68,7 @@ function Form({ onClose }: { onClose: () => void }) { })} > - + ({ @@ -116,7 +115,7 @@ export function AddLibrary({ onCreate }: { onCreate: () => void }) { }; return ( - ); diff --git a/src/components/emptyPrompt.tsx b/src/components/emptyPrompt.tsx index 9c86ba2..d13f25e 100644 --- a/src/components/emptyPrompt.tsx +++ b/src/components/emptyPrompt.tsx @@ -1,5 +1,5 @@ import { Center, createStyles, Stack, Text, Title } from '@mantine/core'; -import { BiBookContent } from 'react-icons/bi'; +import { IconBooks } from '@tabler/icons'; import { AddLibrary } from './addLibrary'; const useStyles = createStyles((theme) => ({ @@ -17,7 +17,7 @@ export function EmptyPrompt({ onCreate }: { onCreate: () => void }) { return (
- + No library found To be able to add new manga, you need to create a library diff --git a/src/components/madeWith.tsx b/src/components/madeWith.tsx index b2f2ae2..452327b 100644 --- a/src/components/madeWith.tsx +++ b/src/components/madeWith.tsx @@ -1,6 +1,6 @@ import { Box, Text, Tooltip } from '@mantine/core'; +import { IconHeart } from '@tabler/icons'; import { motion } from 'framer-motion'; -import { AiFillHeart } from 'react-icons/ai'; export function MadeWith({ minimized }: { minimized: boolean }): JSX.Element { const hearth = ( @@ -17,7 +17,7 @@ export function MadeWith({ minimized }: { minimized: boolean }): JSX.Element { repeatDelay: 0.5, }} > - + ); diff --git a/src/components/mangaDetail.tsx b/src/components/mangaDetail.tsx index c44eade..565f52f 100644 --- a/src/components/mangaDetail.tsx +++ b/src/components/mangaDetail.tsx @@ -1,9 +1,9 @@ import { Badge, createStyles, Divider, Grid, Group, Image, Spoiler, Text, Title } from '@mantine/core'; import { NextLink } from '@mantine/next'; import { Prisma } from '@prisma/client'; +import { IconExternalLink } from '@tabler/icons'; import { contrastColor } from 'contrast-color'; import stc from 'string-to-color'; -import { IconExternalLink } from '@tabler/icons'; const useStyles = createStyles((theme) => ({ root: { diff --git a/src/server/db/client.ts b/src/server/db/client.ts index 4de5b41..849aa3e 100644 --- a/src/server/db/client.ts +++ b/src/server/db/client.ts @@ -9,7 +9,7 @@ declare global { export const prisma = global.prisma || new PrismaClient({ - log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'], + log: ['query', 'error', 'warn'], }); if (process.env.NODE_ENV !== 'production') global.prisma = prisma; diff --git a/src/server/index.ts b/src/server/index.ts index 706b3b0..170d2ab 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -11,7 +11,7 @@ import { notificationQueue } from './queue/notify'; const dev = process.env.NODE_ENV !== 'production'; const app = next({ dev }); const handle = app.getRequestHandler(); -const port = process.env.APP_PORT || 3000; +const port = process.env.KAIZOKU_PORT || 3000; const serverAdapter = new ExpressAdapter(); serverAdapter.setBasePath('/bull/queues'); diff --git a/src/utils/logging.ts b/src/utils/logging.ts index ca3dbe2..0073e6b 100644 --- a/src/utils/logging.ts +++ b/src/utils/logging.ts @@ -7,7 +7,7 @@ export const logger = pino({ targets: [ { target: 'pino-pretty', - level: 'info', + level: 'debug', options: { levelFirst: false, destination: 1, @@ -19,7 +19,10 @@ export const logger = pino({ target: 'pino/file', level: 'debug', options: { - destination: path.resolve(process.cwd(), 'manup.log'), + destination: path.resolve( + process.cwd(), + path.relative(process.cwd(), path.resolve(process.env.KAIZOKU_LOG_PATH || '', 'manup.log')), + ), }, }, ], diff --git a/yarn.lock b/yarn.lock index f42d7c4..6c0bb72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4754,11 +4754,6 @@ react-dom@18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" -react-icons@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.6.0.tgz#f83eda179af5d02c047449a20b702c858653d397" - integrity sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g== - react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"