1 Commits

Author SHA1 Message Date
457722a79c Set Docker host for deployment workflow 2026-02-23 12:39:33 -08:00
14 changed files with 870 additions and 447 deletions

View File

@@ -1,47 +0,0 @@
name: "Deploy to Coolify"
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
with:
submodules: recursive
- name: Login to Gitea Container Registry
uses: docker/login-action@v4
with:
registry: gitea.bchen.dev
username: ${{ github.actor }}
password: ${{ secrets.PAT }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: gitea.bchen.dev/${{ github.actor }}/project-inter-server:latest
- name: Set up Tailscale
uses: tailscale/github-action@v4
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tags: tag:ci
use-cache: false
tailscaled-args: --tun=userspace-networking --socks5-server=localhost:1055 --outbound-http-proxy-listen=localhost:1055
- name: Deploy to Coolify
run: |
curl --request POST '${{ secrets.COOLIFY_WEBHOOK }}' --header 'Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}'

60
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: "Deploy to birb co."
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
# See documentation: https://github.com/tailscale/github-action?tab=readme-ov-file
- name: Connect to Tailscale
uses: tailscale/github-action@v4
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_CLIENT_SECRET }}
tags: tag:ci
- name: Set up SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan ${{ vars.HOST }} >> ~/.ssh/known_hosts
- name: Remove directory from server
run: |
ssh -i ~/.ssh/id_ed25519 ${{ vars.USERNAME }}@${{ vars.HOST }} << 'EOF'
rm -rf ~/${{ vars.DIRECTORY_NAME }}
EOF
# Avoid needing to set up SSH access to GitHub for this user
- name: Transfer repository files to server
run: |
scp -i ~/.ssh/id_ed25519 -r ./* ${{ vars.USERNAME }}@${{ vars.HOST }}:~/${{ vars.DIRECTORY_NAME }}
- name: Deploy on server with Docker
run: |
ssh -i ~/.ssh/id_ed25519 ${{ vars.USERNAME }}@${{ vars.HOST }} << 'EOF'
cd ~/${{ vars.DIRECTORY_NAME }}
export APNS_IS_PRODUCTION=${{ secrets.APNS_IS_PRODUCTION }}
export APNS_BUNDLE_ID=${{ secrets.APNS_BUNDLE_ID }}
export APNS_TEAM_ID=${{ secrets.APNS_TEAM_ID }}
export APNS_KEY_ID=${{ secrets.APNS_KEY_ID }}
export APNS_PRIVATE_KEY=${{ secrets.APNS_PRIVATE_KEY }}
export PARKING_LOGGING_INTERVAL_MS=${{ secrets.PARKING_LOGGING_INTERVAL_MS }}
export RATE_LIMITS_DISABLED=${{ secrets.RATE_LIMITS_DISABLED }}
export RATE_LIMIT_WINDOW_MS=${{ secrets.RATE_LIMIT_WINDOW_MS }}
export RATE_LIMIT_DELAY_AFTER_REQUESTS=${{ secrets.RATE_LIMIT_DELAY_AFTER_REQUESTS }}
export RATE_LIMIT_DELAY_MULTIPLIER_MS=${{ secrets.RATE_LIMIT_DELAY_MULTIPLIER_MS }}
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker compose -f docker-compose.prod.yml down --remove-orphans
docker compose -f docker-compose.prod.yml up -d --build
EOF

4
Caddyfile Normal file
View File

@@ -0,0 +1,4 @@
interchange-api.bchen.dev {
reverse_proxy app:4000
}

View File

@@ -1,7 +1,4 @@
FROM node:22-alpine
WORKDIR /usr/src/app
COPY . .
RUN npm run build
EXPOSE 4000
CMD ["npm", "run", "start"]

View File

@@ -19,8 +19,6 @@ services:
build: .
command: npm run start
restart: unless-stopped
ports:
- "4000:4000"
depends_on:
- redis
environment:
@@ -36,6 +34,18 @@ services:
- ./redis-stack.conf:/redis-stack.conf
command: redis-stack-server /redis-stack.conf
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
volumes:
redis_data:
caddy_data:

1115
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"build:dev": "npm install --include=dev && npm run generate && tsc --project tsconfig.build.json",
"build": "npm install --include=dev && npm run generate && tsc --project tsconfig.build.json && npm prune --omit=dev",
"start:dev": "npm run build:dev && node ./dist/index.js",
"start": "node ./dist/index.js",
"start": "npm run build && node ./dist/index.js",
"generate": "graphql-codegen --config codegen.ts",
"test": "npm run build:dev && jest --runInBand"
},
@@ -26,12 +26,12 @@
},
"private": true,
"dependencies": {
"@apollo/server": "^5.0.0",
"@apollo/server": "^4.11.2",
"@as-integrations/express5": "^1.1.2",
"express": "^5.1.0",
"express-rate-limit": "^8.0.1",
"express-slow-down": "^3.0.0",
"graphql": "^16.11.0",
"graphql": "^16.10.0",
"jsonwebtoken": "^9.0.2",
"redis": "^4.7.0"
},

View File

@@ -6,7 +6,6 @@ scalar DateTime
type System {
id: ID!
name: String!
initialRegion: Region
routes: [Route!]
route(id: ID): Route
stops: [Stop!]
@@ -17,11 +16,6 @@ type System {
parkingSystem: ParkingSystem
}
type Region {
topLeft: Coordinates!
bottomRight: Coordinates!
}
type ParkingSystem {
systemId: ID!
parkingStructures: [ParkingStructure!]

View File

@@ -19,7 +19,6 @@ import { InMemoryExternalSourceETARepository } from "../repositories/shuttle/eta
import { ETAGetterRepository } from "../repositories/shuttle/eta/ETAGetterRepository";
import { InMemorySelfUpdatingETARepository } from "../repositories/shuttle/eta/InMemorySelfUpdatingETARepository";
import { BaseInMemoryETARepository } from "../repositories/shuttle/eta/BaseInMemoryETARepository";
import { IRegion } from "./SharedEntities";
export interface InterchangeSystemBuilderArguments {
name: string;
@@ -50,12 +49,6 @@ export interface InterchangeSystemBuilderArguments {
* at a stop, in latitude/longitude degrees.
*/
shuttleStopArrivalDegreeDelta: number;
/**
* The initial map region to display when the app first loads
* this system. Represents a center coordinate and span.
*/
initialRegion?: IRegion;
}
export class InterchangeSystem {
@@ -69,7 +62,6 @@ export class InterchangeSystem {
public notificationRepository: NotificationRepository,
public parkingTimedDataLoader: TimedApiBasedRepositoryLoader | null,
public parkingRepository: ParkingGetterSetterRepository | null,
public initialRegion: IRegion | null,
) {
}
@@ -104,7 +96,6 @@ export class InterchangeSystem {
notificationRepository,
timedParkingLoader,
parkingRepository,
args.initialRegion ?? null,
);
}
@@ -214,7 +205,6 @@ export class InterchangeSystem {
notificationRepository,
timedParkingLoader,
parkingRepository,
args.initialRegion ?? null,
);
}

