+2
-2
.gitignore
+2
-2
.gitignore
+74
Dockerfile
+74
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 (including PostgreSQL client for multi-database support)
7
+
RUN apk add --no-cache \
8
+
bash \
9
+
git \
10
+
nodejs \
11
+
npm \
12
+
build-base \
13
+
sqlite-dev \
14
+
postgresql-dev
15
+
16
+
# Configure git for non-interactive use
17
+
ENV GIT_TERMINAL_PROMPT=0
18
+
19
+
# Clone quickslice at the v0.17.3 tag (includes sub claim fix)
20
+
RUN git clone --depth 1 --branch v0.17.3 https://github.com/bigmoves/quickslice.git /build
21
+
22
+
# Install dependencies for all projects
23
+
RUN cd /build/client && gleam deps download
24
+
RUN cd /build/lexicon_graphql && gleam deps download
25
+
RUN cd /build/server && gleam deps download
26
+
27
+
# Apply patches to dependencies
28
+
RUN cd /build && patch -p1 < patches/mist-websocket-protocol.patch
29
+
30
+
# Install JavaScript dependencies for client
31
+
RUN cd /build/client && npm install
32
+
33
+
# Compile the client code and output to server's static directory
34
+
RUN cd /build/client \
35
+
&& gleam add --dev lustre_dev_tools \
36
+
&& gleam run -m lustre/dev build quickslice_client --minify --outdir=/build/server/priv/static
37
+
38
+
# Compile the server code
39
+
RUN cd /build/server \
40
+
&& gleam export erlang-shipment
41
+
42
+
# Runtime stage - slim image with only what's needed to run
43
+
FROM ghcr.io/gleam-lang/gleam:${GLEAM_VERSION}-erlang-alpine
44
+
45
+
# Install runtime dependencies and dbmate for migrations
46
+
ARG TARGETARCH
47
+
RUN apk add --no-cache sqlite-libs sqlite libpq curl \
48
+
&& DBMATE_ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "amd64") \
49
+
&& curl -fsSL -o /usr/local/bin/dbmate https://github.com/amacneil/dbmate/releases/latest/download/dbmate-linux-${DBMATE_ARCH} \
50
+
&& chmod +x /usr/local/bin/dbmate
51
+
52
+
# Copy the compiled server code from the builder stage
53
+
COPY --from=builder /build/server/build/erlang-shipment /app
54
+
55
+
# Copy database migrations and config
56
+
COPY --from=builder /build/server/db /app/db
57
+
COPY --from=builder /build/server/.dbmate.yml /app/.dbmate.yml
58
+
COPY --from=builder /build/server/docker-entrypoint.sh /app/docker-entrypoint.sh
59
+
60
+
# Set up the entrypoint
61
+
WORKDIR /app
62
+
63
+
# Create the data directory for the SQLite database and Fly.io volume mount
64
+
RUN mkdir -p /data && chmod 755 /data
65
+
66
+
# Set environment variables
67
+
ENV HOST=0.0.0.0
68
+
ENV PORT=8080
69
+
70
+
# Expose the port the server will run on
71
+
EXPOSE $PORT
72
+
73
+
# Run the server
74
+
CMD ["/app/docker-entrypoint.sh", "run"]
+74
README.md
+74
README.md
···
1
+
# quickslice-status
2
+
3
+
a status app for bluesky, built with [quickslice](https://github.com/bigmoves/quickslice).
4
+
5
+
**live:** https://quickslice-status.pages.dev
6
+
7
+
## architecture
8
+
9
+
- **backend**: [quickslice](https://github.com/bigmoves/quickslice) on fly.io - handles oauth, graphql api, jetstream ingestion
10
+
- **frontend**: static site on cloudflare pages - vanilla js spa
11
+
12
+
## deployment
13
+
14
+
### backend (fly.io)
15
+
16
+
builds quickslice from source at v0.17.3 tag.
17
+
18
+
```bash
19
+
fly deploy
20
+
```
21
+
22
+
required secrets:
23
+
```bash
24
+
fly secrets set SECRET_KEY_BASE="$(openssl rand -base64 64 | tr -d '\n')"
25
+
fly secrets set OAUTH_SIGNING_KEY="$(goat key generate -t p256 | tail -1)"
26
+
```
27
+
28
+
### frontend (cloudflare pages)
29
+
30
+
```bash
31
+
cd site
32
+
npx wrangler pages deploy . --project-name=quickslice-status
33
+
```
34
+
35
+
## oauth client registration
36
+
37
+
register an oauth client in the quickslice admin ui at `https://zzstoatzz-quickslice-status.fly.dev/`
38
+
39
+
redirect uri: `https://quickslice-status.pages.dev/callback`
40
+
41
+
## lexicon
42
+
43
+
uses `io.zzstoatzz.status` lexicon for user statuses.
44
+
45
+
```json
46
+
{
47
+
"lexicon": 1,
48
+
"id": "io.zzstoatzz.status",
49
+
"defs": {
50
+
"main": {
51
+
"type": "record",
52
+
"key": "self",
53
+
"record": {
54
+
"type": "object",
55
+
"required": ["status", "createdAt"],
56
+
"properties": {
57
+
"status": { "type": "string", "maxLength": 128 },
58
+
"createdAt": { "type": "string", "format": "datetime" }
59
+
}
60
+
}
61
+
}
62
+
}
63
+
}
64
+
```
65
+
66
+
## local development
67
+
68
+
serve the frontend locally:
69
+
```bash
70
+
cd site
71
+
python -m http.server 8000
72
+
```
73
+
74
+
for oauth to work locally, you'd need to register a separate oauth client with `http://localhost:8000/callback` as the redirect uri and update `CONFIG.clientId` in `app.js`.
-10
docs/Caddyfile
-10
docs/Caddyfile
-10
docs/Caddyfile.dev
-10
docs/Caddyfile.dev
-9
docs/Dockerfile
-9
docs/Dockerfile
+3
docs/app.js
site/app.js
+3
docs/app.js
site/app.js
docs/bufos.json
site/bufos.json
docs/bufos.json
site/bufos.json
docs/favicon.svg
site/favicon.svg
docs/favicon.svg
site/favicon.svg
-17
docs/fly.toml
-17
docs/fly.toml
···
1
-
app = "quickslice-status"
2
-
primary_region = "ewr"
3
-
4
-
[build]
5
-
dockerfile = "Dockerfile"
6
-
7
-
[http_service]
8
-
internal_port = 8000
9
-
force_https = true
10
-
auto_stop_machines = "stop"
11
-
auto_start_machines = true
12
-
min_machines_running = 0
13
-
14
-
[[vm]]
15
-
cpu_kind = "shared"
16
-
cpus = 1
17
-
memory_mb = 256
docs/index.html
site/index.html
docs/index.html
site/index.html
docs/styles.css
site/styles.css
docs/styles.css
site/styles.css
+1
-1
fly.toml
+1
-1
fly.toml
lexicons.zip
lexicons.zip
This is a binary file and will not be displayed.