Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e351584
docs: phase 0 discovery findings (repos, design, IA, confirmations)
skyp1nus Jun 9, 2026
d8805fa
feat(web): scaffold app shell, design system, nav + Overview
skyp1nus Jun 9, 2026
78f1496
feat(web): build all tool pages from design (mock data)
skyp1nus Jun 9, 2026
5cf126d
chore(web): relax React-Compiler lint rules to warn, drop unused import
skyp1nus Jun 9, 2026
fe07830
fix(web): wrap command palette in <Command> (radix-nova CommandDialog…
skyp1nus Jun 9, 2026
ce9011b
feat(backend): scaffold modular-monolith solution (.NET 10)
skyp1nus Jun 9, 2026
00680b2
feat(backend): SharedKernel contracts
skyp1nus Jun 9, 2026
f833c60
feat(backend): Infrastructure + Host + Sample module + auth + migrations
skyp1nus Jun 9, 2026
6d1dc67
feat(ops): docker-compose stack + Dockerfiles + Phase 0 docs
skyp1nus Jun 9, 2026
465f318
feat(web): BFF + auth screens + data-hook seam + CI
skyp1nus Jun 9, 2026
1602d09
fix(auth): edge-safe session cookie const + enrich /me with email
skyp1nus Jun 9, 2026
eed88aa
fix: close phase-0 auth bypass, docker-compose, and arch-test gaps
skyp1nus Jun 9, 2026
85a1067
feat(youtube-uploads): port slacktube app into module on shared kernel
skyp1nus Jun 10, 2026
c589c54
fix(youtube-uploads): close phase-1 audit findings — audit coverage, …
skyp1nus Jun 10, 2026
9d7168b
docs(architecture): reflect current naming (YouTube Uploads / Comment…
skyp1nus Jun 10, 2026
1ad08ca
feat(youtube-comments): fold legacy comment-bridge into module on sha…
skyp1nus Jun 10, 2026
3ecfa63
fix(youtube-comments): close phase-2 audit partials and scrub legacy …
skyp1nus Jun 10, 2026
8611ad2
feat(youtube-comments): wire write-path end-to-end + close audit-leve…
skyp1nus Jun 10, 2026
2f7a9ba
fix(docker): add missing YouTubeComments csproj COPY to backend Docke…
skyp1nus Jun 10, 2026
a4ab2b2
chore(deploy): prod artifacts + gated CI deploy (local prep)
skyp1nus Jun 10, 2026
bf25114
refactor(comments): drop Feed + Channels pages, fold channel-add into…
skyp1nus Jun 11, 2026
b1a45da
feat(connections,logs): wire to real backend data + fix broken OAuth …
skyp1nus Jun 11, 2026
7e80077
feat(uploads): real Add-route dialog + Delete, honest cleanup of iner…
skyp1nus Jun 11, 2026
5633412
feat(settings): trim to Alerts + Danger Zone, honest-disable pending …
skyp1nus Jun 11, 2026
920f8c7
feat(uploads): P0 channel-mapping Active/Pause toggle
skyp1nus Jun 11, 2026
da11c30
feat(uploads,system): server-side filtered CSV export (History + Logs)
skyp1nus Jun 11, 2026
bef7582
feat(system): Settings control panel — Alerts persistence + Danger Zone
skyp1nus Jun 11, 2026
89bf07d
refactor(web): remove dead/faked controls; close-out docs
skyp1nus Jun 11, 2026
3ef80c7
feat(youtube-comments): reject comments from Slack via OAuth write-back
skyp1nus Jun 11, 2026
80fdd5b
fix(deploy): require Comments Slack signing secret in prod + guard fa…
skyp1nus Jun 11, 2026
1372772
fix(youtube-comments): stamp the Slack user as the moderation audit a…
skyp1nus Jun 11, 2026
658ab8e
fix(youtube-comments): gate the Reject button on force-ssl
skyp1nus Jun 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
23 changes: 23 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ── Hookline local stack (docker compose) ─────────────────────────────────
# Copy to .env and fill real secrets for anything beyond local dev.

# Postgres role password (shared by the DB + the backend connection string).
POSTGRES_PASSWORD=hookline

# Backend secrets. Generate strong values for prod: openssl rand -base64 36
TOKEN_ENCRYPTION_KEY=dev-token-encryption-key-change-me # AES master key — NEVER change after launch
IDENTITY_SIGNING_KEY=dev-identity-signing-key-change-me # HMAC key for the BFF→backend identity assertion
BACKEND_ADMIN_TOKEN=dev-admin-token # proves the BFF is calling (X-Admin-Token)
SESSION_SECRET=dev-session-secret-change-me # signs the BFF session cookie

# Fast-dev: backend runs as a dev admin without the BFF token/identity. Development-only —
# it is forced off and refused at boot in Production. Flip to true locally to skip auth.
AUTH_DEV_NO_AUTH=false
NEXT_PUBLIC_NO_AUTH=false

# First-run bootstrap admin (seeded only when the users table is empty).
BOOTSTRAP_ADMIN_EMAIL=admin@hookline.local
BOOTSTRAP_ADMIN_PASSWORD=

# Client-visible backend origin for OAuth "Connect" redirects (build-arg inlined).
NEXT_PUBLIC_BACKEND_URL=http://localhost:8080
130 changes: 130 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: CI

on:
push:
branches: [main, dev, phase-0]
pull_request:

jobs:
backend:
name: Backend (build + arch/unit tests)
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"
- run: dotnet restore Hookline.slnx
- run: dotnet build Hookline.slnx -c Release --no-restore
# The architecture tests run here — an illegal module reference fails this step
# (non-zero exit), which fails the job and blocks the merge.
- name: Test (gates the build on architecture violations)
run: dotnet test Hookline.slnx -c Release --no-build --verbosity normal

frontend:
name: Frontend (build)
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: bun install --frozen-lockfile
- run: bun run build

# ════════════════════════════════════════════════════════════════════════════
# DEPLOY GATE — image build/push + server deploy are DISABLED until you flip it.
#
# >>> ONE-LINE FLIP: on the `docker` job below, change
# if: false
# to
# if: github.ref == 'refs/heads/main'
# That enables image build/push on main; the `deploy` job (needs: [docker])
# then runs automatically on main too. Until you flip it, both are SKIPPED
# and nothing is pushed or deployed — the backend/frontend jobs above still
# run for real on every push/PR and gate correctness.
#
# Before flipping, set repo secrets DEPLOY_HOST + DEPLOY_SSH_KEY, confirm GH_REPO,
# and complete the go-live checklist in deploy/README.md.
# ════════════════════════════════════════════════════════════════════════════
docker:
name: Docker images (build + push to GHCR)
needs: [backend, frontend]
if: false # ← DEPLOY GATE: flip to `github.ref == 'refs/heads/main'` to enable
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & push backend
uses: docker/build-push-action@v6
with:
context: ./backend
file: ./backend/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}/backend:latest
ghcr.io/${{ github.repository }}/backend:${{ github.sha }}
- name: Build & push frontend
uses: docker/build-push-action@v6
with:
context: ./web
file: ./web/Dockerfile
push: true
# NEXT_PUBLIC_BACKEND_URL is inlined into the client bundle at BUILD time
# (the "Connect Slack/Google" buttons navigate the browser to
# ${NEXT_PUBLIC_BACKEND_URL}/{provider}/.../oauth/start). It MUST be the public
# origin, baked here — a runtime env cannot override an inlined value.
build-args: |
NEXT_PUBLIC_BACKEND_URL=https://danielhub.dev
tags: |
ghcr.io/${{ github.repository }}/frontend:latest
ghcr.io/${{ github.repository }}/frontend:${{ github.sha }}

deploy:
name: Deploy (scp compose + Caddy snippet, pull, up -d, reload Caddy)
needs: [docker]
# Inherits the gate: when `docker` is SKIPPED (if: false above), this is skipped too.
# Its own guard keeps deploy to main even after the docker gate is enabled.
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Copy compose + Caddy site block to server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.DEPLOY_HOST }}
username: deploy
key: ${{ secrets.DEPLOY_SSH_KEY }}
source: "deploy/docker-compose.prod.yml,deploy/Caddyfile"
target: "/opt/hookline"
strip_components: 1
- name: Pull, restart, install + reload shared Caddy
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.DEPLOY_HOST }}
username: deploy
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: |
set -e
cd /opt/hookline
export IMAGE_TAG="${{ github.sha }}"
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
# Publish this app's Caddy site block into the shared Caddy, then hot-reload.
install -D -m 644 Caddyfile /opt/shared/sites/hookline.caddy
docker exec shared-caddy-1 caddy reload --config /etc/caddy/Caddyfile
docker image prune -f
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Design handoff bundle (reference only, not source)
/design/

