Private
Public Access
Rename project from KeepItTalking to DS Chat
Renames the app's display name everywhere (page titles, PWA manifest, TopBar, email subject lines, HMAC signature header) and its internal technical slug from chatapp to ds-chat/ds_chat: the Python package name and console script, the systemd unit and its user/group/paths, the deploy scripts, the Docker container names, and the Postgres database name. The live dev Postgres role stays "chatapp" -- renaming a role requires disconnecting the session using it, which needed a temporary superuser role Claude's auto-mode classifier correctly declined to create unsupervised. Functionally invisible (it's just a login credential), but worth knowing about if this ever needs fully cleaning up by hand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
DATABASE_URL=postgresql+asyncpg://chatapp:chatapp@localhost:5432/chatapp
|
||||
DATABASE_URL=postgresql+asyncpg://ds_chat:ds_chat@localhost:5432/ds_chat
|
||||
SESSION_SECRET=change-me-to-a-long-random-string
|
||||
SESSION_HTTPS_ONLY=false
|
||||
REDIS_URL=redis://localhost:6379/0
|
||||
|
||||
+8
-8
@@ -1,4 +1,4 @@
|
||||
# KeepItTalking backend (Phase 1 + 2 + 4 + 5 + 6 + 7 + 8, image uploads, file attachments, admin-configurable upload size limits, emoji & reactions, user profiles, site invites & email, password reset)
|
||||
# DS Chat backend (Phase 1 + 2 + 4 + 5 + 6 + 7 + 8, image uploads, file attachments, admin-configurable upload size limits, emoji & reactions, user profiles, site invites & email, password reset)
|
||||
|
||||
FastAPI + SQLAlchemy 2.0 (async) + PostgreSQL + Redis. Implements auth, room
|
||||
CRUD (open and private), room roles (owner/admin/member) and direct
|
||||
@@ -24,15 +24,15 @@ Accounts are created by an operator on the app server — see step 4 below.
|
||||
Any local Postgres 14+ works. The quickest option is a container:
|
||||
|
||||
```bash
|
||||
docker run -d --name chatapp-postgres \
|
||||
-e POSTGRES_USER=chatapp -e POSTGRES_PASSWORD=chatapp -e POSTGRES_DB=chatapp \
|
||||
docker run -d --name ds-chat-postgres \
|
||||
-e POSTGRES_USER=ds_chat -e POSTGRES_PASSWORD=ds_chat -e POSTGRES_DB=ds_chat \
|
||||
-p 5432:5432 postgres:16-alpine
|
||||
```
|
||||
|
||||
Then create the test database (used by the test suite, kept separate from dev data):
|
||||
|
||||
```bash
|
||||
docker exec chatapp-postgres psql -U chatapp -d chatapp -c "CREATE DATABASE chatapp_test;"
|
||||
docker exec ds-chat-postgres psql -U ds_chat -d ds_chat -c "CREATE DATABASE ds_chat_test;"
|
||||
```
|
||||
|
||||
(Docker here is purely a local-dev convenience for standing up Postgres quickly —
|
||||
@@ -44,7 +44,7 @@ Used for cross-instance WebSocket fan-out and presence (see the section
|
||||
below). Required — there's no in-memory fallback.
|
||||
|
||||
```bash
|
||||
docker run -d --name chatapp-redis -p 6379:6379 redis:7-alpine
|
||||
docker run -d --name ds-chat-redis -p 6379:6379 redis:7-alpine
|
||||
```
|
||||
|
||||
### 3. Python environment
|
||||
@@ -99,13 +99,13 @@ connected to the other, purely via Redis.
|
||||
|
||||
### 8. Run tests
|
||||
|
||||
Tests run against a real Postgres database (`chatapp_test` by default — native
|
||||
Tests run against a real Postgres database (`ds_chat_test` by default — native
|
||||
`ENUM`/`UUID` types aren't faithfully reproduced by SQLite) and a real Redis
|
||||
(db 15 by default, kept separate from dev use of db 0), with each test
|
||||
wrapped in a transaction that's rolled back afterward:
|
||||
|
||||
```bash
|
||||
DATABASE_URL=postgresql+asyncpg://chatapp:chatapp@localhost:5432/chatapp_test .venv/bin/pytest
|
||||
DATABASE_URL=postgresql+asyncpg://ds_chat:ds_chat@localhost:5432/ds_chat_test .venv/bin/pytest
|
||||
```
|
||||
|
||||
## Layout
|
||||
@@ -247,7 +247,7 @@ call sites rather than duplicated).
|
||||
**Outgoing webhooks / event subscriptions** (`POST
|
||||
/api/rooms/{id}/event-subscriptions`, room-admin managed; room-scoped or
|
||||
global via `room_id=null`): fires an HMAC-SHA256-signed POST
|
||||
(`X-KeepItTalking-Signature: sha256=...`) on `message.created`/
|
||||
(`X-DS-Chat-Signature: sha256=...`) on `message.created`/
|
||||
`message.updated`, delivered via a backgrounded `asyncio.create_task`
|
||||
(`app/services/webhook_delivery.py`) — safe to background here, unlike the
|
||||
Phase 4 push lesson, since there's no DB session involved, just the
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ path_separator = os
|
||||
# database URL. This is consumed by the user-maintained env.py script only.
|
||||
# other means of configuring database URLs may be customized within the env.py
|
||||
# file.
|
||||
sqlalchemy.url = postgresql+asyncpg://chatapp:chatapp@localhost:5432/chatapp
|
||||
sqlalchemy.url = postgresql+asyncpg://chatapp:chatapp@localhost:5432/ds_chat
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
|
||||
+3
-3
@@ -18,8 +18,8 @@ from app.ws.connection_manager import ConnectionManager
|
||||
from app.ws.presence import Presence
|
||||
|
||||
# backend/app/main.py -> backend/ -> repo root -- matches both the local
|
||||
# monorepo layout and the production layout (/srv/chatapp/backend,
|
||||
# /srv/chatapp/frontend/dist), which is the same relative shape.
|
||||
# monorepo layout and the production layout (/srv/ds-chat/backend,
|
||||
# /srv/ds-chat/frontend/dist), which is the same relative shape.
|
||||
FRONTEND_DIST = pathlib.Path(__file__).resolve().parent.parent.parent / "frontend" / "dist"
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
app = FastAPI(title="KeepItTalking", lifespan=lifespan)
|
||||
app = FastAPI(title="DS Chat", lifespan=lifespan)
|
||||
|
||||
app.add_middleware(
|
||||
SessionMiddleware,
|
||||
|
||||
@@ -58,6 +58,6 @@ async def send_test_email(db: AsyncSession, to_address: str) -> None:
|
||||
await _deliver(
|
||||
cfg,
|
||||
to_address,
|
||||
"KeepItTalking test email",
|
||||
"This is a test email from KeepItTalking to confirm your SMTP settings are working.",
|
||||
"DS Chat test email",
|
||||
"This is a test email from DS Chat to confirm your SMTP settings are working.",
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ async def request_password_reset(db: AsyncSession, email: str, base_url: str) ->
|
||||
await send_email(
|
||||
db,
|
||||
email,
|
||||
"Reset your KeepItTalking password",
|
||||
"Reset your DS Chat password",
|
||||
f"Someone requested a password reset for this account.\n\n"
|
||||
f"Reset it here:\n{reset_link}\n\n"
|
||||
f"This link expires in 15 minutes. If you didn't request this, "
|
||||
|
||||
@@ -140,7 +140,7 @@ async def add_member(
|
||||
db,
|
||||
target.email,
|
||||
f"You've been added to #{room.name}",
|
||||
f"You've been added to the #{room.name} room on KeepItTalking.\n\n"
|
||||
f"You've been added to the #{room.name} room on DS Chat.\n\n"
|
||||
f"Open the app: {base_url.rstrip('/')}",
|
||||
)
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ async def create_site_invite(
|
||||
await send_email(
|
||||
db,
|
||||
email,
|
||||
"You're invited to join KeepItTalking",
|
||||
f"You've been invited to join KeepItTalking by {actor.username}.\n\n"
|
||||
"You're invited to join DS Chat",
|
||||
f"You've been invited to join DS Chat by {actor.username}.\n\n"
|
||||
f"Set up your account here:\n{signup_link}\n\n"
|
||||
f"This link expires in 7 days.",
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ async def deliver_event(subscription: EventSubscription, event_type: str, payloa
|
||||
content=body,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-KeepItTalking-Signature": f"sha256={signature}",
|
||||
"X-DS-Chat-Signature": f"sha256={signature}",
|
||||
},
|
||||
)
|
||||
except httpx.HTTPError:
|
||||
|
||||
@@ -6,7 +6,7 @@ from PIL import Image, UnidentifiedImageError
|
||||
|
||||
# backend/app/storage.py -> backend/ -> repo root -- same
|
||||
# resolve-relative-to-file convention FRONTEND_DIST uses in app/main.py, so
|
||||
# this lands in the right place in both local dev and the /srv/chatapp
|
||||
# this lands in the right place in both local dev and the /srv/ds-chat
|
||||
# production layout with zero new config.
|
||||
UPLOADS_DIR = pathlib.Path(__file__).resolve().parent.parent.parent / "uploads"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "chatapp"
|
||||
name = "ds-chat"
|
||||
version = "0.1.0"
|
||||
description = "KeepItTalking chat service backend"
|
||||
description = "DS Chat backend service"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi>=0.115",
|
||||
@@ -25,7 +25,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
chatapp-create-user = "app.cli:main"
|
||||
ds-chat-create-user = "app.cli:main"
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
from pathlib import Path
|
||||
|
||||
os.environ.setdefault(
|
||||
"DATABASE_URL", "postgresql+asyncpg://chatapp:chatapp@localhost:5432/chatapp_test"
|
||||
"DATABASE_URL", "postgresql+asyncpg://chatapp:chatapp@localhost:5432/ds_chat_test"
|
||||
)
|
||||
os.environ.setdefault("SESSION_SECRET", "test-secret")
|
||||
os.environ.setdefault("SESSION_HTTPS_ONLY", "false")
|
||||
|
||||
@@ -31,7 +31,7 @@ async def test_subscribe_creates_row(client, db_session):
|
||||
resp = await client.post("/api/push/subscribe", json=payload)
|
||||
assert resp.status_code == 204
|
||||
|
||||
# The chatapp_test database is shared across the whole suite and the
|
||||
# The ds_chat_test database is shared across the whole suite and the
|
||||
# ws_client-based tests below intentionally don't roll back (see
|
||||
# conftest.ws_client), so a unique endpoint keeps this test independent
|
||||
# of leftover rows from those instead of asserting on the total count.
|
||||
|
||||
@@ -158,7 +158,7 @@ async def test_outgoing_webhook_delivers_signed_payload(client, db_session, monk
|
||||
assert len(posts) == 1
|
||||
body = posts[0]["content"]
|
||||
expected_signature = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
|
||||
assert posts[0]["headers"]["X-KeepItTalking-Signature"] == f"sha256={expected_signature}"
|
||||
assert posts[0]["headers"]["X-DS-Chat-Signature"] == f"sha256={expected_signature}"
|
||||
payload = json.loads(body)
|
||||
assert payload["event"] == "message.created"
|
||||
assert payload["data"]["content"] == "ping"
|
||||
|
||||
Reference in New Issue
Block a user