Skip to content

feat: Adds flyway database migrations to board server #2975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions packages/board-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ RUN npm run build
# Production stage
FROM node:20-slim

ARG STORAGE_BACKEND
ARG STORAGE_BACKEND="sqlite"
ARG ALLOWED_ORIGINS=""
ARG SQLITE_DB_PATH="board-server.db"

ENV NODE_ENV=production
ENV STORAGE_BACKEND="${STORAGE_BACKEND}"
ENV ALLOWED_ORIGINS="${ALLOWED_ORIGINS}"
ENV SQLITE_DB_PATH="${SQLITE_DB_PATH}"

WORKDIR /app

Expand All @@ -30,9 +32,24 @@ COPY --from=build /build/packages/board-server/src ./src
COPY --from=build /build/packages/board-server/package.json ./
COPY --from=build /build/packages/board-server/public ./public

# Install necessary tools, Java, and Flyway CLI, then copy migration scripts if STORAGE_BACKEND is sqlite
RUN apt-get update && apt-get install -y wget tar default-jre && \
if [ "$STORAGE_BACKEND" = "sqlite" ]; then \
wget -qO- https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/9.8.1/flyway-commandline-9.8.1-linux-x64.tar.gz | tar xvz && \
ln -s /app/flyway-9.8.1/flyway /usr/local/bin && \
mkdir -p /flyway/sql && \
cp -r /app/src/migrations/* /flyway/sql/; \
fi && \
apt-get clean && rm -rf /var/lib/apt/lists/*

# Install production dependencies and tsx
RUN npm install --only=production && \
npm install -g tsx

EXPOSE 3000
CMD ["node", "dist/server/index.js", "--host=0.0.0.0" ]

# Run Flyway migrate before starting the application if STORAGE_BACKEND is sqlite
CMD if [ "$STORAGE_BACKEND" = "sqlite" ]; then \
flyway -url="jdbc:sqlite:${SQLITE_DB_PATH}" -locations=filesystem:/flyway/sql migrate; \
fi && \
node dist/server/index.js --host=0.0.0.0
20 changes: 19 additions & 1 deletion packages/board-server/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Breadboard Board Server Reference Implementation

## Getting started

To run the board server locally with SQLite backend:
```
export GOOGLE_APPLICATION_CREDENTIALS=n/a
export STORAGE_BACKEND=sqlite
npm run migrate
npm run dev
```

## Running tests

To run the tests:
Expand All @@ -21,6 +31,14 @@ export STORAGE_BACKEND=sqlite
export SQLITE_DB_PATH=/path/to/board-server.db
```

## Initialize the database for local SQLite development

In `sqlite` mode, the board server uses Flyway for database migrations. To initialize the database using docker, run the following command:

```
npm run migrate
```

## Building with docker

The board server can be run as a self-contained docker image.
Expand Down Expand Up @@ -56,7 +74,7 @@ docker build --platform linux/amd64 ...
To run the container:

```
docker run -d -p 3000:3000 --name board-server board-server
docker run -d -p 3000:3000 --name board-server board-server:sqlite
docker exec -it board-server /bin/bash
# npm run add <username> # add a user and copy your API key
```
Expand Down
3 changes: 2 additions & 1 deletion packages/board-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"dev": "npm run dev:nowatch --watch",
"dev:nowatch": "wireit",
"test": "wireit",
"test:integration": "wireit"
"test:integration": "wireit",
"migrate": "docker run --rm -v \"$(pwd):/flyway/sql\" -v \"$(pwd)/src/migrations:/flyway/sql/migrations\" flyway/flyway -url=jdbc:sqlite:/flyway/sql/board-server.db -locations=filesystem:/flyway/sql/migrations migrate"
},
"wireit": {
"build": {
Expand Down
41 changes: 41 additions & 0 deletions packages/board-server/src/migrations/V1__initial_schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- V1__initial_schema.sql
CREATE TABLE IF NOT EXISTS reanimation_states (
id TEXT PRIMARY KEY,
user TEXT NOT NULL,
state TEXT NOT NULL,
timestamp INTEGER NOT NULL,
expire_at INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
api_key TEXT NOT NULL UNIQUE
);

CREATE TABLE IF NOT EXISTS workspaces (
id TEXT PRIMARY KEY
);

CREATE TABLE IF NOT EXISTS boards (
workspace_id TEXT,
board_id TEXT,
title TEXT,
tags TEXT,
graph TEXT,
PRIMARY KEY (workspace_id, board_id),
FOREIGN KEY (workspace_id) REFERENCES workspaces(id)
);

CREATE TABLE IF NOT EXISTS invites (
workspace_id TEXT,
board_id TEXT,
invite TEXT,
expire_at INTEGER,
PRIMARY KEY (workspace_id, board_id, invite),
FOREIGN KEY (workspace_id, board_id) REFERENCES boards(workspace_id, board_id)
);

CREATE TABLE IF NOT EXISTS configuration (
key TEXT PRIMARY KEY,
value TEXT
);
44 changes: 0 additions & 44 deletions packages/board-server/src/server/storage-providers/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,6 @@ export class SQLiteStorageProvider implements RunBoardStateStore, BoardServerSto

constructor(dbPath: string) {
this.db = new Database(dbPath);

// Initialize tables
this.db.exec(`
CREATE TABLE IF NOT EXISTS reanimation_states (
id TEXT PRIMARY KEY,
user TEXT NOT NULL,
state TEXT NOT NULL,
timestamp INTEGER NOT NULL,
expire_at INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
api_key TEXT NOT NULL UNIQUE
);

CREATE TABLE IF NOT EXISTS workspaces (
id TEXT PRIMARY KEY
);

CREATE TABLE IF NOT EXISTS boards (
workspace_id TEXT,
board_id TEXT,
title TEXT,
tags TEXT,
graph TEXT,
PRIMARY KEY (workspace_id, board_id),
FOREIGN KEY (workspace_id) REFERENCES workspaces(id)
);

CREATE TABLE IF NOT EXISTS invites (
workspace_id TEXT,
board_id TEXT,
invite TEXT,
expire_at INTEGER,
PRIMARY KEY (workspace_id, board_id, invite),
FOREIGN KEY (workspace_id, board_id) REFERENCES boards(workspace_id, board_id)
);

CREATE TABLE IF NOT EXISTS configuration (
key TEXT PRIMARY KEY,
value TEXT
);
`);
}

async saveReanimationState(user: string, state: ReanimationState): Promise<string> {
Expand Down