Commit Graph
27 Commits
Author SHA1 Message Date
s.esmaeiliandCursor 840311a2a0 fix(sms): accept a gateway reply with debug text before its JSON
The relay gateway now prints timing lines before its result, e.g.
"1. Function Start at: ...<br>...572381951{"data":"sent"}". The message
was delivered, but the whole body is not JSON, so it was logged as a failed
SMS. When the body does not parse, the trailing JSON object is read instead.
A body with no JSON at all (the gateway's bare PHP errors) still fails.

Tests: sms.service.spec (relayRejection). 33 SMS tests pass.

پیامکِ ارسال‌شده دیگر به اشتباه در گزارش سرور «ناموفق» ثبت نمی‌شود.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:39:41 +03:30
s.esmaeiliandCursor 797d030e67 fix(sms): stop sending platform to the relay gateway
With platform "kave" (Kavenegar) the gateway answered "cURL library is not
loaded" and nothing was delivered. The body is now { phone, host, text } and
the gateway picks the line itself. SMS_RELAY_PLATFORM is removed from the
driver, docker-compose and the env examples.

Tests: sms.service.spec updated. 150 tests pass.

پیامک بدون انتخاب خط کاوه‌نگار فرستاده می‌شود و سرویس پیامک خودش خط را انتخاب می‌کند.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:34:57 +03:30
s.esmaeiliandCursor c34bf80e6f fix(api): start in production with safe fallbacks instead of crash-looping (502)
A production API with incomplete config threw before listening, so the
container restarted forever and every /api request through nginx was a 502.
The problems are now printed as one loud warning and each gets a safe
fallback:

- JWT_SECRET / JWT_CUSTOMER_SECRET missing, weak or equal: a random key is
  generated once and kept in the Setting table (boot-secrets.ts), so tokens
  survive restarts. A strong value in the environment always wins.
  JwtModule now uses registerAsync so it reads the key after boot fills it.
- OTP_MASTER_CODE: ignored in production unless
  TEST_SERVER_ALLOW_MASTER_CODE=yes.
- CORS_ORIGINS: invalid entries dropped; empty allows every origin.

Tests: boot-secrets.spec, production-checks.spec. 118 tests pass.

سرور با تنظیمات ناقص دیگر مدام ریست نمی‌شود و ورود دوباره کار می‌کند.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:19:30 +03:30
s.esmaeiliandClaude Opus 5.5 7f851676b5 fix(sms): inside Docker, send the server's public IP as the relay host, not the container's
Messages went out locally but not from the server. The relay gateway
identifies the sending server by `host`, and the owner's ruling (D26)
is that this is the server's IP. With SMS_RELAY_HOST unset, the driver
sent the machine's first external IPv4. On the bare PHP server that was
the real IP; inside the API container it is the container's (172.x).
Reproduced: a container on this machine reports 172.17.0.3.

- relay.driver: when SMS_RELAY_HOST is unset and the machine address is
  private (RFC 1918, CGNAT, link-local), the public IPv4 is looked up
  once (ipify, ifconfig.me, icanhazip in turn) and cached. If every
  lookup fails, it falls back to the old value. SMS_RELAY_HOST still wins.
- SmsService logs the host it will send at boot, and warns when it is
  still private, so a wrong value is visible instead of silent.
- Removed a debug console.log that wrote every recipient's mobile to the
  server log.
- .env.example and SERVER-SETUP-CHECKLIST: SMS_RELAY_HOST = this
  server's public IP.

Tests: relay-host.spec (5). All 254 API tests pass.

اپ: پیامک کد ورود دوباره به‌درستی ارسال می‌شود.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:58:13 +03:30
s.esmaeiliandClaude Opus 5.5 1bc3739e78 Merge release/delivery (IAM single sign-on) into the triage work
Brings the four release/delivery commits (IAM SSO for the admin panel,
both apps and the API; IAM env in docker-compose) onto fix/issues-triage.

Conflicts:
- customer-auth.service.ts imports: both kept.
- both apps' api/client.ts: both kept (AccountingBalance/WageQuote, and
  iamAppUrl).
- all build output (admin-panel, user-app, user-app-v2 dist, and the api
  release bundle): rebuilt from the merged source instead of picking a
  side.

