feat: add docker configuration

This commit is contained in:
sam 2024-09-14 18:07:49 +02:00
parent 821712f43b
commit cf2f624ae4
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
21 changed files with 232 additions and 13 deletions

23
.dockerignore Normal file
View file

@ -0,0 +1,23 @@
**/.dockerignore
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

8
DOCKER.md Normal file
View file

@ -0,0 +1,8 @@
# Running with Docker
1. Copy `docker/config.example.ini` to `docker/config.ini`, and change the settings to your liking.
2. Copy `docker/proxy-config.example.json` to `docker/proxy-config.json`, and do the same.
3. Build with `docker compose build`
4. Run with `docker compose up`
The Caddy server will listen on `localhost:5004`.

22
Dockerfile.backend Normal file
View file

@ -0,0 +1,22 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 5000
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Foxnouns.Backend/Foxnouns.Backend.csproj", "Foxnouns.Backend/"]
RUN dotnet restore "Foxnouns.Backend/Foxnouns.Backend.csproj"
COPY . .
WORKDIR "/src/Foxnouns.Backend"
RUN dotnet build "Foxnouns.Backend.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "Foxnouns.Backend.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Foxnouns.Backend.dll", "--migrate-and-start"]

View file

@ -4,6 +4,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -44,4 +45,10 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="..\.version" LogicalName="version" /> <EmbeddedResource Include="..\.version" LogicalName="version" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
</ItemGroup>
</Project> </Project>

View file

@ -0,0 +1,12 @@
FROM docker.io/node:22
RUN mkdir -p /app/node_modules && chown -R node:node /app
WORKDIR /app
COPY package.json yarn.lock ./
USER node
RUN yarn
COPY --chown=node:node . .
RUN yarn build
CMD ["yarn", "start"]

View file