View File

@@ -11,8 +11,3 @@ export interface ICoordinates {
longitude: number;
}
export interface IRegion {
topLeft: ICoordinates;
bottomRight: ICoordinates;
}

View File

@@ -25,10 +25,6 @@ const supportedSystems: InterchangeSystemBuilderArguments[] = [
name: "Chapman University",
useSelfUpdatingEtas: true,
shuttleStopArrivalDegreeDelta: 0.001,
initialRegion: {
topLeft: { latitude: 33.85733, longitude: -117.89553 },
bottomRight: { latitude: 33.73970, longitude: -117.81878 },
},
}
]

View File

@@ -83,14 +83,6 @@ export const SystemResolvers: Resolvers<ServerContext> = {
const shuttles = await system.shuttleRepository.getShuttles();
return shuttles.slice().sort((a, b) => a.name.localeCompare(b.name));
},
initialRegion: async (parent, _args, contextValue, _info) => {
const system = contextValue.findSystemById(parent.id);
if (!system) {
return null;
}
return system.initialRegion;
},
parkingSystem: async (parent, _args, contextValue, _info) => {
const system = contextValue.findSystemById(parent.id);
if (!system) {

View File

@@ -35,37 +35,6 @@ describe("SystemResolvers", () => {
});
}
describe("initialRegion", () => {
const query = `
query GetSystemInitialRegion($systemId: ID!) {
system(id: $systemId) {
initialRegion {
topLeft {
latitude
longitude
}
bottomRight {
latitude
longitude
}
}
}
}
`;
it("returns the initial region for the system", async () => {
const response = await getResponseFromQueryNeedingSystemId(query);
assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined();
const initialRegion = (response.body.singleResult.data as any).system.initialRegion;
expect(initialRegion).toEqual({
topLeft: { latitude: 33.85733, longitude: -117.89553 },
bottomRight: { latitude: 33.7397, longitude: -117.81878 },
});
});
});
describe("routes", () => {
const query = `
query GetSystemRoutes($systemId: ID!) {

View File

@@ -26,10 +26,6 @@ const systemInfoForTesting: InterchangeSystemBuilderArguments = {
parkingSystemId: ChapmanApiBasedParkingRepositoryLoader.id,
useSelfUpdatingEtas: false,
shuttleStopArrivalDegreeDelta: 0.001,
initialRegion: {
topLeft: { latitude: 33.85733, longitude: -117.89553 },
bottomRight: { latitude: 33.73970, longitude: -117.81878 },
},
};
export function buildSystemForTesting() {