Fixed while verifying:
- The IAM change calls iam.browserBootstrapUrl after every login, but
  the identity-service stand-ins in three test files lacked it. 15 tests
  failed on release/delivery itself.
- SMS relay: SMS_RELAY_PLATFORM was read but never sent, so the gateway
  never got `platform` (documented body: phone, host, text, platform).
  relayConfig() now returns it ("default" → null) and the request carries
  it. sms.service.spec now passes.

All 249 API tests pass; the three front-ends type-check and build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:03:53 +03:30
mahmood dcb74603ad feat add some for users in iam 2026-10-08 00:10:10 +03:30
mahmood 40a28d5605 Feat build new 2026-10-07 21:26:54 +03:30
mahmood 148b99c4a6 feat build 2026-09-25 19:12:39 +03:30
s.esmaeiliandClaude Opus 5 24b7a4c395 fix(api): treat blank env vars as unset; verify the whole flow in Docker Compose
Found by running the delivery flow against the actual built container rather
than a local dev process.

DEPLOY-02 (Critical) — ADMIN LOGIN RETURNED 500 FOR EVERY USER IN DOCKER.
docker-compose.yml passes each optional variable as ${X:-}, so an unconfigured
one arrives as "" rather than undefined. The code read them with
`process.env.X ?? 'default'`, which only falls back on undefined, so the empty
string reached jsonwebtoken and it threw

  "expiresIn" should be a number of seconds or string representing a timespan

on every sign. Invisible to every existing suite, all of which run against a
local process where the variable is genuinely absent.

Added src/common/env.ts (envOr / envOrNull), which treat blank as unset, and
applied it to every auth, port, upload and SMS read. Three were not cosmetic:
a blank UPLOAD_DIR would have served static files from the FILESYSTEM ROOT, a
blank PORT would have bound a random ephemeral port (Number('') === 0), and a
blank JWT_SECRET would have become the verifying key. Recorded as D25, with a
standing rule to use these readers instead of `??`.

Pinned by src/common/env.spec.ts (9 tests), which reproduces the original throw
against the real jsonwebtoken and asserts the reader can no longer produce it.

DEPLOY-03 (process, documented) — the API image is `COPY release/ ./`, so it
ships a bundle pre-built on the developer machine. `docker compose build` will
happily produce an image from a STALE release/app.js and report success; at the
start of this run that bundle was four days old and the rebuilt container was
missing every route added since. DELIVERY.md's deploy section now leads with
`pnpm build:release`.

New: scripts/test-compose-e2e.cjs — the whole delivery flow against the built
container, over HTTP only, as a black box: a sales expert and an ordinary
customer both sign in with the master code, both place an order, both orders
appear in the admin grid and open there, and the three token boundaries still
hold in the deployed image. 32/32.

Verified: compose e2e 32/32 · jest 54/54 · build 5/5 ·
against the CONTAINER: complete-profile 38/38, report 40/40, expert login 36/36 ·
locally: apps smoke 43/43, admin smoke 43/43, B2B scan 34 ok/0 broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 02:27:30 +03:30
Montech 79226c9f13 fix some for api download pic 2026-09-19 15:44:00 +03:30
s.esmaeili 7227e9c61b build 2026-09-19 13:12:58 +03:30
s.esmaeili 80ccf1a2aa feat build 2026-09-18 19:58:29 +03:30
mahmood 062fe80a68 feat build 2026-09-18 17:30:32 +03:30
Montech 05fe780c89 Merge branch 'dev'
# Conflicts:
#	apps/admin-panel/dist/index.html
#	apps/admin-panel/nginx.conf
#	apps/admin-panel/vite.config.ts
#	apps/api/release/app.js
2026-09-16 15:45:23 +03:30
mahmood 4743cfb89f fix admin and montzar 2026-09-16 11:59:35 +03:30
mahmood 2448627e1b fix: stop drift migration from dropping tracking table 2026-09-15 22:40:00 +03:30
Montech 341c7a5095 fix some for db 2026-09-15 17:25:12 +03:30
Montech a7e13fe64e fix update build 2026-09-15 15:41:47 +03:30
s.esmaeiliandClaude Opus 5 a12fb0450e api: don't replay the baseline migration on pre-fix databases
The folder-discovery fix in the previous commit surfaced a second problem.
Databases migrated BEFORE that fix recorded the baseline under the name of
the hand-added flat duplicate that used to sit beside it
(prisma/migrations/0001_init.sql). Once the runner started discovering the
real folder (20260910113720_first), it saw a name it had never recorded and
tried to apply the baseline again — against a database that already had it:

  Raw query failed. Code: 42710.
  ERROR: type "AdminRole" already exists