@ -1,5 +1,5 @@
import { TFunction } from "i18next"; import { TFunction } from "i18next";
import Alert from "react-bootstrap/Alert"; import { Alert } from "react-bootstrap";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { import {
ApiError, ApiError,

View file

@ -3,9 +3,7 @@ import Meta from "~/lib/api/meta";
import { User, UserSettings } from "~/lib/api/user"; import { User, UserSettings } from "~/lib/api/user";
import Logo from "./Logo"; import Logo from "./Logo";
import Nav from "react-bootstrap/Nav"; import { Nav, Navbar, NavDropdown } from "react-bootstrap";
import Navbar from "react-bootstrap/Navbar";
import NavDropdown from "react-bootstrap/NavDropdown";
import { BrightnessHigh, BrightnessHighFill, MoonFill } from "react-bootstrap-icons"; import { BrightnessHigh, BrightnessHighFill, MoonFill } from "react-bootstrap-icons";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View file

@ -1,3 +1,4 @@
import "dotenv/config";
import { env } from "node:process"; import { env } from "node:process";
export const API_BASE = env.API_BASE || "https://pronouns.localhost/api"; export const API_BASE = env.API_BASE || "https://pronouns.localhost/api";

View file

@ -10,10 +10,8 @@ import {
ShouldRevalidateFunction, ShouldRevalidateFunction,
} from "@remix-run/react"; } from "@remix-run/react";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import Form from "react-bootstrap/Form"; import { Form, Button, Alert } from "react-bootstrap";
import Button from "react-bootstrap/Button";
import ErrorAlert from "~/components/ErrorAlert"; import ErrorAlert from "~/components/ErrorAlert";
import Alert from "react-bootstrap/Alert";
export const shouldRevalidate: ShouldRevalidateFunction = ({ actionResult }) => { export const shouldRevalidate: ShouldRevalidateFunction = ({ actionResult }) => {
return !actionResult; return !actionResult;

View file

@ -6,11 +6,7 @@ import {
ActionFunctionArgs, ActionFunctionArgs,
} from "@remix-run/node"; } from "@remix-run/node";
import { Form as RemixForm, useActionData, useLoaderData } from "@remix-run/react"; import { Form as RemixForm, useActionData, useLoaderData } from "@remix-run/react";
import Form from "react-bootstrap/Form"; import { Form, Button, ButtonGroup, ListGroup, Row, Col } from "react-bootstrap";
import Button from "react-bootstrap/Button";
import ButtonGroup from "react-bootstrap/ButtonGroup";
import ListGroup from "react-bootstrap/ListGroup";
import { Row, Col } from "react-bootstrap";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import i18n from "~/i18next.server"; import i18n from "~/i18next.server";
import serverRequest, { getToken, writeCookie } from "~/lib/request.server"; import serverRequest, { getToken, writeCookie } from "~/lib/request.server";

View file

@ -22,6 +22,7 @@
"compression": "^1.7.4", "compression": "^1.7.4",
"cookie": "^0.6.0", "cookie": "^0.6.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"i18next": "^23.15.1", "i18next": "^23.15.1",
"i18next-browser-languagedetector": "^8.0.0", "i18next-browser-languagedetector": "^8.0.0",

View file

@ -2554,7 +2554,7 @@ domutils@^3.0.1, domutils@^3.1.0:
domelementtype "^2.3.0" domelementtype "^2.3.0"
domhandler "^5.0.3" domhandler "^5.0.3"
dotenv@^16.0.0: dotenv@^16.0.0, dotenv@^16.4.5:
version "16.4.5" version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==

71
docker-compose.yml Normal file
View file

@ -0,0 +1,71 @@
services:
backend:
image: backend
build:
context: .
dockerfile: ./Dockerfile.backend
environment:
- "Database:Url=Host=pgbouncer;Database=postgres;Username=postgres;Password=postgres"
- "Database:EnablePooling=false"
- "Host=0.0.0.0"
- "Port=5000"
restart: unless-stopped
volumes:
- ./docker/config.ini:/app/config.ini
frontend:
image: frontend
build: ./Foxnouns.Frontend
environment:
- "API_BASE=http://rate:5003/api"
restart: unless-stopped
volumes:
- ./docker/frontend.env:/app/.env
rate:
image: rate
build: ./rate
environment:
- "PORT=5003"
restart: unless-stopped
volumes:
- ./docker/proxy-config.json:/app/proxy-config.json
pgbouncer:
image: docker.io/edoburu/pgbouncer:latest
environment:
- "DATABASE_URL=postgres://postgres:postgres@postgres/postgres"
- "AUTH_TYPE=scram-sha-256"
- "MAX_CLIENT_CONN=100"
- "DEFAULT_POOL_SIZE=100"
- "MIN_POOL_SIZE=10"
restart: unless-stopped
postgres:
image: docker.io/postgres:16
command: [ "postgres",
"-c", "max-connections=1000",
"-c", "timezone=Etc/UTC",
"-c", "max_wal_size=1GB",
"-c", "min_wal_size=80MB",
"-c", "shared_buffers=128MB" ]
environment:
- "POSTGRES_PASSWORD=postgres"
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
caddy:
image: docker.io/caddy:2
restart: unless-stopped
ports:
- "5004:80"
volumes:
- ./docker/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
postgres_data:

4
docker/Caddyfile Normal file
View file

@ -0,0 +1,4 @@
http:// {
reverse_proxy /api/* http://rate:5003
reverse_proxy http://frontend:3000
}

48
docker/config.example.ini Normal file
View file

@ -0,0 +1,48 @@
;; This configuration file is specifically for Docker installations.
;; Host, Port, and Database settings are overridden in the compose configuration.
; The base *external* URL
BaseUrl = https://pronouns.localhost
; The base URL for media, without a trailing slash. This must be publicly accessible.
MediaBaseUrl = https://cdn-staging.pronouns.localhost
[Logging]
; The level to log things at. Valid settings: Verbose, Debug, Information, Warning, Error, Fatal
LogEventLevel = Debug
; The URL to the Seq instance (optional)
SeqLogUrl = http://localhost:5341
; The Sentry DSN to log to (optional)
SentryUrl = https://examplePublicKey@o0.ingest.sentry.io/0
; Whether to trace performance with Sentry (optional)
SentryTracing = true
; Percentage of performance traces to send to Sentry (optional). Defaults to 0.0 (no traces at all)
SentryTracesSampleRate = 1.0
; Whether to log SQL queries. Note that this is very verbose. Defaults to false.
LogQueries = false
; Whether metrics are enabled. If this is set to true, Foxnouns.NET will rely on Prometheus scraping metrics to update stats.
; If set to false, a background service will be used instead. Does not actually disable the /metrics endpoint.
; Defaults to false.
EnableMetrics = true
; The port the /metrics endpoint will listen on. Defaults to 5001.
MetricsPort = 5001
[Storage]
Endpoint = <s3EndpointHere>
AccessKey = <s3AccessKey>
SecretKey = <s3SecretKey>
Bucket = pronounscc
[EmailAuth]
; The address that emails will be sent from. If not set, email auth is disabled.
From = noreply@accounts.pronouns.cc
; The Coravel mail driver configuration. Keys should be self-explanatory.
[Coravel:Mail]
Host = localhost
Port = 1025
Username = smtp-username
Password = smtp-password
[DiscordAuth]
ClientId = <clientIdHere>
ClientSecret = <clientSecretHere>

0
docker/frontend.env Normal file
View file

View file

@ -0,0 +1,6 @@
{
"port": 5003,
"proxy_target": "http://localhost:5000",
"debug": true,
"powered_by": "5 gay rats"
}

16
rate/Dockerfile Normal file
View file

@ -0,0 +1,16 @@
FROM docker.io/golang:latest AS builder
WORKDIR /build
EXPOSE 5003
COPY . ./
RUN go mod download -x
ENV CGO_ENABLED 0
RUN go build -v -o rate
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /build/rate rate
CMD ["/app/rate"]

View file

View file

View file

@ -23,6 +23,14 @@ func main() {
log.Fatalf("unmarshaling config.json: %v", err) log.Fatalf("unmarshaling config.json: %v", err)
} }
// Override port from environment if it's set
if portEnv := os.Getenv("PORT"); portEnv != "" {
port, err := strconv.Atoi(portEnv)
if err == nil {
hn.Port = port
}
}
proxyURL, err := url.Parse(hn.ProxyTarget) proxyURL, err := url.Parse(hn.ProxyTarget)
if err != nil { if err != nil {
log.Fatalf("parsing proxy_target as URL: %v", err) log.Fatalf("parsing proxy_target as URL: %v", err)