feat: add docker configuration
This commit is contained in:
parent
821712f43b
commit
cf2f624ae4
21 changed files with 232 additions and 13 deletions
23
.dockerignore
Normal file
23
.dockerignore
Normal 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
8
DOCKER.md
Normal 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
22
Dockerfile.backend
Normal 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"]
|
|
@ -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>
|
||||||
|
|
12
Foxnouns.Frontend/Dockerfile
Normal file
12
Foxnouns.Frontend/Dockerfile
Normal 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"]
|
|
@ -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,
|
||||||
|
|
|
@ -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";
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
71
docker-compose.yml
Normal 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
4
docker/Caddyfile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
http:// {
|
||||||
|
reverse_proxy /api/* http://rate:5003
|
||||||
|
reverse_proxy http://frontend:3000
|
||||||
|
}
|
48
docker/config.example.ini
Normal file
48
docker/config.example.ini
Normal 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
0
docker/frontend.env
Normal file
6
docker/proxy-config.example.json
Normal file
6
docker/proxy-config.example.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"port": 5003,
|
||||||
|
"proxy_target": "http://localhost:5000",
|
||||||
|
"debug": true,
|
||||||
|
"powered_by": "5 gay rats"
|
||||||
|
}
|
16
rate/Dockerfile
Normal file
16
rate/Dockerfile
Normal 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"]
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue