diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f10a034..2832688 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -45,5 +45,6 @@ jobs: with: push: true tags: rmcampos/tasknote:${{ github.event.number }} + build-args: BUILD=${{ github.event.number }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..ba30628 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,57 @@ +{ + auto_https off + admin off +} + +:5000 { + root * /app/dist + + encode gzip + + log { + output stdout + format console { + time_format iso8601 + level_format color + } + level "{$LOG_LEVEL}" + } + + header { + X-Frame-Options "SAMEORIGIN" + X-XSS-Protection "1;mode=block" + Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate" + X-Content-Type-Options "nosniff" + Strict-Transport-Security "max-age=31536000" + Content-Security-Policy " + base-uri 'self'; + connect-src 'self'; + default-src 'self'; + font-src https://cdn.jsdelivr.net/npm/ 'self'; + frame-src 'self'; + img-src 'self'; + manifest-src 'self'; + media-src 'self'; + object-src 'none'; + script-src 'unsafe-inline' 'report-sample' 'self'; + style-src https://cdn.jsdelivr.net/npm/ 'report-sample' 'self'; + worker-src 'none'; + " + Referrer-Policy "same-origin" + } + + handle /env.js { + header Content-Type "text/javascript" + respond `window.config = {"BUILD":"{$BUILD}"};` + } + + file_server + + handle_errors { + @404 { + expression {http.error.status_code} == 404 + } + rewrite @404 / + file_server + } +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..938292c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +# Build static files +# Node Bullseye has npm +FROM node:20.17-bullseye-slim AS build +ENV NODE_OPTIONS="--max-old-space-size=3072" + +# Build +WORKDIR /app +COPY *.html *.json *.ts ./ +COPY ./src ./src +COPY ./public ./public +RUN npm ci --ignore-scripts --no-update-notifier --omit=dev && \ + npm run build && \ + rm -rf node_modules + +# Deploy container +# Caddy serves static files +FROM caddy:2.8.4-alpine +RUN apk add --no-cache ca-certificates curl + +# Receive build number as argument, retain as environment variable +ARG BUILD +ENV BUILD=${BUILD} + +# Copy files and run formatting +COPY --from=build /app/dist/ /app/dist +COPY Caddyfile /etc/caddy/Caddyfile +RUN caddy fmt --overwrite /etc/caddy/Caddyfile + +# User, port and healthcheck +EXPOSE 5000 +HEALTHCHECK CMD curl -f http://localhost:5000 +USER 1001 diff --git a/README.md b/README.md index 86a4151..7ea3ac4 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Contributions are what make the open-source community such an amazing place to l ## 📄 License -Distributed under the MIT License. See `LICENSE` for more information. +Distributed under GPLv3 License. See `LICENSE` for more information. ## 📞 Contact diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1e86a86 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +--- + +services: + tasknote: + container_name: tasknote + profiles: + - "dev" + image: node:20.17-bullseye-slim + ports: + - "5000:5000" + entrypoint: sh -c "npm i --no-update-notifier && npm start" + user: root + volumes: + - "./:/app" + working_dir: /app + environment: + LOG_LEVEL: debug + healthcheck: + test: timeout 10s bash -c 'true > /dev/tcp/127.0.0.1/3000' + + caddy: + container_name: caddy + profiles: + - "caddy" + build: + context: . + args: + BUILD: nightly + ports: + - "80:5000" + volumes: + - "./Caddyfile:/etc/caddy/Caddyfile" + environment: + LOG_LEVEL: debug + healthcheck: + test: timeout 10s bash -c 'true > /dev/tcp/127.0.0.1/3000' diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index bf4f5a0..440f433 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,14 +1,20 @@ import React from 'react'; import style from './Footer.module.css'; +import { env } from '../env'; -const Footer = () => ( - -); +const Footer = () => { + const build = env.BUILD; + + return ( + + ); +}; export default Footer; diff --git a/tools.md b/tools.md index e8f1956..45d08c8 100644 --- a/tools.md +++ b/tools.md @@ -3,3 +3,22 @@ - **TaskNote:** https://tasknote.local - **Docker Hub:** https://hub.docker.com/ - **Time tracking:** https://track.toggl.com/timer + +# Building locally + +Build with Docker: + +```sh +docker build --build-arg BUILD=nightly -t tasknote:nightly . +``` + +Run with Docker: +```sh +docker run -it --rm \ + --name tasknote \ + tasknote:nightly +``` +Build and run with Docker Compose: +```sh +docker compose --profile caddy up --build +``` diff --git a/vite.config.ts b/vite.config.ts index 0fbe8d1..33be9da 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,7 +5,27 @@ import { fileURLToPath } from 'url'; export default defineConfig(({ mode }: ConfigEnv) => { const config: UserConfig = { define: {} as any, - plugins: [react()], + plugins: [ + react(), + { + name: 'build-html', + apply: 'build', + transformIndexHtml: (html) => { + return { + html, + tags: [ + { + tag: 'script', + attrs: { + src: '/env.js' + }, + injectTo: 'head' + } + ] + } + } + } + ], build: { outDir: 'dist', sourcemap: true