Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.

Commit 5743227

Browse files
committed
feat: dockerize application
1 parent 155a367 commit 5743227

12 files changed

+473
-409
lines changed

.env.example

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Application
2+
APP_PORT=3000
3+
4+
# Database
5+
DATABASE_USER=kaizoku
6+
DATABASE_SCHEMA=kaizoku
7+
DATABASE_PASSWORD=kaizoku
8+
DATABASE_PORT=5432
9+
DATABASE_HOST=localhost
10+
DATABASE_URL=postgresql://${DATABASE_USER}:${DATABASE_PASSWORD}@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_SCHEMA}
11+
12+
# Redis
13+
REDIS_HOST=localhost
14+
REDIS_PORT=6379
15+
16+
# Notification
17+
TELEGRAM_TOKEN="***REMOVED***"
18+
TELEGRAM_CHAT_ID="***REMOVED***"
19+
TELEGRAM_SEND_SILENTLY=0

Dockerfile

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
##### DEPENDENCIES
2+
3+
FROM node:18-alpine AS deps
4+
RUN apk add --no-cache libc6-compat openssl
5+
WORKDIR /app
6+
7+
# Install Prisma Client - remove if not using Prisma
8+
9+
COPY prisma ./
10+
11+
# Install dependencies based on the preferred package manager
12+
13+
COPY package.json yarn.lock ./
14+
15+
RUN yarn --frozen-lockfile
16+
17+
##### BUILDER
18+
19+
FROM node:18-alpine AS builder
20+
WORKDIR /app
21+
COPY --from=deps /app/node_modules ./node_modules
22+
COPY . .
23+
24+
ENV NEXT_TELEMETRY_DISABLED 1
25+
26+
RUN yarn build
27+
28+
##### RUNNER
29+
30+
FROM node:18-alpine AS runner
31+
WORKDIR /app
32+
33+
ENV NODE_ENV production
34+
35+
ENV NEXT_TELEMETRY_DISABLED 1
36+
37+
RUN addgroup --system --gid 1001 nodejs
38+
RUN adduser --system --uid 1001 nextjs
39+
40+
COPY --from=builder /app/next.config.mjs ./
41+
COPY --from=builder /app/public ./public
42+
COPY --from=builder /app/package.json ./package.json
43+
44+
COPY --from=builder /app/.next ./.next
45+
COPY --from=builder /app/prisma ./prisma
46+
COPY --from=builder /app/dist ./dist
47+
COPY --from=builder /app/node_modules ./node_modules
48+
49+
RUN chown -R nextjs:nodejs /app
50+
51+
USER nextjs
52+
EXPOSE 3000
53+
ENV PORT 3000
54+
55+
CMD ["yarn", "prod"]

docker-compose.yml

+14-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,23 @@ volumes:
55

66

77
services:
8+
app:
9+
container_name: kaizoku
10+
build: .
11+
environment:
12+
DATABASE_URL: postgresql://${DATABASE_USER}:${DATABASE_PASSWORD}@db:${DATABASE_PORT}/${DATABASE_SCHEMA}
13+
REDIS_HOST: redis
14+
env_file:
15+
- .env
16+
depends_on:
17+
db:
18+
condition: service_healthy
19+
ports:
20+
- "${APP_PORT}:3000"
821
redis:
922
image: redis:7-alpine
1023
ports:
11-
- 6379:6379
24+
- "${REDIS_PORT}:6379"
1225
db:
1326
image: postgres:alpine
1427
restart: unless-stopped

next.config.mjs

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import withBundleAnalyzer from '@next/bundle-analyzer';
2-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
3-
import { env } from './src/env/server.mjs';
42

