+31
.dockerignore
+31
.dockerignore
···
···
1
+
# flyctl launch added from client/.gitignore
2
+
client/**/*.beam
3
+
client/**/*.ez
4
+
client/build
5
+
client/**/erl_crash.dump
6
+
7
+
#Added automatically by Lustre Dev Tools
8
+
client/.lustre
9
+
client/dist
10
+
client/node_modules
11
+
12
+
# flyctl launch added from server/.gitignore
13
+
server/**/*.beam
14
+
server/**/*.ez
15
+
server/build
16
+
server/**/erl_crash.dump
17
+
server/**/*db*
18
+
server/**/.env
19
+
20
+
# flyctl launch added from server/build/packages/squall/.gitignore
21
+
server/build/packages/squall/**/*.beam
22
+
server/build/packages/squall/**/*.ez
23
+
server/build/packages/squall/**/build
24
+
server/build/packages/squall/**/erl_crash.dump
25
+
26
+
# flyctl launch added from shared/.gitignore
27
+
shared/**/*.beam
28
+
shared/**/*.ez
29
+
shared/build
30
+
shared/**/erl_crash.dump
31
+
fly.toml
+18
.github/workflows/fly-deploy.yml
+18
.github/workflows/fly-deploy.yml
···
···
1
+
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
2
+
3
+
name: Fly Deploy
4
+
on:
5
+
push:
6
+
branches:
7
+
- main
8
+
jobs:
9
+
deploy:
10
+
name: Deploy app
11
+
runs-on: ubuntu-latest
12
+
concurrency: deploy-group # optional: ensure only one action runs at a time
13
+
steps:
14
+
- uses: actions/checkout@v4
15
+
- uses: superfly/flyctl-actions/setup-flyctl@master
16
+
- run: flyctl deploy --remote-only
17
+
env:
18
+
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+58
Dockerfile
+58
Dockerfile
···
···
1
+
ARG GLEAM_VERSION=v1.13.0
2
+
3
+
# Build stage - compile the application
4
+
FROM ghcr.io/gleam-lang/gleam:${GLEAM_VERSION}-erlang-alpine AS builder
5
+
6
+
# Install build dependencies
7
+
RUN apk add --no-cache \
8
+
git \
9
+
nodejs \
10
+
npm \
11
+
build-base \
12
+
sqlite-dev
13
+
14
+
# Configure git for non-interactive use
15
+
ENV GIT_TERMINAL_PROMPT=0
16
+
17
+
# Add project code
18
+
COPY ./shared /build/shared
19
+
COPY ./client /build/client
20
+
COPY ./server /build/server
21
+
22
+
# Install dependencies for all projects
23
+
RUN cd /build/shared && gleam deps download
24
+
RUN cd /build/client && gleam deps download
25
+
RUN cd /build/server && gleam deps download
26
+
27
+
# Install JavaScript dependencies for client
28
+
RUN cd /build/client && npm install
29
+
30
+
# Compile the client code and output to server's static directory
31
+
RUN cd /build/client \
32
+
&& gleam add --dev lustre_dev_tools \
33
+
&& gleam run -m lustre/dev build client --minify --outdir=/build/server/priv/static
34
+
35
+
# Compile the server code
36
+
RUN cd /build/server \
37
+
&& gleam export erlang-shipment
38
+
39
+
# Runtime stage - slim image with only what's needed to run
40
+
FROM ghcr.io/gleam-lang/gleam:${GLEAM_VERSION}-erlang-alpine
41
+
42
+
# Copy the compiled server code from the builder stage
43
+
COPY --from=builder /build/server/build/erlang-shipment /app
44
+
45
+
# Set up the entrypoint
46
+
WORKDIR /app
47
+
RUN echo -e '#!/bin/sh\nexec ./entrypoint.sh "$@"' > ./start.sh \
48
+
&& chmod +x ./start.sh
49
+
50
+
# Set environment variables
51
+
ENV HOST=0.0.0.0
52
+
ENV PORT=8080
53
+
54
+
# Expose the port the server will run on
55
+
EXPOSE $PORT
56
+
57
+
# Run the server
58
+
CMD ["./start.sh", "run"]
+22
fly.toml
+22
fly.toml
···
···
1
+
# fly.toml app configuration file generated for atconf on 2025-10-24T14:04:15-07:00
2
+
#
3
+
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
4
+
#
5
+
6
+
app = 'atconf'
7
+
primary_region = 'sjc'
8
+
9
+
[build]
10
+
11
+
[http_service]
12
+
internal_port = 8080
13
+
force_https = true
14
+
auto_stop_machines = 'stop'
15
+
auto_start_machines = true
16
+
min_machines_running = 0
17
+
processes = ['app']
18
+
19
+
[[vm]]
20
+
memory = '512mb'
21
+
cpu_kind = 'shared'
22
+
cpus = 1
+8
server/.env.example
+8
server/.env.example
···
3
# Or the server will generate one on first run and print it to console
4
SECRET_KEY_BASE=CHANGE_ME_TO_A_RANDOM_64_CHARACTER_STRING
5
6
+
# Server Configuration
7
+
# Host address to bind to (defaults to 127.0.0.1 if not set)
8
+
# Use 0.0.0.0 to listen on all interfaces (required for Docker/production)
9
+
HOST=127.0.0.1
10
+
11
+
# Port the server will listen on (defaults to 3000 if not set)
12
+
PORT=3000
13
+
14
# OAuth Configuration
15
# These values will be used if environment variables are not set
16
+22
-1
server/src/server.gleam
+22
-1
server/src/server.gleam
···
9
import gleam/http.{Get, Post}
10
import gleam/http/request
11
import gleam/httpc
12
import gleam/io
13
import gleam/json
14
import gleam/list
···
82
use db <- sqlight.with_connection("./sessions.db")
83
let assert Ok(_) = session.init_db(db)
84
85
let assert Ok(_) =
86
handle_request(static_directory, db, oauth_config, _)
87
|> wisp_mist.handler(secret_key_base)
88
|> mist.new
89
-
|> mist.port(3000)
90
|> mist.start
91
92
process.sleep_forever()
···
9
import gleam/http.{Get, Post}
10
import gleam/http/request
11
import gleam/httpc
12
+
import gleam/int
13
import gleam/io
14
import gleam/json
15
import gleam/list
···
83
use db <- sqlight.with_connection("./sessions.db")
84
let assert Ok(_) = session.init_db(db)
85
86
+
// Get host from environment or default to 127.0.0.1
87
+
let host = case envoy.get("HOST") {
88
+
Ok(h) -> h
89
+
Error(_) -> "127.0.0.1"
90
+
}
91
+
92
+
// Get port from environment or default to 3000
93
+
let port = case envoy.get("PORT") {
94
+
Ok(port_str) -> {
95
+
case int.parse(port_str) {
96
+
Ok(p) -> p
97
+
Error(_) -> 3000
98
+
}
99
+
}
100
+
Error(_) -> 3000
101
+
}
102
+
103
+
io.println("Listening on http://" <> host <> ":" <> int.to_string(port))
104
+
105
let assert Ok(_) =
106
handle_request(static_directory, db, oauth_config, _)
107
|> wisp_mist.handler(secret_key_base)
108
|> mist.new
109
+
|> mist.bind(host)
110
+
|> mist.port(port)
111
|> mist.start
112
113
process.sleep_forever()