diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..49ad795 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +node_modules/ +dist/ +web/dist/ +.git/ +.gitattributes +.gitignore +*.md +test/ +mailship.db +test-mailship.db +test-data/ +.env +.env.local +*.p8 +*firebase* \ No newline at end of file diff --git a/.env.template b/.env.template index abd80e4..885b96f 100644 --- a/.env.template +++ b/.env.template @@ -8,3 +8,4 @@ SEARCH_RELAYS=relay.nostr.band POSTMARK_API_KEY= POSTMARK_SENDER_ADDRESS= PORT=4738 +DATA_DIR=./data diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..871df82 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +# syntax=docker/dockerfile:1 +FROM node:22-alpine AS build + +WORKDIR /app + +# Install build dependencies for native modules +RUN apk add --no-cache python3 make g++ git + +# Install pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# Copy package files +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY remove-pnpm-overrides.js ./ + +# Remove pnpm link overrides for production build +RUN node remove-pnpm-overrides.js package.json + +# Install dependencies +RUN pnpm install --frozen-lockfile + +# Copy source +COPY tsconfig.json ./ +COPY src/ ./src/ +COPY src/pages/ ./src/pages/ +COPY src/emails/ ./src/emails/ + +# Build TypeScript +RUN pnpm run build + +# Build web UI +COPY web/package.json web/pnpm-lock.yaml web/tsconfig.json web/vite.config.js web/index.html ./ +COPY web/src/ ./web/src/ +RUN cd web && pnpm install --frozen-lockfile && pnpm run build + +# Production image +FROM node:22-alpine AS production + +WORKDIR /app + +RUN apk add --no-cache sqlite + +# Install pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# Copy production dependencies +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY remove-pnpm-overrides.js ./ +RUN node remove-pnpm-overrides.js package.json +RUN pnpm install --frozen-lockfile --prod + +# Copy build artifacts +COPY --from=build /app/dist/ ./dist/ +COPY --from=build /app/web/dist/ ./web/dist/ +COPY --from=build /app/src/pages/ ./dist/pages/ +COPY --from=build /app/src/emails/ ./dist/emails/ + +# Create data directory for SQLite +RUN mkdir -p /data + +EXPOSE 4738 + +ENV NODE_ENV=production +ENV PORT=4738 +ENV DATA_DIR=/data + +CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/README.md b/README.md index e7ded5a..563d760 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,34 @@ pnpm run build pnpm run start ``` +## Docker + +### Quick start + +```sh +cp .env.template .env +# Fill in your secrets in .env +docker compose up -d +``` + +### Build and run manually + +```sh +docker build -t mailship . +docker run -d \ + -p 4738:4738 \ + -v mailship-data:/data \ + --env-file .env \ + mailship +``` + +## Tests + +```sh +pnpm test # Run integration tests +pnpm test:server # Start server for manual testing +``` + ## Forked from Anchor Mailship is a fork of [Anchor](https://github.com/coracle-social/anchor), stripped of push notification support and adapted for NIP-9a relay push event intake. \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1d172dd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +services: + mailship: + build: . + ports: + - "4738:4738" + volumes: + - mailship-data:/data + environment: + - MAILSHIP_SECRET=${MAILSHIP_SECRET:?required} + - MAILSHIP_NAME=${MAILSHIP_NAME:-Mailship} + - MAILSHIP_URL=${MAILSHIP_URL:?required} + - BASE_URL=${BASE_URL:?required} + - POSTMARK_API_KEY=${POSTMARK_API_KEY:?required} + - POSTMARK_SENDER_ADDRESS=${POSTMARK_SENDER_ADDRESS:?required} + - DEFAULT_RELAYS=${DEFAULT_RELAYS:-wss://relay.damus.io,wss://relay.primal.net} + - INDEXER_RELAYS=${INDEXER_RELAYS:-wss://purplepag.es,wss://relay.damus.io} + - SEARCH_RELAYS=${SEARCH_RELAYS:-wss://relay.nostr.band} + - PORT=4738 + - DATA_DIR=/data + restart: unless-stopped + +volumes: + mailship-data: \ No newline at end of file