Some checks failed
deploy-site / deploy (push) Failing after 4m9s
Installers could not be published at all: the signing certificate does not exist yet, and the release pipelines stop at their signing guard. Users had no way to install a fixed build, so the product was effectively stuck behind a certificate that takes weeks to obtain. There is also a second, independent blocker: the download feed sits behind Cloudflare, which rejects any upload body over about 100 MiB, and the app with its speech engine exceeds that even when signed. A portable channel now publishes what can actually be delivered today: the app compressed into 95 MiB 7z volumes (162 MiB total instead of 243 MiB), a Scoop bucket for a normal install and uninstall experience, and a verifiable manual installer script. It is deliberately separate from the auto-update feed, needs no certificate, and refuses to overwrite an already published version.
227 lines
16 KiB
Markdown
227 lines
16 KiB
Markdown
# 02 — Repository & Infrastructure Map
|
|
|
|
> Surface: whole repo
|
|
> Source of truth for: layout, workspaces, build/test commands, CI/CD, Docker, deploy, scripts, resources
|
|
|
|
---
|
|
|
|
## 1. Repository layout
|
|
|
|
```
|
|
D:/workspace/D3ROVoice
|
|
├── apps/
|
|
│ ├── desktop/ Electron app (npm workspace @d3ro/desktop)
|
|
│ ├── web/ Next.js console (npm workspace @d3ro/web)
|
|
│ ├── admin/ Next.js back office (npm workspace @d3ro/admin)
|
|
│ ├── mobile-rn/ React Native product mobile app (NOT an npm workspace)
|
|
│ ├── api-server/ ASP.NET Core 10 API (D3ROVoice.Api)
|
|
│ ├── api-server.Tests/ xUnit tests for the API
|
|
│ └── admin-swagger/ Static Swagger UI + openapi.json
|
|
├── packages/ Shared TS packages (npm workspaces)
|
|
│ ├── core/ @d3ro/core — types, errors, IPC channels, crypto, utils
|
|
│ ├── ui/ @d3ro/ui — web/desktop design system (MUI + tokens)
|
|
│ ├── ui-native/ @d3ro/ui-native — React Native design system
|
|
│ ├── i18n/ @d3ro/i18n — 12 locales, provider, formatters
|
|
│ └── api-client/ @d3ro/api-client — Supabase wrapper + shared types
|
|
├── server/
|
|
│ ├── supabase/ Supabase project: config.toml, migrations/, functions/, tests/
|
|
│ └── cloudflare-worker/ Edge gateway (wrangler.toml + src/index.ts)
|
|
├── site/ Vite landing site (deployed to Cloudflare Pages + GitHub Pages)
|
|
├── resources/ icons/ (empty), sox/ (bundled Windows SoX binaries)
|
|
├── release/ Version identity SSOT + license/evidence public keys
|
|
├── scripts/ ~145 automation scripts + scripts/ci/ (31) + scripts/lib/
|
|
├── docs/ Design, phases, v2/v3 plans, deployment, map/ (this map)
|
|
├── memory/ Project status, handoffs, archive
|
|
├── tests/e2e/, test-results/ Root-level e2e + last-run artifacts
|
|
├── scratch/ Large local evidence (APKs, screenshots, DBs) — not build input
|
|
├── supabase/ Empty scaffolding (.branches/, snippets/) — real project is server/supabase
|
|
├── .github/workflows/ GitHub Actions (6)
|
|
├── .gitlab-ci.yml GitLab CI (primary desktop/mobile release pipeline)
|
|
├── .forgejo/workflows/ Forgejo Actions (site deploy to Cloudflare Pages)
|
|
├── docker-compose.yml, docker-compose.nas.yml
|
|
├── Dockerfile.admin, apps/api-server/Dockerfile, apps/admin/Dockerfile
|
|
├── turbo.json, tsconfig.json, tsconfig.base.json, pnpm-workspace.yaml
|
|
├── package.json monorepo root, npm workspaces
|
|
├── CLAUDE.md Claude-specific project rules
|
|
└── AGENTS.md Cross-agent entry: rules + map obligation (this map's anchor)
|
|
```
|
|
|
|
Note: root `package.json` declares npm workspaces `apps/desktop`, `apps/web`, `apps/admin`, `packages/*`. `pnpm-workspace.yaml` also exists (`apps/*`, `packages/*`) but npm is the active toolchain. **Mobile is intentionally outside the workspace.**
|
|
|
|
---
|
|
|
|
## 2. Toolchain & versions
|
|
|
|
| Tool | Version | Source |
|
|
|---|---|---|
|
|
| Node | 24.19.0 | `.nvmrc` |
|
|
| TypeScript | 5.7 | root `package.json` |
|
|
| .NET SDK | 10.0.300 (rollForward latestPatch) | `global.json` |
|
|
| Deno | 2.8.1 | CI (`edge-functions-quality`) |
|
|
| JDK | 17 | mobile CI |
|
|
| Electron | 33.4.11 | `apps/desktop/electron-builder.yml` |
|
|
| React Native | 0.85 | `apps/mobile-rn/package.json` |
|
|
| Turborepo | turbo.json tasks: build/typecheck/test/lint/dev | `turbo.json` |
|
|
|
|
---
|
|
|
|
## 3. Root scripts (`package.json`)
|
|
|
|
```bash
|
|
npm run dev # desktop dev (electron-vite)
|
|
npm run build # desktop production build
|
|
npm run build:admin # admin production build
|
|
npm run build:all | npm run ci # scripts/ci/build-all.mjs
|
|
npm run checksum # scripts/ci/generate-checksums.mjs
|
|
npm run version:check | version:sync
|
|
npm run release:metadata[:test]
|
|
npm run release:forgejo[:check] # canonical Forgejo publisher/feed
|
|
npm run release:tag # annotated/signed immutable release tag
|
|
npm run security:secrets[:test] # hardcoded-secret scanner
|
|
npm run test:e2e:red # content-report red e2e
|
|
npm run release:mobile:boundary[:test]
|
|
npm run release:mobile:config[:test]
|
|
npm run release:mobile:build-config:test
|
|
npm run release:play:assets[:test]
|
|
npm run typecheck # all workspaces
|
|
npm run test # all workspaces
|
|
npm run lint # eslint apps/desktop apps/web apps/admin packages
|
|
npm run format # prettier
|
|
npm run typecheck:mobile # apps/mobile-rn tsc (outside npm workspaces)
|
|
npm run lint:mobile # apps/mobile-rn eslint --max-warnings=0
|
|
npm run test:mobile # apps/mobile-rn jest
|
|
npm run verify:all # aggregate: workspaces + mobile (typecheck/lint/test)
|
|
```
|
|
|
|
Per-app commands that matter:
|
|
|
|
| App | Commands |
|
|
|---|---|
|
|
| desktop | `npm run dev --workspace=@d3ro/desktop`, `build`, `typecheck`, `test` (vitest), playwright e2e; local STT engine: `npm --prefix apps/desktop run sidecar:setup` then `sidecar:build` (PyInstaller → `sidecar-dist/sidecar`), full local Windows package: `npm --prefix apps/desktop run dist:win:full`; `setup:sox` re-downloads the bundled SoX |
|
|
| mobile-rn | `npm run typecheck:mobile` / `lint:mobile` / `test:mobile` (root), or `npm --prefix apps/mobile-rn run lint/typecheck/test`; android gradle builds, Maestro E2E |
|
|
| api-server | `dotnet build`, `dotnet test` (also `apps/api-server.Tests`) |
|
|
| web | `next build`, playwright e2e in `apps/web/e2e` |
|
|
| admin | `next build` (`build:admin`) |
|
|
|
|
Desktop GUI execution rule (from `CLAUDE.md` / `.agents/rules/`): run via `run-desktop.bat` or an external terminal; do not background-launch GUI from an agent subshell.
|
|
|
|
---
|
|
|
|
## 4. Shared packages
|
|
|
|
See [`03-shared-packages.md`](./03-shared-packages.md). Summary:
|
|
|
|
| Package | Provides |
|
|
|---|---|
|
|
| `@d3ro/core` | Domain types, `D3ROError`/`ErrorCode`, IPC channel SSOT, constants, `crypto-license`, `pii-redactor`, `secure-memory`, `supabase-config`, `meeting-markdown`, `markdown-to-docx` |
|
|
| `@d3ro/ui` | Theme tokens, CSS vars, MUI DS components (web/desktop) |
|
|
| `@d3ro/ui-native` | RN design system (MetalCard, PhosphorText, Led, PhysicalButton, WaveBars, …) |
|
|
| `@d3ro/i18n` | 12 locales, `I18nProvider`, `t()`, date/number/relative formatters |
|
|
| `@d3ro/api-client` | Supabase browser/server clients, meetings/history/usage/transcribe wrappers, shared types; has vitest tests |
|
|
|
|
---
|
|
|
|
## 5. CI/CD
|
|
|
|
### GitHub Actions (`.github/workflows/`)
|
|
|
|
| Workflow | Purpose |
|
|
|---|---|
|
|
| `ci.yml` | Main CI: `code-quality` (secret scan, mobile release/config/build-config self-tests, Play asset contract, lint, typecheck), `api-server-tests`, `edge-functions-quality` (Deno), `test-matrix` (win/mac/ubuntu vitest), `build-validation` (desktop win, admin ubuntu), `mobile-android` (debug/CSPRNG/E2E APKs + verifiers), `mobile-emulator-e2e` (API 35 + Maestro 2.7.0) |
|
|
| `release.yml` | On tag `v*.*.*`: preflight → `package-windows` (NSIS) → `package-macos` (DMG/ZIP arm64) → `package-android` (signed APK/AAB + evidence) → `package-admin-docker` (GHCR) → `publish-release` (checksums + GitHub Release + Forgejo canonical publish) |
|
|
| `deploy-site.yml` | On `site/**`: build Vite site, boundary self-test, deploy GitHub Pages |
|
|
| `build-mac.yml` | Manual macOS build (arm64/x64), sox + PyInstaller sidecar + electron-rebuild |
|
|
| `payple-renew.yml` | Daily cron → `payple-renew` edge function |
|
|
| `release-signing-ca.yml` | Manual Windows (Azure Trusted Signing) / macOS notarize build+sign |
|
|
|
|
### GitLab CI (`.gitlab-ci.yml`)
|
|
|
|
Stages `validate → test → build → e2e → package → publish → deploy`. Primary pipeline for desktop Windows/macOS releases (Forgejo Generic Registry is the canonical updater feed; GitLab project 1172 is a legacy mirror) and production mobile releases (`mobile-production-release`, manual/protected). Admin NAS deploy job is intentionally **disabled**. `package-windows`/`package-macos` build the faster-whisper sidecar (`sidecar:setup` → `sidecar:build`) and run `scripts/ci/verify-sidecar-bundle.mjs` before electron-builder, so a release can never ship without the local STT engine.
|
|
|
|
### Forgejo Actions (`.forgejo/workflows/`)
|
|
`portable.yml` — 태그/수동 실행으로 **서명 없이** portable 채널(95MiB 7z 분할 볼륨 + Scoop 매니페스트 + 설치 스크립트)을 게시한다. `WIN_CSC_*` 불필요, updater feed는 건드리지 않는다.
|
|
|
|
`deploy-site.yml` / `deploy-site-windows.yml` — build `site`, write release identity, deploy to Cloudflare Pages `d3ro` (`d3ro.chanpaca.net`), verify live commit/version, app-links, legal URLs.
|
|
`release.yml` — tag-triggered Windows build (signed) + `publish-forgejo-release.mjs` to the canonical Forgejo feed/release hub.
|
|
|
|
---
|
|
|
|
## 6. Docker & deployment
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `Dockerfile.admin` | 3-stage Next.js admin build (node 24.19.0-alpine, port 3001) |
|
|
| `apps/admin/Dockerfile` | Next.js standalone runner for `.next/standalone` |
|
|
| `apps/api-server/Dockerfile` | Multi-stage .NET 10 (sdk → aspnet runtime), port 5000, `VOLUME /app/data` |
|
|
| `docker-compose.yml` | Dev/self-host: `d3ro-api-server` (5050→5000, `./data` volume), `d3ro-admin` (3001), optional `ollama` (profile `ai`, 11434) |
|
|
| `docker-compose.nas.yml` | NAS: prebuilt `d3ro-voice-api:latest` + `d3ro-voice-admin:latest`; API mounts `/volume1/docker/d3ro/wwwroot/{privacy,terms,delete-account,legal.css}` read-only |
|
|
|
|
Deploy scripts: `scripts/deploy-nas.ps1`, `scripts/deploy-nas.sh`, `scripts/deploy-site-to-nas.js`, `scripts/nas-control.sh` (start/stop/restart/status/logs/backup/update).
|
|
|
|
Public endpoints (production): `https://d3ro.chanpaca.net` (portal/API), `https://admin.chanpaca.net` (admin CRM). Edge: `server/cloudflare-worker` proxying to the NAS origin, plus a **Cron Trigger** (`* * * * *`) that drains the Supabase push outbox via `send-push?mode=drain` (`src/push-drain.ts`; needs `SUPABASE_URL` var + `SUPABASE_SERVICE_ROLE_KEY` secret). Tunnel: Cloudflare Tunnel `kd-nas`.
|
|
|
|
---
|
|
|
|
## 7. `server/supabase` (backend)
|
|
|
|
- `config.toml` — project `d3ro-voice`, ports 55321-55324, DB major 17, auth redirects (localhost, `d3ro.chanpaca.net`, `d3ro-voice://auth-callback`), providers Google/GitHub/Apple.
|
|
- `migrations/` — **63 SQL migrations** (schema, RLS, auth triggers, storage, team invites, knowledge/pgvector, push outbox, Payple/Stripe billing, admin roles, mobile platform/monetization, atomic command reorder, device revocation, content reporting, audit log, meeting documents, STT quota reservations, ad reward replay protection, team activity feed).
|
|
- `functions/` — **~27 Deno Edge Functions** (`stt-proxy`, `llm-proxy`, `content-report`, `generate-meeting-document`, `embed-chunks`, `search-knowledge`, `realtime-token`, `team-invite`, `team-accept`, `send-push`, `account-delete`, `admin-users`, `admin-subscriptions`, `admin-payments`, `admin-audit-log`, billing `billing-catalog`/`stripe-checkout`/`stripe-portal`/`stripe-webhook`/`payple-checkout`/`payple-manage`/`payple-renew`/`payple-webhook`, `iap-verify`, `admob-ssv`, `google-play-rtdn`). Shared contracts in `functions/_shared/` — push transports now include `webpush.ts` (VAPID + RFC 8291) and `apns.ts` (.p8 token) alongside FCM. CI (`edge-functions-quality`) runs `deno check` + `deno test` and also the Cloudflare worker drain test.
|
|
- `tests/` — integration/E2E for content report, mobile platform/recording/reward-race, payments, mobile release preflight, push, team push security, STT quota.
|
|
|
|
Full detail: [`09-supabase-backend.md`](./09-supabase-backend.md).
|
|
|
|
---
|
|
|
|
## 8. `scripts/` groups
|
|
|
|
- **CI (`scripts/ci/`, 33 files):** build/version/release (`build-all`, `sync-version`, `generate-checksums`, `verify-release-metadata`, `create-release-tag`, `extract-release-notes`), security (`check-no-hardcoded-secrets`), mobile release gates (`verify-mobile-release-boundary/-config/-build-config`, `verify-android-artifact/-app-links`, `verify-play-store-assets`, `prepare-whisper-model`, `create-mobile-release-evidence`, `prepare-mobile-release-publication`, emulator/CSPRNG gates), keys (`create-desktop-license-keypair`, `create-release-evidence-key`, etc.), publish (`publish-forgejo-release` canonical, `publish-gitlab-release` mirror; legacy `sync-and-publish-forgejo-release`, `upload-asset-to-forgejo-release`), env/tooling (`bootstrap-linux-toolchain.sh`, `audit-nas-stt-config.ps1`, mobile local E2E scripts).
|
|
- **Deploy/release:** `deploy-nas.ps1/.sh`, `deploy-site-to-nas.js`, `nas-control.sh`, `publish-gh.ps1`, `gen-keystore.js`.
|
|
- **GCP/Google OAuth automation + inspection (~70 `*.mjs`):** `auto-configure-oauth`, `automate-google-oauth`, `setup-consent`, `create-*-client`, `check-*`, `inspect-*` — mostly one-off/browser-driven console automation.
|
|
- **AdMob console automation:** `admob-probe.mjs` (read-only login/app/ad-unit probe), `admob-login.mjs` + `run-admob-login.bat` (one interactive headful Chrome login into a persistent profile), `admob-automate.mjs` (dry-run by default; `--apply` creates/verifies banner+rewarded units and reports Play-store link). Uses `playwright` with `channel: 'chrome'` and the gitignored `.chrome-playwright-profile`.
|
|
- **E2E / verification:** `e2e-desktop-*.js`, `real-app-multi-tab-e2e.js`, `test-and-capture-all-10-ad-services.js`, `verify-live-production-d3ro.js`.
|
|
- **Screenshots/captures:** `capture-*.js`.
|
|
- **Forgejo ops:** `check-forgejo-actions-runs.js`, `capture-forgejo-*.js`.
|
|
|
|
> `scripts/` is large and partially scratch. Prefer `scripts/ci/*` for anything release-gated, and `server/supabase/tests` for backend integration.
|
|
|
|
---
|
|
|
|
## 9. Release & versioning SSOT
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `release/product-version.json` | version `1.3.0`, `androidVersionCode`/`iosBuildNumber` `1030001`, releaseDate, desktop license keyId |
|
|
| `release/android-release-identity.json` | package `com.d3ro.voice`, Play app ID, app-signing SHA-256, upload cert SHA-256, evidence keyId, AdMob unit IDs |
|
|
| `release/desktop-license-public.pem` | Ed25519 public key for desktop offline licenses |
|
|
| `release/mobile-release-evidence-public.pem` | Ed25519 public key for mobile release evidence |
|
|
| `apps/desktop/electron-builder.yml` | appId `com.d3ro.voice`, NSIS x64 (forced code signing), macOS DMG/ZIP arm64, generic Forgejo publish feed, asarUnpack native modules + `@ffmpeg-installer`, extraResources (icons, sounds, sox, **sidecar**, ffmpeg, ollama) |
|
|
| `apps/desktop/src/main/update-feed.ts` | Auto-update feed SSOT (canonical Forgejo + legacy GitLab mirror, channels) |
|
|
| `release/update-policy.json` | Update policy SSOT (channels, minimum supported version, forced update, delta/full, staged rollout, kill switch) |
|
|
| `apps/desktop/src/main/update-policy.ts` | Policy parsing/decision logic |
|
|
| `scripts/ci/publish-forgejo-release.mjs` | Canonical Forgejo registry + Release + feed publisher |
|
|
|
|
Version sync is enforced by `scripts/ci/sync-version.mjs` and `verify-release-metadata.mjs`; `npm run version:check` should be clean.
|
|
|
|
---
|
|
|
|
## 10. Resources & tests
|
|
|
|
- `resources/sox/` — bundled Windows SoX (`sox.exe` + DLLs) for audio capture.
|
|
- `resources/ffmpeg/` — optional bundled ffmpeg (CI or manual); `getFfmpegPath()` also resolves the `@ffmpeg-installer/ffmpeg` binary from `app.asar.unpacked`.
|
|
- `apps/desktop/sidecar-dist/` — PyInstaller sidecar bundle consumed by `extraResources` (gitignored; built by `npm --prefix apps/desktop run sidecar:build`).
|
|
- `resources/icons/` — empty; electron-builder falls back to `build/icon.ico|png`.
|
|
- Desktop tests: `apps/desktop/tests/` (vitest unit + playwright e2e), `apps/desktop/test-results/`.
|
|
- Mobile tests: `apps/mobile-rn/__tests__/` (57 suites / 353 tests per mobile SSOT), `.maestro/` + `.maestro-output/` E2E evidence. Note: a full parallel Jest run can hit the 5s render timeout on slow machines; re-run the failing spec in isolation before treating it as a regression.
|
|
- Web tests: `apps/web/e2e/` (playwright).
|
|
- API tests: `apps/api-server.Tests/` (xUnit).
|
|
- Root: `tests/e2e/`, `test-results/.last-run.json`.
|
|
|
|
---
|
|
|
|
## 11. Known infrastructure gaps
|
|
|
|
See [`11-gap-backlog.md`](./11-gap-backlog.md) for the maintained list (`INFRA-*`). Headlines:
|
|
- `apps/mobile-rn` is not an npm workspace member; use `typecheck:mobile`/`lint:mobile`/`test:mobile` or `verify:all`.
|
|
- Admin NAS deploy job disabled in GitLab CI; admin ships via GitHub/GHCR + manual NAS compose.
|
|
- Two identity systems (.NET JWT/SQLite vs Supabase); a canonical resolver now exists in `@d3ro/core/entitlement` but web/mobile/.NET adoption is incremental (`11` GAP-ID-02).
|