Commit Graph

16 Commits

Author SHA1 Message Date
soroush.asadi 4cc1c3a423 feat(payment): FlatRender Pay (ZarinPal broker) checkout + webhook
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m3s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 37s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 53s
CI/CD / Deploy · all services (push) Successful in 1m41s
Adds a signed broker integration for online plan purchases:
- FlatPayService: POST /v1/pay/request with X-Api-Key + X-Signature =
  hex(HMAC-SHA256(secret, raw JSON bytes)); the exact serialized bytes are both
  signed and sent. VerifyWebhook does a fixed-time compare of the digest, plus an
  in-memory first-seen idempotency set.
- POST /api/payment/request (auth, ManageBilling): parses a "Tier:Months" product
  (e.g. "Pro:12"), prices it via the plan catalog, creates a Pending SubscriptionPayment
  (provider=FlatPay) as the order, and returns the broker payment URL. The order id is
  the client_ref / metadata.payment_id.
- POST /api/payment/webhook (anonymous; HMAC is the auth — 401 on bad signature):
  on status=Paid + first-seen id, grants the order via the shared plan-activation
  path (extracted ActivatePaymentAsync, reused by all providers). Always 200 after a
  valid signature so the broker won't retry an accepted job.
- Config FlatPay__{ApiKey,Secret,BaseUrl,ReturnUrl} (env-supplied; secrets stay out
  of git), compose + .env.example wiring. PaymentProvider.FlatPay appended (int, no
  migration).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 04:20:02 +03:30
soroush.asadi 8ea98bdc09 fix(seo): website/koja base URL defaulted to localhost → de-indexed in GSC
CI/CD / CI · API (dotnet build + test) (push) Successful in 5m47s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 34s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Successful in 3m53s
Production serves robots.txt Host/Sitemap, sitemap <loc>, and every page's
canonical + og:url as http://localhost:3010 — so Google rejects all URLs
("URL not allowed") and indexes nothing. Cause: NEXT_PUBLIC_SITE_URL is baked in
at BUILD time and was unset in prod, so it fell back to the localhost defaults in
the compose files + website Dockerfile.

Changes the defaults to the real domains (website → https://meezi.ir, koja →
https://koja.meezi.ir) in docker-compose.yml, docker-compose.full.yml, the
website Dockerfile ARG, and .env.example.

Build-time var → the website image MUST be rebuilt + redeployed (CI does this on
push), then purge the WCDN cache and resubmit the sitemap in Search Console.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 18:58:38 +03:30
soroush.asadi 32a7cf5b25 ops: nightly DB backup + self-hosted uptime monitoring
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 1m48s
Backup (production data-loss protection — was none):
- meezi-backup sidecar in docker-compose.yml runs pg_dump nightly at 02:00
  Tehran, gzip, 14-day rotation, atomic .partial→final, into ./backups
  (persists across deploys; rsync off-box per RESTORE.md).
- Wired into the deploy job (up -d --no-deps backup); takes one dump on boot.
- scripts/backup/pg-backup-loop.sh + RESTORE.md (restore + off-box guidance).

Monitoring:
- docker-compose.monitoring.yml: Uptime Kuma stack (own volume), stood up
  once, independent of app deploys.
- Caddyfile status.{$DOMAIN} route; docs/monitoring.md lists the exact
  monitors (incl. /q guest-menu 200 check) + TLS-expiry alerts (catches the
  ~90-day cert breakage early) + alert-channel setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:45:07 +03:30
soroush.asadi 9765491f6f fix(prod): payment/tax gateways never fake success outside Development
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m7s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Successful in 1m31s
Production-readiness audit fixes — every mock fallback is now gated on
IsDevelopment; in production these paths fail loudly instead:

- ZarinPal/Tara/SnappPay init: missing credentials returned a MOCK
  payment URL whose callback verified as paid — a café could activate a
  paid plan without paying. Now: "Payment gateway is not configured."
- Tara/SnappPay verify: a forged MOCK-* trace/token on the callback was
  accepted as a verified payment in any environment. Now rejected
  outside Development.
- Taraz (سامانه مودیان): returned a fake MOCK-TARAZ tracking code as if
  invoices reached the tax authority. Now returns an honest error (the
  real integration is not built yet).
- Admin integrations: NextPay/Vandar removed — they were listed but have
  no gateway implementation (selecting them silently used ZarinPal).
- docker-compose: ASPNETCORE_ENVIRONMENT default flipped Development →
  Production so a missing env var can never run prod in dev mode.

86 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 10:16:01 +03:30
soroush.asadi aec5b21f98 fix: lock compose project name to 'meezi', scope image prune to meezi only
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 1m22s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m0s
CI/CD / CI · Admin Web (tsc) (push) Successful in 33s
CI/CD / CI · Website (tsc) (push) Successful in 43s
CI/CD / CI · Koja (tsc) (push) Successful in 47s
CI/CD / Deploy · all services (push) Failing after 2s
Prevents runner workspace collisions with other projects (DrSousan etc.)
causing containers to be treated as orphans and stopped on deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:45:07 +03:30
soroush.asadi 255695e8ae fix(deploy): auto-migrate on boot + seed admin credentials from env
CI/CD / CI · API (dotnet build + test) (push) Successful in 47s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 43s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 3m36s
- docker-compose.admin.yml: RUN_MIGRATIONS was hardcoded false → now
  uses ${RUN_MIGRATIONS:-true} so migrations run automatically on deploy
- Both compose files: expose Seed__SystemAdminPhone/Username/Password
  env vars so the seeder sets admin credentials without manual SQL
- .env.example: document SEED_ADMIN_* variables

On next deploy: migrations run, Username='admin' is patched on the
existing admin, and password is hashed from SEED_ADMIN_PASSWORD.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 20:10:56 +03:30
soroush.asadi 345ae0a4b5 first commit
CI/CD / CI · Admin API (dotnet build) (push) Successful in 41s
CI/CD / CI · Admin Web (tsc) (push) Failing after 5s
CI/CD / CI · Website (tsc) (push) Failing after 4s
CI/CD / CI · Koja (tsc) (push) Failing after 5s
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m13s
CI/CD / CI · Dashboard (tsc) (push) Failing after 2m32s
CI/CD / Deploy · all services (push) Has been skipped
2026-05-31 11:06:24 +03:30
soroush.asadi 8ca2cae988 Pull Docker images from Nexus connector port 8087
The Docker daemon reaches the Nexus Docker group over the dedicated
connector port 8087 (its registry mirror), not the main 8081 HTTP port,
which caused HTTPS-to-HTTP pull failures in CI. Repoint all image refs to
171.22.25.73:8087 at the connector root; npm and NuGet stay on 8081.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 05:17:21 +03:30
soroush.asadi e8cd6d3282 Route all package mirrors through local Nexus
Point Docker, NuGet, and npm pulls at the Nexus group repos on
171.22.25.73:8081 for both CI/CD and local builds, so the pipeline and
developers no longer depend on Docker Hub, MCR, nuget.org, or npmjs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 00:28:07 +03:30
soroush.asadi 289c808257 Rename public discovery app from "finder" to "koja"
Rebrand the public café-discovery app: directories web/finder→web/koja and
docker/finder→docker/koja, plus all service wiring (docker-compose, Caddy
subdomain koja.meezi.ir, env vars KOJA_PORT / NEXT_PUBLIC_KOJA_URL, CI
workflows) and the app's display name (Koja / کجا).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 17:02:22 +03:30
soroush.asadi 16cff8730b feat : kavenegar otp added 2026-05-29 10:18:47 +03:30
soroush.asadi 42d7667735 Fully implement Kavenegar SMS support
Core changes:
- ISmsService: add SendBulkAsync (batches of 200) + GetAccountInfoAsync
- KavenegarSmsService: POST requests, sender number config, bulk send
  via comma-separated receptors, account balance, full error code mapping
  (HTTP 400-432), enabled-flag check before any send
- SmsMarketingService: replaced per-recipient loop with SendBulkAsync
- SmsController: new GET /sms/balance endpoint returns Kavenegar credit
- SmsDtos: SmsBalanceDto
- IntegrationDtos + PlatformIntegrationService: SenderNumber field
- appsettings.json + docker-compose: Kavenegar__SenderNumber = 90005671

Dashboard:
- sms-screen: char counter, SMS parts indicator (Persian 70/67 chars,
  Latin 160/153), account balance card, sender line display, result banner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 02:38:06 +03:30
soroush.asadi aa4612e06b fix: use Liara mirrors directly for images with Nexus OCI proxy issues
- aspnet:10.0, postgres:16-alpine, redis:7-alpine all fail on first
  fetch through Nexus proxy (OCI manifest format bug in Nexus)
- Change DOTNET_ASPNET_IMAGE default to mcr-mirror.liara.ir directly
- Change postgres/redis service images to docker-mirror.liara.ir
- CI service containers (api-build job) also use Liara directly
- All images parameterized so ENV_FILE can override for any registry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:08:07 +03:30
soroush.asadi 979dcaa949 fix: route all Docker builds through local Nexus mirrors
- All Node Dockerfiles rewritten with NODE_IMAGE + NPM_REGISTRY build args
  defaulting to local Nexus proxies (171.22.25.73:5000/library/node:20-alpine
  and http://mirror:8081/repository/npm-group/)
- Add extra_hosts: mirror:host-gateway to every build section so the
  mirror hostname resolves during docker build
- Replace nuget.org with nuget.docker.config (Nexus mirror) in api/admin-api
  Dockerfiles to fix NuGet restore in Iranian network
- Rewrite admin-web and website Dockerfiles (were referencing non-existent
  meezi-node:20-alpine base image with no npm install step)
- Update dotnet image defaults to 171.22.25.73:5002 MCR proxy in admin-api
  and docker-compose.admin.yml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 21:01:43 +03:30
soroush.asadi 88a9f96108 feat(infra): parameterize secrets, add Caddy reverse proxy for domain
All hardcoded passwords/keys replaced with env vars so .env controls
everything in both dev and production:
  - DB_PASSWORD, DB_CONNECTION_STRING, JWT_KEY
  - CORS_ORIGIN_*, ASPNETCORE_ENVIRONMENT
  - All ZarinPal/Kavenegar/Snappfood secrets

New files for tomorrow's domain setup:
  - Caddyfile        → routes all subdomains with auto TLS
  - docker-compose.caddy.yml → adds Caddy service to the stack

.env.example now has clear TODAY (IP) vs TOMORROW (domain) sections.
Fixed hardcoded ZarinPal MerchantId in docker-compose.full.yml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 18:54:55 +03:30
soroush.asadi 45cd028d1c chore: initial project structure and root configuration
Adds root-level config files: solution (.slnx), NuGet, global.json,
Docker Compose files for all services (API, dashboard, website, finder,
admin), environment example, and developer documentation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-27 21:33:10 +03:30