containerize and add basic setup to backend

This commit is contained in:
2025-07-16 10:13:09 +01:00
parent faabae9ee0
commit a674f5641d
11 changed files with 256 additions and 4 deletions

2
backend/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
.build/
tests/

13
backend/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM golang:1.24-alpine AS build
WORKDIR /app
COPY . .
RUN go build -o nuchat fergus.molloy.xyz/nuchat
FROM scratch
WORKDIR /app
COPY --from=build /app/nuchat .
ENTRYPOINT ["/app/nuchat"]

View File

@ -1,6 +1,21 @@
build:
GO_TEST := "gotestsum --format=testname --"
dirs:
@mkdir -p .build/tests
build: dirs
go build -o .build/nuchat fergus.molloy.xyz/nuchat
run: build
./.build/nuchat
test: unit integration coverage
integration:
{{GO_TEST}} ./tests/... | tee .build/tests/integration.log
unit:
{{GO_TEST}} -race $(go list ./... | grep -v "/tests") | tee .build/tests/unit.log
coverage:
{{GO_TEST}} $(go list ./... | grep -v "/tests") -coverprofile .build/tests/coverprofile.txt | tee .build/tests/coverage.log

View File

@ -3,5 +3,5 @@ package main
import "fmt"
func main() {
fmt.Println("hello world")
fmt.Println("hello world 3")
}

View File

@ -0,0 +1,7 @@
package tests
import "testing"
func TestHello(t *testing.T) {
}

22
docker-compose.yml Normal file
View File

@ -0,0 +1,22 @@
services:
ui:
build: ./ui
ports:
- "5173:5173"
backend:
build: ./backend
#ports:
#- "7000:7000"
db:
image: postgres:17-alpine
ports:
- "5432:5432"
environment:
POSTGRES_DB: nuchat
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:

2
ui/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
node_modules/
build/

31
ui/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM node:24-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
FROM base AS prod
WORKDIR /app
COPY pnpm-lock.yaml .
RUN pnpm fetch --prod
FROM base AS build
WORKDIR /app
COPY pnpm-lock.yaml .
COPY package.json .
RUN pnpm install
COPY . .
RUN pnpm run build
FROM base AS runner
COPY --from=prod /app/node_modules /app/node_modules
COPY --from=build /app/build build/
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "build"]

View File

@ -21,6 +21,7 @@
"@eslint/js": "^9.18.0",
"@playwright/test": "^1.49.1",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/adapter-node": "^5.2.13",
"@sveltejs/kit": "^2.22.0",
"@sveltejs/vite-plugin-svelte": "^6.0.0",
"@tailwindcss/vite": "^4.0.0",
@ -44,6 +45,7 @@
},
"pnpm": {
"onlyBuiltDependencies": [
"@tailwindcss/oxide",
"esbuild"
]
}

158
ui/pnpm-lock.yaml generated
View File