53
const bundleAnalyzer = withBundleAnalyzer({
64
enabled: process.env.ANALYZE === 'true',

package.json

+34-32
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dev:server": "NODE_ENV=development ts-node --project tsconfig.server.json src/server/index.ts",
88
"build:server": "tsc --project tsconfig.server.json",
99
"start:server": "NODE_ENV=production node dist/server/index.js",
10+
"prod": "prisma migrate deploy && yarn run start:server",
1011
"dev": "next dev",
1112
"build:next": "next build",
1213
"build": "yarn run build:next && yarn run build:server",
@@ -32,51 +33,52 @@
3233
]
3334
},
3435
"dependencies": {
35-
"@bull-board/api": "^4.3.2",
36-
"@bull-board/express": "^4.3.2",
36+
"@bull-board/api": "^4.6.0",
37+
"@bull-board/express": "^4.6.0",
3738
"@emotion/react": "^11.10.4",
3839
"@emotion/server": "^11.10.0",
39-
"@mantine/core": "^5.5.4",
40-
"@mantine/form": "^5.5.4",
41-
"@mantine/hooks": "^5.5.4",
42-
"@mantine/modals": "^5.5.4",
43-
"@mantine/next": "^5.5.4",
44-
"@mantine/notifications": "^5.5.4",
45-
"@mantine/nprogress": "^5.5.4",
46-
"@mantine/spotlight": "^5.5.4",
40+
"@mantine/core": "^5.5.6",
41+
"@mantine/form": "^5.5.6",
42+
"@mantine/hooks": "^5.5.6",
43+
"@mantine/modals": "^5.5.6",
44+
"@mantine/next": "^5.5.6",
45+
"@mantine/notifications": "^5.5.6",
46+
"@mantine/nprogress": "^5.5.6",
47+
"@mantine/spotlight": "^5.5.6",
4748
"@next/bundle-analyzer": "^12.3.1",
48-
"@prisma/client": "4.4.0",
49-
"@tabler/icons": "^1.101.0",
50-
"@tanstack/react-query": "^4.9.0",
51-
"@trpc/client": "^10.0.0-proxy-beta.13",
52-
"@trpc/next": "^10.0.0-proxy-beta.13",
49+
"@prisma/client": "4.5.0",
50+
"@tabler/icons": "^1.106.0",
51+
"@tanstack/react-query": "^4.12.0",
52+
"@trpc/client": "^10.0.0-proxy-beta.23",
53+
"@trpc/next": "^10.0.0-proxy-beta.23",
5354
"@trpc/react": "^10.0.0-proxy-beta.13",
54-
"@trpc/server": "^10.0.0-proxy-beta.13",
55-
"bullmq": "^2.1.3",
55+
"@trpc/react-query": "^10.0.0-proxy-beta.23",
56+
"@trpc/server": "^10.0.0-proxy-beta.23",
57+
"bullmq": "^2.3.2",
5658
"chokidar": "^3.5.3",
5759
"contrast-color": "^1.0.1",
5860
"dayjs": "^1.11.5",
5961
"execa": "5.1.1",
60-
"express": "^4.18.1",
61-
"framer-motion": "^7.5.3",
62+
"express": "^4.18.2",
63+
"framer-motion": "^7.6.1",
6264
"json-diff": "^0.9.0",
6365
"mantine-datatable": "^1.7.9",
6466
"meow": "^9.0.0",
6567
"moment": "^2.29.4",
6668
"next": "12.3.1",
6769
"node-telegram-bot-api": "^0.59.0",
6870
"pino": "^8.6.1",
69-
"pino-pretty": "^9.1.0",
71+
"pino-pretty": "^9.1.1",
7072
"pretty-bytes": "5.6.0",
7173
"prop-types": "^15.8.1",
7274
"react": "18.2.0",
7375
"react-dom": "18.2.0",
74-
"react-icons": "^4.4.0",
76+
"react-icons": "^4.6.0",
7577
"react-no-ssr": "^1.1.0",
7678
"sharp": "^0.31.1",
7779
"string-to-color": "^2.2.2",
78-
"superjson": "^1.10.0",
79-
"yaml": "^2.1.2",
80+
"superjson": "^1.10.1",
81+
"yaml": "^2.1.3",
8082
"zod": "^3.19.1"
8183
},
8284
"devDependencies": {
@@ -85,31 +87,31 @@
8587
"@types/contrast-color": "^1.0.0",
8688
"@types/express": "^4.17.14",
8789
"@types/json-diff": "^0.9.0",
88-
"@types/node": "18.8.0",
89-
"@types/node-telegram-bot-api": "^0.57.1",
90+
"@types/node": "18.11.2",
91+
"@types/node-telegram-bot-api": "^0.57.5",
9092
"@types/react": "18.0.21",
9193
"@types/react-dom": "18.0.6",
9294
"@types/react-no-ssr": "^1.1.3",
93-
"@typescript-eslint/eslint-plugin": "^5.39.0",
94-
"@typescript-eslint/parser": "^5.39.0",
95+
"@typescript-eslint/eslint-plugin": "^5.40.1",
96+
"@typescript-eslint/parser": "^5.40.1",
9597
"autoprefixer": "^10.4.12",
96-
"eslint": "8.24.0",
98+
"eslint": "8.25.0",
9799
"eslint-config-airbnb": "^19.0.4",
98100
"eslint-config-airbnb-typescript": "^17.0.0",
99101
"eslint-config-next": "12.3.1",
100102
"eslint-config-prettier": "^8.5.0",
101103
"eslint-plugin-import": "^2.26.0",
102104
"eslint-plugin-jsx-a11y": "^6.6.1",
103105
"eslint-plugin-prettier": "^4.2.1",
104-
"eslint-plugin-react": "^7.31.8",
106+
"eslint-plugin-react": "^7.31.10",
105107
"eslint-plugin-react-hooks": "^4.6.0",
106108
"husky": "^4.3.8",
107109
"lint-staged": "^13.0.3",
108-
"postcss": "^8.4.17",
110+
"postcss": "^8.4.18",
109111
"prettier": "^2.7.1",
110112
"prettier-plugin-tailwindcss": "^0.1.13",
111-
"prisma": "^4.4.0",
112-
"tailwindcss": "^3.1.8",
113+
"prisma": "^4.5.0",
114+
"tailwindcss": "^3.2.0",
113115
"ts-node": "^10.9.1",
114116
"typescript": "4.8.4"
115117
}

src/server/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { notificationQueue } from './queue/notify';
1111
const dev = process.env.NODE_ENV !== 'production';
1212
const app = next({ dev });
1313
const handle = app.getRequestHandler();
14-
const port = process.env.PORT || 3000;
14+
const port = process.env.APP_PORT || 3000;
1515

1616
const serverAdapter = new ExpressAdapter();
1717
serverAdapter.setBasePath('/bull/queues');

src/server/queue/checkChapters.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ const checkChapters = async (manga: MangaWithLibraryAndMetadata) => {
9494

9595
export const checkChaptersQueue = new Queue('checkChaptersQueue', {
9696
connection: {
97-
host: 'localhost',
98-
port: 6379,
97+
host: process.env.REDIS_HOST,
98+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
9999
},
100100
});
101101

@@ -109,8 +109,8 @@ export const checkChaptersWorker = new Worker(
109109
{
110110
concurrency: 5,
111111
connection: {
112-
host: 'localhost',
113-
port: 6379,
112+
host: process.env.REDIS_HOST,
113+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
114114
},
115115
},
116116
);

src/server/queue/download.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,16 @@ export const downloadWorker = new Worker(
5252
{
5353
concurrency: 5,
5454
connection: {
55-
host: 'localhost',
56-
port: 6379,
55+
host: process.env.REDIS_HOST,
56+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
5757
},
5858
},
5959
);
6060

6161
export const downloadQueue = new Queue('downloadQueue', {
6262
connection: {
63-
host: 'localhost',
64-
port: 6379,
63+
host: process.env.REDIS_HOST,
64+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
6565
},
6666
defaultJobOptions: {
6767
attempts: 20,

src/server/queue/notify.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ export const notificationWorker = new Worker(
2626
}
2727
},
2828
{
29+
connection: {
30+
host: process.env.REDIS_HOST,
31+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
32+
},
2933
concurrency: 30,
3034
limiter: {
3135
max: 30,
@@ -36,8 +40,8 @@ export const notificationWorker = new Worker(
3640

3741
export const notificationQueue = new Queue('notificationQueue', {
3842
connection: {
39-
host: 'localhost',
40-
port: 6379,
43+
host: process.env.REDIS_HOST,
44+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
4145
},
4246
defaultJobOptions: {
4347
attempts: 20,

src/server/trpc/router/library.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@ export const libraryRouter = t.router({
2020
path: libraryPath,
2121
},
2222
});
23-
await createLibrary(libraryPath);
23+
try {
24+
await createLibrary(libraryPath);
25+
} catch (err) {
26+
await ctx.prisma.library.delete({
27+
where: {
28+
id: library.id,
29+
},
30+
});
31+
throw err;
32+
}
2433
return library;
2534
}),
2635
});

src/server/utils/notification.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import TelegramBot from 'node-telegram-bot-api';
22

3-
const token = '***REMOVED***';
4-
const chatId = '***REMOVED***';
3+
const token = process.env.TELEGRAM_TOKEN || '';
4+
const chatId = process.env.TELEGRAM_CHAT_ID || '';
5+
const disableNotification = !!parseInt(process.env.TELEGRAM_SEND_SILENTLY || '0', 10);
56
const bot = new TelegramBot(token);
67

78
export const sendNotification = async (title: string, body: string, url?: string) => {
9+
if (!token || !chatId) {
10+
return;
11+
}
12+
813
const message = `
914
<b>${title}</b>
1015
1116
${body}
1217
1318
${url || ''}
1419
`;
15-
await bot.sendMessage(chatId, message, { parse_mode: 'HTML' });
20+
await bot.sendMessage(chatId, message, { parse_mode: 'HTML', disable_notification: disableNotification });
1621
};

0 commit comments

Comments
 (0)