# Dependencies / build artifacts
**/node_modules/
**/.next/
**/out/
**/build/
*.tsbuildinfo

# Env
**/.env
**/.env.local
**/.env.*.local
!**/.env.example

# Local Claude settings
.claude/settings.local.json

# OS
.DS_Store

# Local prod-profile smoke harness (holds TEST secrets — never commit)
deploy/local/
11 changes: 11 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"mcpServers": {
"shadcn": {
"command": "npx",
"args": [
"shadcn@latest",
"mcp"
]
}
}
}
6 changes: 6 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
**/bin/
**/obj/
.git/
.vs/
**/*.user
[Tt]est[Rr]esults/
6 changes: 6 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
bin/
obj/
*.user
.vs/
[Tt]est[Rr]esults/
*.tsbuildinfo
8 changes: 8 additions & 0 deletions backend/.idea/.idea.Hookline/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/.idea/.idea.Hookline/.idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions backend/.idea/.idea.Hookline/.idea/encodings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions backend/.idea/.idea.Hookline/.idea/indexLayout.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions backend/.idea/.idea.Hookline/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions backend/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>
34 changes: 34 additions & 0 deletions backend/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- EF Core 10 + Postgres -->
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.8" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageVersion Include="EFCore.NamingConventions" Version="10.0.1" />
<!-- Background jobs -->
<PackageVersion Include="Hangfire.AspNetCore" Version="1.8.23" />
<PackageVersion Include="Hangfire.Core" Version="1.8.23" />
<PackageVersion Include="Hangfire.PostgreSql" Version="1.21.1" />
<!-- Google APIs (YouTube Uploads module: Drive download + YouTube upload) -->
<PackageVersion Include="Google.Apis.Auth" Version="1.74.0" />
<PackageVersion Include="Google.Apis.Drive.v3" Version="1.74.0.4135" />
<PackageVersion Include="Google.Apis.YouTube.v3" Version="1.74.0.4162" />
<!-- Redis -->
<PackageVersion Include="StackExchange.Redis" Version="2.13.17" />
<!-- Logging -->
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
<!-- Auth -->
<PackageVersion Include="BCrypt.Net-Next" Version="4.0.3" />
<!-- Test stack -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.4" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="NetArchTest.Rules" Version="1.3.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.8" />
</ItemGroup>
</Project>
30 changes: 30 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# ── build ──────────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

# Restore first (cache layer) — copy only the project graph + central config.
COPY global.json Directory.Build.props Directory.Packages.props Hookline.slnx ./
COPY src/Hookline.Host/Hookline.Host.csproj src/Hookline.Host/
COPY src/Hookline.SharedKernel/Hookline.SharedKernel.csproj src/Hookline.SharedKernel/
COPY src/Hookline.Infrastructure/Hookline.Infrastructure.csproj src/Hookline.Infrastructure/
COPY src/Modules/Hookline.Modules.YouTubeUploads/Hookline.Modules.YouTubeUploads.csproj src/Modules/Hookline.Modules.YouTubeUploads/
COPY src/Modules/Hookline.Modules.YouTubeComments/Hookline.Modules.YouTubeComments.csproj src/Modules/Hookline.Modules.YouTubeComments/
COPY tests/Hookline.ArchitectureTests/Hookline.ArchitectureTests.csproj tests/Hookline.ArchitectureTests/
RUN dotnet restore src/Hookline.Host/Hookline.Host.csproj

COPY . .
RUN dotnet publish src/Hookline.Host/Hookline.Host.csproj -c Release -o /app --no-restore

# ── runtime ────────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
# Kerberos/GSSAPI lib so Npgsql doesn't log a (harmless) probe error on startup.
RUN apt-get update \
&& apt-get install -y --no-install-recommends libgssapi-krb5-2 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /app .

ENV ASPNETCORE_URLS=http://+:8080 \
DOTNET_gcServer=0
EXPOSE 8080
ENTRYPOINT ["dotnet", "Hookline.Host.dll"]
17 changes: 17 additions & 0 deletions backend/Hookline.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/Hookline.Host/Hookline.Host.csproj" />
<Project Path="src/Hookline.Infrastructure/Hookline.Infrastructure.csproj" />
<Project Path="src/Hookline.SharedKernel/Hookline.SharedKernel.csproj" />
</Folder>
<Folder Name="/src/Modules/">
<Project Path="src/Modules/Hookline.Modules.YouTubeUploads/Hookline.Modules.YouTubeUploads.csproj" />
<Project Path="src/Modules/Hookline.Modules.YouTubeComments/Hookline.Modules.YouTubeComments.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Hookline.ArchitectureTests/Hookline.ArchitectureTests.csproj" />
<Project Path="tests/Hookline.Infrastructure.Tests/Hookline.Infrastructure.Tests.csproj" />
<Project Path="tests/Hookline.Modules.YouTubeUploads.Tests/Hookline.Modules.YouTubeUploads.Tests.csproj" />
<Project Path="tests/Hookline.Modules.YouTubeComments.Tests/Hookline.Modules.YouTubeComments.Tests.csproj" />
</Folder>
</Solution>
13 changes: 13 additions & 0 deletions backend/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "10.0.8",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}
6 changes: 6 additions & 0 deletions backend/global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestFeature"
}
}
Loading
Loading