@ -20,6 +20,9 @@ importers:
'@sveltejs/adapter-auto':
specifier: ^6.0.0
version: 6.0.1(@sveltejs/kit@2.24.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))
'@sveltejs/adapter-node':
specifier: ^5.2.13
version: 5.2.13(@sveltejs/kit@2.24.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))
'@sveltejs/kit':
specifier: ^2.22.0
version: 2.24.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1))
@ -359,6 +362,42 @@ packages:
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@rollup/plugin-commonjs@28.0.6':
resolution: {integrity: sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==}
engines: {node: '>=16.0.0 || 14 >= 14.17'}
peerDependencies:
rollup: ^2.68.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/plugin-json@6.1.0':
resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/plugin-node-resolve@16.0.1':
resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^2.78.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@5.2.0':
resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.45.1':
resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==}
cpu: [arm]
@ -469,6 +508,11 @@ packages:
peerDependencies:
'@sveltejs/kit': ^2.0.0
'@sveltejs/adapter-node@5.2.13':
resolution: {integrity: sha512-yS2TVFmIrxjGhYaV5/iIUrJ3mJl6zjaYn0lBD70vTLnYvJeqf3cjvLXeXCUCuYinhSBoyF4DpfGla49BnIy7sQ==}
peerDependencies:
'@sveltejs/kit': ^2.4.0
'@sveltejs/kit@2.24.0':
resolution: {integrity: sha512-6aCsU6PwxB4CQBJEvLnOoSv8hoviZWDVTKDTzQoERG6vpIHeXJufVWQlyaOXrdSlqBiwknHm0nQT/S/Nn3518A==}
engines: {node: '>=18.13'}
@ -611,6 +655,9 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@typescript-eslint/eslint-plugin@8.37.0':
resolution: {integrity: sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -809,6 +856,9 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
commondir@1.0.1:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@ -935,6 +985,9 @@ packages:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
@ -999,6 +1052,9 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@ -1025,6 +1081,10 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@ -1041,6 +1101,10 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@ -1049,10 +1113,16 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-module@1.0.0:
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
is-reference@3.0.3:
resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
@ -1255,6 +1325,9 @@ packages:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
@ -1413,6 +1486,11 @@ packages:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
resolve@1.22.10:
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
engines: {node: '>= 0.4'}
hasBin: true
reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@ -1473,6 +1551,10 @@ packages:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
svelte-check@4.2.2:
resolution: {integrity: sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ==}
engines: {node: '>= 18.0.0'}
@ -1885,6 +1967,42 @@ snapshots:
'@polka/url@1.0.0-next.29': {}
'@rollup/plugin-commonjs@28.0.6(rollup@4.45.1)':
dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.45.1)
commondir: 1.0.1
estree-walker: 2.0.2
fdir: 6.4.6(picomatch@4.0.3)
is-reference: 1.2.1
magic-string: 0.30.17
picomatch: 4.0.3
optionalDependencies:
rollup: 4.45.1
'@rollup/plugin-json@6.1.0(rollup@4.45.1)':
dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.45.1)
optionalDependencies:
rollup: 4.45.1
'@rollup/plugin-node-resolve@16.0.1(rollup@4.45.1)':
dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.45.1)
'@types/resolve': 1.20.2
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.10
optionalDependencies:
rollup: 4.45.1
'@rollup/pluginutils@5.2.0(rollup@4.45.1)':
dependencies:
'@types/estree': 1.0.8
estree-walker: 2.0.2
picomatch: 4.0.3
optionalDependencies:
rollup: 4.45.1
'@rollup/rollup-android-arm-eabi@4.45.1':
optional: true
@ -1953,6 +2071,14 @@ snapshots:
dependencies:
'@sveltejs/kit': 2.24.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1))
'@sveltejs/adapter-node@5.2.13(@sveltejs/kit@2.24.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))':
dependencies:
'@rollup/plugin-commonjs': 28.0.6(rollup@4.45.1)
'@rollup/plugin-json': 6.1.0(rollup@4.45.1)
'@rollup/plugin-node-resolve': 16.0.1(rollup@4.45.1)
'@sveltejs/kit': 2.24.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1))
rollup: 4.45.1
'@sveltejs/kit@2.24.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.2)(vite@7.0.4(jiti@2.4.2)(lightningcss@1.30.1))':
dependencies:
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
@ -2093,6 +2219,8 @@ snapshots:
'@types/json-schema@7.0.15': {}
'@types/resolve@1.20.2': {}
'@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
@ -2328,6 +2456,8 @@ snapshots:
color-name@1.1.4: {}
commondir@1.0.1: {}
concat-map@0.0.1: {}
cookie@0.6.0: {}
@ -2491,6 +2621,8 @@ snapshots:
estraverse@5.3.0: {}
estree-walker@2.0.2: {}
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.8
@ -2547,6 +2679,8 @@ snapshots:
fsevents@2.3.3:
optional: true
function-bind@1.1.2: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@ -2565,6 +2699,10 @@ snapshots:
has-flag@4.0.0: {}
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
ignore@5.3.2: {}
ignore@7.0.5: {}
@ -2576,14 +2714,24 @@ snapshots:
imurmurhash@0.1.4: {}
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
is-extglob@2.1.1: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-module@1.0.0: {}
is-number@7.0.0: {}
is-reference@1.2.1:
dependencies:
'@types/estree': 1.0.8
is-reference@3.0.3:
dependencies:
'@types/estree': 1.0.8
@ -2740,6 +2888,8 @@ snapshots:
path-key@3.1.1: {}
path-parse@1.0.7: {}
pathe@2.0.3: {}
pathval@2.0.1: {}
@ -2815,6 +2965,12 @@ snapshots:
resolve-from@4.0.0: {}
resolve@1.22.10:
dependencies:
is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
reusify@1.1.0: {}
rollup@4.45.1:
@ -2885,6 +3041,8 @@ snapshots:
dependencies:
has-flag: 4.0.0
supports-preserve-symlinks-flag@1.0.0: {}
svelte-check@4.2.2(picomatch@4.0.3)(svelte@5.36.2)(typescript@5.8.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.29

View File

@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */