Skip to content

Commit 53d8f25

Browse files
Interoperate with ozone and add deployment files (#24)
add deployment files, longer lasting oauth tokens, and ozone endpoints
1 parent 352bae8 commit 53d8f25

22 files changed

+960
-28
lines changed

.env.prod.sample

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Production Environment Configuration
2+
# This file serves as a template for GitHub Secrets configuration
3+
#
4+
# IMPORTANT: Secrets are injected directly into Docker containers via environment
5+
# variables. No .env files are created on the host system for security.
6+
7+
# Domain Configuration
8+
DOMAIN=
9+
10+
# Container Registry Configuration
11+
FRONTEND_IMAGE=
12+
BACKEND_IMAGE=
13+
14+
# Database Configuration (External PostgreSQL)
15+
PGUSER=
16+
PGPASSWORD=
17+
PGHOST=
18+
PGDATABASE=
19+
PGPORT=
20+
21+
# Security Keys (Store as GitHub Secrets)
22+
ENCRYPTION_KEY=
23+
JWT_SECRET=
24+
25+
# RSKY Configuration
26+
RSKY_FEEDGEN=
27+
RSKY_API_KEY=your_rsky_api_key
28+
29+
# Fixed Configuration (automatically set in deployment)
30+
PORT=
31+
CLIENT_URL=
32+
BASE_URL=
33+
BSKY_BASE_API_URL=https://api.bsky.app
34+
MUTE_LIST_URI=
35+
MUTE_LIST_ADMIN_DID=
36+
RECONCILIATION_INTERVAL_MS=

.env.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
PORT=3000
2+
NODE_ENV=test
3+
PGHOST=db
4+
PGUSER=postgres
5+
PGPASSWORD=testpassword
6+
PGDATABASE=safeskiesdb
7+
PGPORT=5432
8+
JWT_SECRET=test-jwt-secret
9+
CLIENT_URL=http://localhost:3000
10+
BASE_URL=http://localhost:3000
11+
MUTE_LIST_URI=test-uri
12+
MUTE_LIST_ADMIN_DID=test-did
13+
BSKY_BASE_API_URL=https://bsky.social
14+
RSKY_FEEDGEN=http://localhost:8080
15+
RSKY_API_KEY=test-key

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: CI - Validate Changes
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
push:
7+
branches: [main]
8+
9+
jobs:
10+
validate:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: '22'
21+
cache: 'npm'
22+
23+
- name: Install dependencies
24+
run: npm ci
25+
26+
- name: Run type checking
27+
run: npm run type-check

.github/workflows/deploy.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Deploy to Production
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
env:
8+
IMAGE_NAME: safe-skies-api
9+
10+
jobs:
11+
build-and-push:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
packages: write
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Docker Buildx
22+
uses: docker/setup-buildx-action@v3
23+
24+
- name: Install doctl
25+
uses: digitalocean/action-doctl@v2
26+
with:
27+
token: ${{ secrets.DO_REGISTRY_TOKEN }}
28+
29+
- name: Extract metadata
30+
id: meta
31+
uses: docker/metadata-action@v5
32+
with:
33+
images: ${{ secrets.DO_REGISTRY_URL }}
34+
tags: |
35+
type=sha,prefix={{branch}}-
36+
type=raw,value=latest
37+
38+
- name: Log in to DigitalOcean Container Registry with short-lived credentials
39+
run: doctl registry login --expiry-seconds 1200
40+
41+
- name: Build and push image to DigitalOcean Container Registry
42+
uses: docker/build-push-action@v5
43+
with:
44+
context: .
45+
push: true
46+
tags: ${{ steps.meta.outputs.tags }}
47+
labels: ${{ steps.meta.outputs.labels }}
48+
cache-from: type=gha
49+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ RUN chown -R node:node /usr/src/app
2525

2626
USER node
2727
COPY --chown=node:node . .
28-
RUN npm run build
28+
RUN npm run build && npm run build:knexfile
2929

3030
FROM base AS prodrunner
31+
COPY docker-entrypoint.sh ./docker-entrypoint.sh
32+
RUN chmod +x ./docker-entrypoint.sh && sed -i 's/\r$//' ./docker-entrypoint.sh && chown node:node ./docker-entrypoint.sh
3133
USER node
3234
COPY --from=prodbuilder --chown=node:node /usr/src/app/node_modules ./node_modules
3335
COPY --from=prodbuilder --chown=node:node /usr/src/app/dist ./dist
34-
CMD node dist/src/server.js
36+
COPY --from=prodbuilder --chown=node:node /usr/src/app/migrations ./migrations
37+
COPY --from=prodbuilder --chown=node:node /usr/src/app/knexfile.js ./knexfile.js
38+
COPY --from=prodbuilder --chown=node:node /usr/src/app/package.json ./package.json
39+
ENTRYPOINT ["./docker-entrypoint.sh"]

docker-compose.test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
services:
2+
backend:
3+
image: safe-skies-api:test
4+
env_file: .env.test
5+
db:
6+
environment:
7+
- POSTGRES_DB=safeskiesdb
8+
- POSTGRES_PASSWORD=testpassword
9+
healthcheck:
10+
test: ["CMD", "pg_isready"]
11+
interval: 10s
12+
timeout: 5s
13+
retries: 5
14+
start_period: 30s

docker-entrypoint.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/sh
2+
set -e
3+
4+
echo "Running database migrations..."
5+
npm run migrate:up
6+
7+
echo "Starting server..."
8+
exec node dist/src/server.js
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Knex } from "knex";
2+
3+
export async function up(knex: Knex): Promise<void> {
4+
await knex.schema.createTable("oauth_keys", (table) => {
5+
table.text("key_id").primary();
6+
table.text("private_key").notNullable();
7+
table.text("public_key").notNullable();
8+
table.text("algorithm").notNullable().defaultTo("ES256");
9+
table.boolean("is_active").notNullable().defaultTo(true);
10+
table.timestamp("created_at").defaultTo(knex.fn.now());
11+
12+
table.index("is_active", "idx_oauth_keys_active");
13+
table.index("created_at", "idx_oauth_keys_created_at");
14+
});
15+
}
16+
17+
export async function down(knex: Knex): Promise<void> {
18+
await knex.schema.dropTableIfExists("oauth_keys");
19+
}
20+

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
"type-check": "tsc --noEmit",
1717
"format": "biome format . --write",
1818
"prepare": "husky install",
19-
"migrate:create": "knex migrate:make",
20-
"migrate:up": "knex migrate:latest",
21-
"migrate:down": "knex migrate:down"
19+
"migrate:create": "ts-node node_modules/.bin/knex migrate:make",
20+
"migrate:up": "ts-node node_modules/.bin/knex migrate:latest",
21+
"migrate:down": "ts-node node_modules/.bin/knex migrate:down",
22+
"build:knexfile": "tsc knexfile.ts --outDir . --esModuleInterop --module commonjs --target ES2020 --skipLibCheck"
2223
},
2324
"keywords": [],
2425
"author": "",

src/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ app.use((req, res, next) => {
3838
next();
3939
});
4040

41+
app.use('/auth/admin', adminAuthRouter);
4142
app.use('/auth', authRouter);
42-
app.use('/admin/auth', adminAuthRouter);
4343
app.use('/oauth', clientMetadataRouter);
4444
app.use('/api/feeds', feedsRouter);
4545
app.use('/api', profileRouter);

0 commit comments

Comments
 (0)