The two files are identical SQL (verified byte-for-byte after normalising
line endings; they differ only by one trailing blank line), so the recorded
legacy name genuinely means that migration is applied. A LEGACY_NAME_ALIASES
map now treats '0001_init.sql' as an already-applied marker for
'20260910113720_first', so upgrading an existing database is a no-op instead
of a crash.

Verified against the local dev database this actually broke on
(postgresql://localhost:5432/gold_banking, which had recorded only the old
flat name): 'npm run dev' in apps/api now logs 'No pending migrations' and
'Nest application successfully started' instead of dying. That database's
tracking table was also reconciled to record both real migration names, and
it was confirmed to already contain both migrations' schema (AdminRole,
City.percent, user_type, CustomerKind all present) — nothing was re-run
against it. The dockerised database is unaffected: it already records the
real names, so the alias is inert there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 21:44:14 +03:30
s.esmaeiliandClaude Sonnet 5 2b6795d007 api: fix migration runner, serve uploads correctly, add product photos
Three real bugs found and fixed while wiring up product images end-to-end:

1. src/prisma/migrate.ts only discovered flat prisma/migrations/<name>.sql
   files, never Prisma's own folder-per-migration layout
   (<timestamp>_<name>/migration.sql). Every migration created the normal
   way (via 'prisma migrate dev') was silently skipped; only a hand-added
   flat duplicate (prisma/migrations/0001_init.sql, now removed) ever ran.
   This meant schema.prisma had drifted well ahead of what any real deploy
   database actually had (missing columns on City, Customer, Branch,
   Product, several new tables). Fixed to discover both layouts; the
   drift is closed with a new 20260914165255_sync_schema_drift migration.

2. Neither nginx.conf (production) nor vite.config.ts (dev) proxied
   /uploads/ to the API — only /api/. UploadedFile/ProductImage URLs
   (/uploads/<file>, served by main.ts's useStaticAssets OUTSIDE the /api
   prefix) 404'd through both. Both now proxy /uploads/ the same way /api/
   already was.

3. docker-compose.yml's api service had no volume for storage/uploads, so
   every uploaded file — and by extension every ProductImage row pointing
   at one — was lost on the next container recreate/redeploy. Added a
   named volume.

Also: main.ts's static file serving now sets Content-Type: image/avif
explicitly (serve-static's bundled mime table predates .avif and served it
as application/octet-stream; browsers rendered it anyway by sniffing, but
a stricter client would not have).

Product photos: copied 15 real jewelry photos into the uploads volume,
registered them as UploadedFile rows, and assigned each of the 5 existing
Product rows a randomly picked one (duplicates allowed, per request),
replacing their /img/no-items.png placeholder ProductImage row. Verified
end-to-end in a real browser against the real containerized API+DB: logged
in, opened the admin product list, confirmed all 5 rows render their photo
(naturalWidth/Height populated, network 200s, no console errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 20:56:17 +03:30
mahmood f19f49daeb feat: auto-run SQL migrations and one-time seed on boot 2026-09-10 17:15:17 +03:30
mahmood 1b675804ea fix: nginx dns re-resolve, db healthcheck, explicit bind 2026-09-10 14:51:57 +03:30
mahmood 7d93507089 fix: run API on bookworm with debian Prisma engine 2026-09-10 14:45:47 +03:30
mahmood 88bb7b1fbf fix: use bookworm + debian Prisma engine 2026-09-10 13:58:50 +03:30
mahmood bbbe6a5202 fix: add linux-musl Prisma engines for Alpine 2026-09-10 13:00:05 +03:30
mahmood f0f82d20ec fix: vendor Prisma into release for Docker 2026-09-10 12:53:23 +03:30
mahmood cea3b808d8 Fix some for production 2026-09-10 12:17:47 +03:30