Skip to content

Commit c685523

Browse files
feat!: migrate from PostgreSQL to SQLite with persistent Docker volume
BREAKING CHANGE: PostgreSQL is no longer supported. SQLite is the sole database provider, configured via DB_PATH env var (default: /data/meetup_bot.db). Database: - Remove postgres branch from get_db_config(), add DB_PATH env var - Enable WAL mode via raw sqlite3 connection after PonyORM mapping - Remove psycopg2-binary and testcontainers[postgres] dependencies Docker: - Rename Dockerfile.web to Dockerfile, delete Dockerfile.dev - Add /data directory with appuser permissions in Dockerfile - Remove postgres service from docker-compose.yml - Replace pgdata volume with db_data:/data for SQLite persistence CI: - Delete smoke-test.yml workflow - Remove DB_HOST, DB_NAME, DB_PORT from pytest.yml env blocks - Update Dockerfile references in docker.yml workflow Tests: - Consolidate 6 unit test files into test_unit.py - Move test_smoke.py into test_integration.py - Rewrite test_e2e.py to use tempfile SQLite instead of PostgresContainer - Add DISABLE_IP_WHITELIST env var and set DEV=false in e2e env to properly test unauthenticated request rejection from localhost - Remove DEV=true workaround from conftest.py, set DB_PATH to temp path Cleanup: - Delete heroku.yml - Update .env.example, README.md, architecture.md, devcontainer.json, taskfiles/docker.yml, taskfiles/pytest.yml Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1c9f1de commit c685523

26 files changed

Lines changed: 990 additions & 1328 deletions

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"name": "Dev Environment",
55
// "build": {
66
// "context": "..",
7-
// "dockerfile": "../Dockerfile.web"
7+
// "dockerfile": "../Dockerfile"
88
// },
99
"service": "meetup-bot",
1010
"workspaceFolder": "/app",

.env.example

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
ALGORITHM=HS256
2-
DEV=false
2+
DB_PATH=/data/meetup_bot.db
33
APP_TOKEN=xapp-apptoken
44
AUTH_BASE_URL=https://secure.meetup.com/oauth2/authorize
55
BOT_USER_TOKEN=xoxb-botusertoken
@@ -10,11 +10,7 @@ CLIENT_ID=securesuperkey
1010
CLIENT_SECRET=secretsecuresuper
1111
CSV_FN=/tmp/output.csv
1212
DAYS=7
13-
DB_HOST=localoraws
14-
DB_NAME=herokuawsstring
1513
DB_PASS=anothercredgoeshere
16-
DB_PORT=5432
17-
DB_URL=ridiculousdbconnection
1814
DB_USER=someuser
1915
ENDPOINT=emoji.list
2016
HEROKU_APP=herokuapp

.github/workflows/docker.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
runs-on: ubuntu-latest
4040
strategy:
4141
matrix:
42-
dockerfile: [Dockerfile.web]
42+
dockerfile: [Dockerfile]
4343
concurrency:
4444
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.ref }}
4545
cancel-in-progress: true

.github/workflows/pytest.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ env:
2626
CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
2727
CSV_FN: ${{ vars.CSV_FN }}
2828
DAYS: ${{ vars.DAYS }}
29-
DB_HOST: ${{ secrets.DB_HOST }}
30-
DB_NAME: ${{ secrets.DB_NAME }}
3129
DB_PASS: ${{ secrets.DB_PASS }}
32-
DB_PORT: ${{ vars.DB_PORT }}
3330
DB_USER: ${{ secrets.DB_USER }}
3431
JSON_FN: ${{ vars.JSON_FN }}
3532
PORT: ${{ vars.PORT }}
@@ -63,10 +60,7 @@ jobs:
6360
CLIENT_SECRET: ${{ env.CLIENT_SECRET }}
6461
CSV_FN: ${{ env.CSV_FN }}
6562
DAYS: ${{ env.DAYS }}
66-
DB_HOST: ${{ env.DB_HOST }}
67-
DB_NAME: ${{ env.DB_NAME }}
6863
DB_PASS: ${{ env.DB_PASS }}
69-
DB_PORT: ${{ env.DB_PORT }}
7064
DB_USER: ${{ env.DB_USER }}
7165
JSON_FN: ${{ env.JSON_FN }}
7266
PORT: ${{ env.PORT }}

.github/workflows/smoke-test.yml

Lines changed: 0 additions & 41 deletions
This file was deleted.

Dockerfile.web renamed to Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ RUN adduser \
8282
--uid "${UID}" \
8383
${USER_NAME}
8484

85+
RUN mkdir -p /data && chown 10001:10001 /data
86+
8587
USER ${USER_NAME}
8688

8789
WORKDIR /app

Dockerfile.dev

Lines changed: 0 additions & 60 deletions
This file was deleted.

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ Use Meetup Pro API to send Slack messages before events occur.
2424
## Minimum Requirements
2525

2626
* [Python 3.11+](https://www.python.org/downloads/)
27-
* [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli)
2827
* [Create a Meetup API key](https://secure.meetup.com/meetup_api/key/)
2928
* Slack
3029
* [Create a Slack app](https://api.slack.com/apps)
@@ -96,7 +95,7 @@ devbox run test
9695
cd ./app
9796

9897
# build image
99-
docker build -f Dockerfile.web --progress=plain -t meetup_bot:latest .
98+
docker build -f Dockerfile --progress=plain -t meetup_bot:latest .
10099

101100
# run image
102101
docker run --name meetup_bot -it --rm --env-file .env -p 3000:3000 meetup_bot bash

app/db.py

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,17 @@ class UserInfo(db.Entity):
2828
email = Optional(str)
2929

3030

31-
def get_db_config(dev=None):
32-
"""Return provider-specific bind kwargs based on DEV env var."""
33-
if dev is None:
34-
dev = config("DEV", default="false").lower() in ("true", "1", "yes")
35-
36-
if dev:
37-
return {
38-
"provider": "sqlite",
39-
"filename": str(APP_DIR / "db.sqlite"),
40-
"create_db": True,
41-
}
42-
31+
def get_db_config():
32+
"""Return SQLite bind kwargs."""
33+
db_path = config("DB_PATH", default="/data/meetup_bot.db")
4334
return {
44-
"provider": "postgres",
45-
"user": config("DB_USER"),
46-
"password": config("DB_PASS").strip('"'),
47-
"host": config("DB_HOST"),
48-
"database": config("DB_NAME"),
49-
"port": config("DB_PORT", default=5432, cast=int),
50-
"sslmode": config("DB_SSLMODE", default="prefer"),
35+
"provider": "sqlite",
36+
"filename": db_path,
37+
"create_db": True,
5138
}
5239

5340

54-
def init_db(dev=None):
41+
def init_db():
5542
"""Bind the shared Database instance and generate mappings.
5643
5744
Safe to call multiple times; subsequent calls are no-ops.
@@ -60,7 +47,14 @@ def init_db(dev=None):
6047
if _initialized:
6148
return
6249

63-
db_config = get_db_config(dev=dev)
50+
db_config = get_db_config()
6451
db.bind(**db_config)
6552
db.generate_mapping(create_tables=True)
53+
54+
import sqlite3
55+
56+
conn = sqlite3.connect(db_config["filename"])
57+
conn.execute("PRAGMA journal_mode=WAL")
58+
conn.close()
59+
6660
_initialized = True

app/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
"""
7676

7777

78+
DISABLE_IP_WHITELIST = config("DISABLE_IP_WHITELIST", default=False, cast=bool)
79+
80+
7881
class IPConfig(BaseModel):
7982
whitelist: list[str] = ["localhost", "127.0.0.1"]
8083
public_ips: list[str] = []
@@ -232,7 +235,7 @@ async def get_current_active_user(current_user: User | None = Depends(get_curren
232235

233236

234237
async def ip_whitelist_or_auth(request: Request, current_user: User | None = Depends(get_current_active_user)):
235-
if DEV or is_ip_allowed(request):
238+
if DEV or (not DISABLE_IP_WHITELIST and is_ip_allowed(request)):
236239
return {"bypass_auth": True}
237240

238241
if current_user is None:

0 commit comments

Comments
 (0)