Commit graph

465 commits

Author SHA1 Message Date
Ashwin Kumar Sivakumar
3b4a4eac3f fix(ci): install build-essential before cargo test in ai-credits job
Some checks failed
build-and-release / build (catering-services) (push) Successful in 28s
build-and-release / build (customers) (push) Successful in 30s
build-and-release / build (cron) (push) Successful in 55s
build-and-release / build (companies) (push) Successful in 1m54s
build-and-release / build (job-seekers) (push) Successful in 8s
build-and-release / build (employees) (push) Successful in 2m11s
build-and-release / build (fitness-trainers) (push) Successful in 1m48s
build-and-release / build (developers) (push) Successful in 2m26s
build-and-release / build (gateway) (push) Successful in 2m2s
build-and-release / build (jobs) (push) Successful in 54s
build-and-release / build (graphic-designers) (push) Successful in 2m32s
build-and-release / build (ugc-content-creators) (push) Successful in 8s
build-and-release / build (payments) (push) Successful in 1m50s
build-and-release / build (photographers) (push) Successful in 1m51s
backend-integration-tests / ai-credits (push) Failing after 4s
build-and-release / build (makeup-artists) (push) Successful in 2m32s
build-and-release / build (social-media-managers) (push) Successful in 2m55s
build-and-release / build (tutors) (push) Successful in 2m45s
build-and-release / build (users) (push) Has been cancelled
build-and-release / build (video-editors) (push) Has been cancelled
The bare bookworm runner image has no C toolchain at all, unlike
build.yaml's jobs which build inside a Dockerfile-based image with one
already present. proc-macro2/libc/etc build scripts need `cc` to link
and failed immediately (error: linker `cc` not found) before any real
test code ran.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 17:39:12 +05:30
Ashwin Kumar Sivakumar
da9be7f6a3 feat(db,ci): fix missing ai_credits tables, wire integration tests to CI
Some checks failed
build-and-release / build (cron) (push) Successful in 1m10s
build-and-release / build (developers) (push) Successful in 1m40s
build-and-release / build (catering-services) (push) Successful in 1m58s
build-and-release / build (employees) (push) Successful in 2m19s
build-and-release / build (companies) (push) Successful in 2m36s
build-and-release / build (customers) (push) Successful in 2m45s
build-and-release / build (gateway) (push) Successful in 1m11s
build-and-release / build (jobs) (push) Successful in 40s
build-and-release / build (fitness-trainers) (push) Successful in 2m28s
build-and-release / build (graphic-designers) (push) Successful in 2m14s
build-and-release / build (payments) (push) Successful in 1m48s
build-and-release / build (job-seekers) (push) Successful in 2m57s
build-and-release / build (makeup-artists) (push) Successful in 2m49s
build-and-release / build (photographers) (push) Successful in 2m43s
backend-integration-tests / ai-credits (push) Failing after 31s
build-and-release / build (social-media-managers) (push) Successful in 2m58s
build-and-release / build (tutors) (push) Successful in 2m47s
build-and-release / build (ugc-content-creators) (push) Successful in 2m39s
build-and-release / build (users) (push) Has been cancelled
build-and-release / build (video-editors) (push) Has been cancelled
Re-enables and corrects 20260703210000_ai_credits_wallet, which had been
disabled (.up.sql.skip) in commit 0163349 because 'relation already
exists' aborted the original CREATE TABLE script on its very first
table (ai_plans) - silently leaving ai_credit_ledger and
ai_reservation_holds never created, despite the wallet system being
actively wired into apps/users/handlers/ai_credits.rs, apps/payments,
apps/cron with real subscription rows already in prod.

Rewrote the migration to be idempotent: ADD COLUMN IF NOT EXISTS to
bring ai_plans/user_ai_subscriptions/ai_feature_costs/ai_usage_logs up
to the full column set crates/db/src/models/ai_credits.rs expects
(safe zero/default backfills, no data loss - verified against the 3
existing subscription rows), and CREATE TABLE IF NOT EXISTS for the 2
genuinely-missing tables. down.sql updated to only reverse what this
corrected version actually adds, not drop tables that predate it.

Applied directly to the live database (verified schema + existing rows
intact afterward).

Also wires crates/db/tests/ai_credits.rs + ai_credits_reaper.rs to
Forgejo Actions CI (docs/LIVE_SERVER_RUNBOOK.md step 6):
- New TEST_DATABASE_URL repo secret, pointing at a dedicated
  nxtgauge_test database (created fresh, schema mirrored from live prod
  via pg_dump --schema-only - never runs against the real nxtgauge DB).
- .forgejo/workflows/test.yaml: runs on push to main/high-performance,
  skips when crates/db/ isn't touched, cargo test -p db --test
  ai_credits --test ai_credits_reaper -- --test-threads=1.

Also commits docs/openapi.wallet-holds.json (hand-written minimal
OpenAPI spec used for schemathesis fuzzing in step 4) and gitignores
the local .schemathesis/ cache directory from that run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 17:31:22 +05:30
Ashwin Kumar Sivakumar
6e1041a1f6 chore(deps): document rkyv CVE exemption, restore + expand audit.toml comments
All checks were successful
build-and-release / build (companies) (push) Successful in 5s
build-and-release / build (cron) (push) Successful in 5s
build-and-release / build (developers) (push) Successful in 6s
build-and-release / build (employees) (push) Successful in 6s
build-and-release / build (fitness-trainers) (push) Successful in 6s
build-and-release / build (gateway) (push) Successful in 7s
build-and-release / build (graphic-designers) (push) Successful in 8s
build-and-release / build (job-seekers) (push) Successful in 10s
build-and-release / build (customers) (push) Successful in 20s
build-and-release / build (catering-services) (push) Successful in 21s
build-and-release / build (makeup-artists) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 6s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (photographers) (push) Successful in 7s
build-and-release / build (ugc-content-creators) (push) Successful in 7s
build-and-release / build (tutors) (push) Successful in 8s
build-and-release / build (social-media-managers) (push) Successful in 11s
build-and-release / build (users) (push) Successful in 5s
build-and-release / build (video-editors) (push) Successful in 6s
cargo audit flagged RUSTSEC-2026-0235 (rkyv, out-of-bounds read) via
rust_decimal's optional rkyv feature, which apps/payments never enables
(features = ["db-tokio-postgres"] only) - confirmed absent from the
actual feature-resolved graph via cargo tree, and cargo-deny (which is
feature-aware) already doesn't flag it. Added to both deny.toml and
.cargo/audit.toml so cargo audit also exits 0.

Also re-documented the 4 pre-existing crate-name-collision ignores in
.cargo/audit.toml (RUSTSEC-2020-0128/2021-0006/2023-0040/2023-0059 -
our own workspace crates crates/cache and apps/users happen to share
name+version with unrelated abandoned crates.io packages) with fuller
explanation, and mirrored them into deny.toml for a single source of
truth even though cargo-deny's real graph resolution never matched
them in the first place.

Verified: cargo audit and cargo deny check advisories bans both now
exit 0 against the live server runbook's requirement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:30:30 +05:30
Ashwin Kumar Sivakumar
f007ca0999 fix(db): re-enable wallet_full migration (tracecoin_holds/buckets)
All checks were successful
build-and-release / build (gateway) (push) Successful in 49s
build-and-release / build (employees) (push) Successful in 1m46s
build-and-release / build (ugc-content-creators) (push) Successful in 2m36s
build-and-release / build (companies) (push) Successful in 1m55s
build-and-release / build (catering-services) (push) Successful in 2m48s
build-and-release / build (fitness-trainers) (push) Successful in 1m45s
build-and-release / build (makeup-artists) (push) Successful in 1m43s
build-and-release / build (payments) (push) Successful in 2m52s
build-and-release / build (tutors) (push) Successful in 2m54s
build-and-release / build (job-seekers) (push) Successful in 7m28s
build-and-release / build (developers) (push) Successful in 2m28s
build-and-release / build (photographers) (push) Successful in 2m37s
build-and-release / build (video-editors) (push) Successful in 2m32s
build-and-release / build (users) (push) Successful in 4m55s
build-and-release / build (social-media-managers) (push) Successful in 7m32s
build-and-release / build (customers) (push) Successful in 2m11s
build-and-release / build (cron) (push) Successful in 2m14s
build-and-release / build (jobs) (push) Successful in 39s
build-and-release / build (graphic-designers) (push) Successful in 2m0s
20260627030000_wallet_full.up.sql was disabled in 0163349 as part of a
bulk cleanup of migrations referencing the dropped professionals
table, but this one doesn't touch that table at all - it only
references tracecoin_wallets, tracecoin_ledger and users, all of
which already exist in prod. It was swept up by mistake.

Without it, tracecoin_holds/tracecoin_buckets never got created, so
the new GET /wallet/me/holds route (crates/contracts/src/profession_shared.rs)
500s with 'relation "tracecoin_holds" does not exist'.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:18:51 +05:30
Ashwin Kumar Sivakumar
b30629411a fix(ci): diff/scan full pushed commit range, not just HEAD^..HEAD
All checks were successful
build-and-release / build (customers) (push) Successful in 2m42s
build-and-release / build (developers) (push) Successful in 1m46s
build-and-release / build (graphic-designers) (push) Successful in 2m43s
build-and-release / build (tutors) (push) Successful in 2m44s
build-and-release / build (catering-services) (push) Successful in 1m49s
build-and-release / build (fitness-trainers) (push) Successful in 1m48s
build-and-release / build (photographers) (push) Successful in 2m55s
build-and-release / build (users) (push) Successful in 4m57s
build-and-release / build (gateway) (push) Successful in 1m22s
build-and-release / build (payments) (push) Successful in 2m12s
build-and-release / build (ugc-content-creators) (push) Successful in 2m44s
build-and-release / build (companies) (push) Successful in 1m59s
build-and-release / build (social-media-managers) (push) Successful in 2m42s
build-and-release / build (job-seekers) (push) Successful in 7m6s
build-and-release / build (video-editors) (push) Successful in 2m42s
build-and-release / build (cron) (push) Successful in 2m9s
build-and-release / build (employees) (push) Successful in 2m30s
build-and-release / build (makeup-artists) (push) Successful in 2m40s
build-and-release / build (jobs) (push) Successful in 6m53s
Multi-commit pushes (e.g. this repo's mirror sync from GitHub) were
silently skipping every service's build: the 'does this service need
building' check only looked at the last commit vs its immediate
parent. A push landing 3 commits where only the final one was a no-op
(docs/.gitignore) meant the actual code commit's changes were never
seen, so all 19 services reported 'no changes relevant - skipping'
even though real backend code had changed.

Use github.event.before (the pre-push SHA) to diff/scan the whole
pushed range when available, falling back to HEAD^..HEAD only when
that ref is unavailable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:18:37 +05:30
Tracewebstudio Dev
7b4a23cee6 chore: gitignore *.pid runtime files
All checks were successful
build-and-release / build (companies) (push) Successful in 14s
build-and-release / build (developers) (push) Successful in 13s
build-and-release / build (customers) (push) Successful in 15s
build-and-release / build (employees) (push) Successful in 15s
build-and-release / build (catering-services) (push) Successful in 19s
build-and-release / build (cron) (push) Successful in 18s
build-and-release / build (fitness-trainers) (push) Successful in 6s
build-and-release / build (graphic-designers) (push) Successful in 6s
build-and-release / build (gateway) (push) Successful in 7s
build-and-release / build (job-seekers) (push) Successful in 8s
build-and-release / build (makeup-artists) (push) Successful in 8s
build-and-release / build (jobs) (push) Successful in 8s
build-and-release / build (payments) (push) Successful in 6s
build-and-release / build (social-media-managers) (push) Successful in 6s
build-and-release / build (photographers) (push) Successful in 7s
build-and-release / build (tutors) (push) Successful in 8s
build-and-release / build (ugc-content-creators) (push) Successful in 8s
build-and-release / build (users) (push) Successful in 7s
build-and-release / build (video-editors) (push) Successful in 7s
PID files are created by running services locally and should never
be tracked in version control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-12 18:25:40 +02:00
Tracewebstudio Dev
9e37060737 docs: add live server runbook
All checks were successful
build-and-release / build (cron) (push) Successful in 12s
build-and-release / build (customers) (push) Successful in 16s
build-and-release / build (employees) (push) Successful in 15s
build-and-release / build (companies) (push) Successful in 18s
build-and-release / build (developers) (push) Successful in 17s
build-and-release / build (catering-services) (push) Successful in 19s
build-and-release / build (fitness-trainers) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (gateway) (push) Successful in 6s
build-and-release / build (job-seekers) (push) Successful in 6s
build-and-release / build (makeup-artists) (push) Successful in 8s
build-and-release / build (jobs) (push) Successful in 9s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (photographers) (push) Successful in 6s
build-and-release / build (social-media-managers) (push) Successful in 7s
build-and-release / build (ugc-content-creators) (push) Successful in 7s
build-and-release / build (tutors) (push) Successful in 8s
build-and-release / build (video-editors) (push) Successful in 7s
build-and-release / build (users) (push) Successful in 9s
Covers everything that needs a running DB/server:
- Deploy the backend changes (wallet holds, printpdf upgrade)
- Verify /wallet/me/holds endpoint with curl
- cargo audit + cargo deny check
- Schemathesis API fuzzing setup
- Playwright e2e tests
- CI integration tests with TEST_DATABASE_URL
- npm run lint and tsc --noEmit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-12 13:39:36 +02:00
Tracewebstudio Dev
8423b7a0f5 fix: security, wallet holds, printpdf upgrade
- Upgrade printpdf 0.7 → 0.12.5 in job_seekers (fixes lopdf HIGH CVE RUSTSEC-2023-0068)
  Rewrote build_resume_pdf() for the new Op-based API; same PDF output
- Add wallet/me/holds and wallet/me/holds/{id}/release routes to profession_shared
  Backed by wallet::hold::list_for_user and wallet::hold::release
  Applies to all 9 profession services via the shared router
- Add deny.toml for cargo-deny (advisory + ban policy enforcement)
  RSA timing CVE and bincode unmaintained acknowledged with documented reasons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-12 13:38:17 +02:00
Ashwin Kumar Sivakumar
ddb2110e86 fix: return JSON errors from create_job and add admin job-slot grant endpoint
All checks were successful
build-and-release / build (customers) (push) Successful in 8s
build-and-release / build (catering-services) (push) Successful in 11s
build-and-release / build (fitness-trainers) (push) Successful in 7s
build-and-release / build (developers) (push) Successful in 12s
build-and-release / build (cron) (push) Successful in 18s
build-and-release / build (employees) (push) Successful in 16s
build-and-release / build (gateway) (push) Successful in 8s
build-and-release / build (job-seekers) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 7s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (makeup-artists) (push) Successful in 6s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (tutors) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 5s
build-and-release / build (ugc-content-creators) (push) Successful in 6s
build-and-release / build (users) (push) Successful in 5s
build-and-release / build (video-editors) (push) Successful in 4s
build-and-release / build (companies) (push) Successful in 1m21s
create_job returned plain-text bodies on every failure branch except
quota-exhausted, so the frontend's res.json() silently failed and always
showed a generic "Failed to create job" message regardless of the real
cause (company not approved, quota exhausted, DB error).

Also adds PATCH /api/admin/companies/{id}/job-slots as a manual top-up
for purchased_job_slots, since no self-serve purchase flow exists yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 17:04:00 +05:30
Ashwin Kumar Sivakumar
b281eb5278 fix: profile approval 500 — users table has no role column
All checks were successful
build-and-release / build (employees) (push) Successful in 11s
build-and-release / build (cron) (push) Successful in 13s
build-and-release / build (catering-services) (push) Successful in 17s
build-and-release / build (companies) (push) Successful in 17s
build-and-release / build (customers) (push) Successful in 17s
build-and-release / build (developers) (push) Successful in 16s
build-and-release / build (fitness-trainers) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (gateway) (push) Successful in 7s
build-and-release / build (job-seekers) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 8s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (makeup-artists) (push) Successful in 10s
build-and-release / build (photographers) (push) Successful in 7s
build-and-release / build (social-media-managers) (push) Successful in 8s
build-and-release / build (tutors) (push) Successful in 8s
build-and-release / build (ugc-content-creators) (push) Successful in 7s
build-and-release / build (video-editors) (push) Successful in 8s
build-and-release / build (users) (push) Successful in 2m45s
activate_profile_after_final_approval ran UPDATE users SET role = $1,
but users only has role_id (FK into roles), never a plain role column.
Resolve the role's id via RoleRepository::get_by_key first and update
role_id instead, matching how role is tracked everywhere else.
2026-07-31 16:52:33 +05:30
Tracewebstudio Dev
2f47308498 fix: verification submission no longer blocks login or misreports status
All checks were successful
build-and-release / build (customers) (push) Successful in 12s
build-and-release / build (catering-services) (push) Successful in 15s
build-and-release / build (developers) (push) Successful in 13s
build-and-release / build (cron) (push) Successful in 17s
build-and-release / build (employees) (push) Successful in 5s
build-and-release / build (gateway) (push) Successful in 5s
build-and-release / build (fitness-trainers) (push) Successful in 6s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (makeup-artists) (push) Successful in 5s
build-and-release / build (jobs) (push) Successful in 8s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (photographers) (push) Successful in 7s
build-and-release / build (social-media-managers) (push) Successful in 5s
build-and-release / build (tutors) (push) Successful in 6s
build-and-release / build (ugc-content-creators) (push) Successful in 5s
build-and-release / build (video-editors) (push) Successful in 4s
build-and-release / build (companies) (push) Successful in 1m10s
build-and-release / build (job-seekers) (push) Successful in 1m8s
build-and-release / build (users) (push) Successful in 3m7s
- submit_for_verification: stop downgrading user_role_assignments.status
  to PENDING. This was locking users out of login while their verification
  was under review. Verification progress is tracked in the verifications
  table; role assignment stays APPROVED throughout.

- verification_status: remove role_assignment_approved from the status
  calculation. Because the assignment now stays APPROVED, using it as a
  proxy was incorrectly overriding PENDING verification status to APPROVED
  immediately after submission.

- companies submit_with_documents: encode document_type from filename
  prefix before '|' separator (set by frontend) rather than file stem;
  add duplicate-verification guard to match users service.

- job_seekers: add get_or_create_job_seeker_profile helper to ensure
  profile row exists before upsert operations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-30 15:36:12 +02:00
Tracewebstudio Dev
5f1b48d27b fix: sync profile approval unlock state
All checks were successful
build-and-release / build (developers) (push) Successful in 9s
build-and-release / build (customers) (push) Successful in 13s
build-and-release / build (companies) (push) Successful in 12s
build-and-release / build (cron) (push) Successful in 15s
build-and-release / build (catering-services) (push) Successful in 14s
build-and-release / build (fitness-trainers) (push) Successful in 6s
build-and-release / build (employees) (push) Successful in 15s
build-and-release / build (gateway) (push) Successful in 6s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (job-seekers) (push) Successful in 6s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 8s
build-and-release / build (makeup-artists) (push) Successful in 9s
build-and-release / build (photographers) (push) Successful in 6s
build-and-release / build (social-media-managers) (push) Successful in 7s
build-and-release / build (ugc-content-creators) (push) Successful in 6s
build-and-release / build (tutors) (push) Successful in 8s
build-and-release / build (video-editors) (push) Successful in 9s
build-and-release / build (users) (push) Successful in 6m14s
2026-07-29 15:14:37 +02:00
Ashwin Kumar Sivakumar
2da1ebc8be Fix COMPANY verification snapshot dropping every field but company_name
All checks were successful
build-and-release / build (catering-services) (push) Successful in 13s
build-and-release / build (cron) (push) Successful in 12s
build-and-release / build (customers) (push) Successful in 13s
build-and-release / build (companies) (push) Successful in 16s
build-and-release / build (developers) (push) Successful in 6s
build-and-release / build (fitness-trainers) (push) Successful in 6s
build-and-release / build (employees) (push) Successful in 6s
build-and-release / build (gateway) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 6s
build-and-release / build (job-seekers) (push) Successful in 6s
build-and-release / build (jobs) (push) Successful in 6s
build-and-release / build (makeup-artists) (push) Successful in 6s
build-and-release / build (payments) (push) Successful in 5s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 6s
build-and-release / build (tutors) (push) Successful in 5s
build-and-release / build (ugc-content-creators) (push) Successful in 6s
build-and-release / build (video-editors) (push) Successful in 6s
build-and-release / build (users) (push) Successful in 2m49s
fetch_saved_profile() is the fallback used by submit-for-verification
when the caller doesn't send profile_data (e.g. VerificationStatusPage's
resubmit-after-revision-request flow, which only sends {roleKey}). It's a
separate implementation from GET /api/profile and still had the same
stale COMPANY query that was already fixed there — SELECT company_name
only, discarding email/phone/website/address/city/state/pin/GST. The
admin verification sidebar renders exactly this snapshot, so a resubmit
made every other field vanish from the admin's review view. Select and
return the full column set, matching get_profile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 15:00:25 +05:30
Ashwin Kumar Sivakumar
fd889efa22 Fix wizard-submitted profile data being dropped and wizard schemas saving with no fields
All checks were successful
build-and-release / build (customers) (push) Successful in 11s
build-and-release / build (catering-services) (push) Successful in 13s
build-and-release / build (companies) (push) Successful in 16s
build-and-release / build (developers) (push) Successful in 16s
build-and-release / build (cron) (push) Successful in 17s
build-and-release / build (employees) (push) Successful in 16s
build-and-release / build (fitness-trainers) (push) Successful in 6s
build-and-release / build (gateway) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (job-seekers) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 5s
build-and-release / build (makeup-artists) (push) Successful in 5s
build-and-release / build (payments) (push) Successful in 7s
build-and-release / build (photographers) (push) Successful in 4s
build-and-release / build (tutors) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 6s
build-and-release / build (ugc-content-creators) (push) Successful in 5s
build-and-release / build (video-editors) (push) Successful in 4s
build-and-release / build (users) (push) Successful in 2m31s
- GET /api/profile for COMPANY only ever returned company_name, silently
  discarding every other field the wizard (and PATCH) already saved —
  the profile page looked empty after approval. Select and return all
  company_profiles columns.
- The generic professional-role PATCH path (photographer, tutor, etc.)
  overwrote custom_data wholesale instead of merging, so RoleWizard's two
  sequential PATCH calls (portfolio step, then basic+documents step)
  clobbered each other. Read-merge-write instead, matching the existing
  JOB_SEEKER/CUSTOMER pattern.
- create_onboarding_config now rejects schemas with enableWizardFlow=true
  but no steps, or a non-review step with no fields — the root cause of
  a wizard rendering with nothing to fill in.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 11:46:03 +05:30
Ashwin Kumar Sivakumar
a24e03fd02 Add runtime-config foundation for verification wizard + field locking
All checks were successful
build-and-release / build (cron) (push) Successful in 1m1s
build-and-release / build (catering-services) (push) Successful in 1m40s
build-and-release / build (companies) (push) Successful in 1m50s
build-and-release / build (employees) (push) Successful in 2m19s
build-and-release / build (developers) (push) Successful in 2m23s
build-and-release / build (customers) (push) Successful in 2m27s
build-and-release / build (gateway) (push) Successful in 1m4s
build-and-release / build (graphic-designers) (push) Successful in 57s
build-and-release / build (jobs) (push) Successful in 47s
build-and-release / build (makeup-artists) (push) Successful in 50s
build-and-release / build (fitness-trainers) (push) Successful in 2m37s
build-and-release / build (photographers) (push) Successful in 1m39s
build-and-release / build (payments) (push) Successful in 2m4s
build-and-release / build (job-seekers) (push) Successful in 2m56s
build-and-release / build (social-media-managers) (push) Successful in 2m25s
build-and-release / build (tutors) (push) Successful in 2m52s
build-and-release / build (ugc-content-creators) (push) Successful in 3m0s
build-and-release / build (video-editors) (push) Successful in 2m3s
build-and-release / build (users) (push) Successful in 4m20s
Reconciles onboarding_configs.schema_json (previously orphaned seed data
using a field vocabulary that didn't match production) with the live
profile-fields-config.ts field keys, and extends the schema with
lockAfterApproval flags, step types, and a per-role portfolioModel so
the frontend wizard is entirely schema-driven rather than hardcoded per
role.

Also: closes an unauthenticated write on the onboarding/dashboard
config create endpoints (require_admin was missing), and adds
server-side enforcement in save_profile rejecting changes to any field
marked lockAfterApproval once a profile is APPROVED — the UI already
disables these inputs, this stops a direct API call from bypassing it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 20:10:15 +05:30
Ashwin Kumar Sivakumar
80acfdd64f Add presigned document URLs to stop leaking permanent Backblaze links
All checks were successful
build-and-release / build (employees) (push) Successful in 1m34s
build-and-release / build (catering-services) (push) Successful in 1m43s
build-and-release / build (cron) (push) Successful in 1m59s
build-and-release / build (companies) (push) Successful in 2m4s
build-and-release / build (customers) (push) Successful in 2m26s
build-and-release / build (developers) (push) Successful in 2m38s
build-and-release / build (fitness-trainers) (push) Successful in 1m31s
build-and-release / build (gateway) (push) Successful in 1m29s
build-and-release / build (jobs) (push) Successful in 1m9s
build-and-release / build (graphic-designers) (push) Successful in 2m19s
build-and-release / build (job-seekers) (push) Successful in 2m16s
build-and-release / build (photographers) (push) Successful in 1m43s
build-and-release / build (makeup-artists) (push) Successful in 3m3s
build-and-release / build (social-media-managers) (push) Successful in 2m36s
build-and-release / build (ugc-content-creators) (push) Successful in 2m46s
build-and-release / build (payments) (push) Successful in 4m15s
build-and-release / build (video-editors) (push) Successful in 2m53s
build-and-release / build (tutors) (push) Successful in 4m24s
build-and-release / build (users) (push) Successful in 7m40s
Verification documents were being stored/rendered as permanent, unsigned
B2 URLs across every role's admin review and self-service dashboard.
Add StorageClient::presign() plus two mediating endpoints (admin and
self-service) so viewers always get a short-lived signed URL instead of
the raw storage link.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 18:07:15 +05:30
Ashwin Kumar Sivakumar
cd695514fe Surface real B2 upload errors and enforce 10MB body limit on document uploads
All checks were successful
build-and-release / build (developers) (push) Successful in 1m27s
build-and-release / build (employees) (push) Successful in 1m52s
build-and-release / build (customers) (push) Successful in 1m56s
build-and-release / build (cron) (push) Successful in 2m16s
build-and-release / build (companies) (push) Successful in 2m21s
build-and-release / build (catering-services) (push) Successful in 2m29s
build-and-release / build (gateway) (push) Successful in 39s
build-and-release / build (fitness-trainers) (push) Successful in 2m0s
build-and-release / build (graphic-designers) (push) Successful in 1m52s
build-and-release / build (jobs) (push) Successful in 1m34s
build-and-release / build (job-seekers) (push) Successful in 2m4s
build-and-release / build (photographers) (push) Successful in 1m35s
build-and-release / build (makeup-artists) (push) Successful in 2m41s
build-and-release / build (payments) (push) Successful in 3m15s
build-and-release / build (tutors) (push) Successful in 2m16s
build-and-release / build (social-media-managers) (push) Successful in 2m33s
build-and-release / build (ugc-content-creators) (push) Successful in 2m47s
build-and-release / build (video-editors) (push) Successful in 2m32s
build-and-release / build (users) (push) Successful in 4m11s
Document upload endpoints (job_seekers, customers, companies, and the
profession_shared crate used by 10 profession apps) returned a generic
"File upload failed" 500 on any storage error, hiding the actual cause
from both the API response and (for job_seekers/companies) the server
logs, which only printed anyhow's outer context via Display instead of
the full error chain via Debug.

Also add an explicit DefaultBodyLimit(11MB) to every affected app's
router — none had one, so axum's implicit 2MB default could silently
reject uploads under the UI's advertised 10MB cap.
2026-07-26 19:25:53 +05:30
Ashwin Kumar Sivakumar
da8188563f Fix job seeker document upload 404ing before profile is saved
All checks were successful
build-and-release / build (companies) (push) Successful in 10s
build-and-release / build (developers) (push) Successful in 9s
build-and-release / build (customers) (push) Successful in 11s
build-and-release / build (catering-services) (push) Successful in 14s
build-and-release / build (cron) (push) Successful in 15s
build-and-release / build (fitness-trainers) (push) Successful in 5s
build-and-release / build (employees) (push) Successful in 12s
build-and-release / build (gateway) (push) Successful in 4s
build-and-release / build (graphic-designers) (push) Successful in 6s
build-and-release / build (makeup-artists) (push) Successful in 5s
build-and-release / build (jobs) (push) Successful in 8s
build-and-release / build (payments) (push) Successful in 8s
build-and-release / build (photographers) (push) Successful in 6s
build-and-release / build (tutors) (push) Successful in 6s
build-and-release / build (social-media-managers) (push) Successful in 8s
build-and-release / build (ugc-content-creators) (push) Successful in 8s
build-and-release / build (video-editors) (push) Successful in 7s
build-and-release / build (users) (push) Successful in 8s
build-and-release / build (job-seekers) (push) Successful in 1m8s
upload_document and list_documents both required a pre-existing
job_seeker_profiles row, but nothing creates that row until the user
clicks "Save" on the Basic Information tab. A job seeker who opened
the Documents tab first (or never saved Basic Info) got every upload
silently rejected with a 404, and the earlier fix in frontend-solid
(070c4bd) only addressed a different bug in the dashboard widget - it
never touched this upload path.

upload_document now lazily creates a blank profile row on first
upload instead of 404ing (uploading a verification document doesn't
depend on basic profile fields being filled in). list_documents now
returns an empty list instead of erroring when no profile exists yet,
since that's just the normal empty state for a new job seeker.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 23:44:44 +05:30
Ashwin Kumar Sivakumar
fdd5c7a418 Fix security audit findings: admin authz, captcha, secret leak, CORS
All checks were successful
build-and-release / build (developers) (push) Successful in 5m31s
build-and-release / build (catering-services) (push) Successful in 5m56s
build-and-release / build (employees) (push) Successful in 6m23s
build-and-release / build (companies) (push) Successful in 6m31s
build-and-release / build (cron) (push) Successful in 6m38s
build-and-release / build (customers) (push) Successful in 7m19s
build-and-release / build (gateway) (push) Successful in 1m28s
build-and-release / build (fitness-trainers) (push) Successful in 2m2s
build-and-release / build (jobs) (push) Successful in 1m12s
build-and-release / build (graphic-designers) (push) Successful in 2m20s
build-and-release / build (job-seekers) (push) Successful in 2m24s
build-and-release / build (photographers) (push) Successful in 2m20s
build-and-release / build (makeup-artists) (push) Successful in 2m55s
build-and-release / build (tutors) (push) Successful in 2m15s
build-and-release / build (payments) (push) Successful in 3m48s
build-and-release / build (ugc-content-creators) (push) Successful in 2m35s
build-and-release / build (social-media-managers) (push) Successful in 4m46s
build-and-release / build (video-editors) (push) Successful in 2m41s
build-and-release / build (users) (push) Successful in 8m7s
- Require admin role on role/module/permission management endpoints
  that previously accepted any authenticated user (privilege escalation)
- Add server-side captcha generation/verification (Redis-backed,
  single-use, 5 min TTL) enforced on register/login for users and
  employees services
- Untrack .env.test111 (contained a live SMTP key) and harden
  .gitignore against future .env commits
- Stop logging OTP codes in plaintext
- Restrict jobs service CORS to an explicit origin allowlist
- Mask PayU merchant secret/salt in payment-gateway-config responses,
  preserving the stored value on save when the field is left unchanged
- Bump vulnerable transitive dependencies (quinn-proto, rustls-webpki,
  anyhow) via cargo update; switch aws-sdk-s3 off the legacy rustls
  feature

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 17:31:48 +05:30
Ashwin Kumar Sivakumar
73beb03368 fix: seed verification_status into JOB_SEEKER dashboard widgets
All checks were successful
build-and-release / build (catering-services) (push) Successful in 1m45s
build-and-release / build (developers) (push) Successful in 1m43s
build-and-release / build (companies) (push) Successful in 1m53s
build-and-release / build (cron) (push) Successful in 2m9s
build-and-release / build (customers) (push) Successful in 2m27s
build-and-release / build (employees) (push) Successful in 1m46s
build-and-release / build (fitness-trainers) (push) Successful in 1m48s
build-and-release / build (graphic-designers) (push) Successful in 1m47s
build-and-release / build (jobs) (push) Successful in 1m32s
build-and-release / build (gateway) (push) Successful in 2m21s
build-and-release / build (job-seekers) (push) Successful in 2m53s
build-and-release / build (photographers) (push) Successful in 1m32s
build-and-release / build (makeup-artists) (push) Successful in 1m58s
build-and-release / build (social-media-managers) (push) Successful in 2m17s
build-and-release / build (payments) (push) Successful in 2m51s
build-and-release / build (tutors) (push) Successful in 2m38s
build-and-release / build (ugc-content-creators) (push) Successful in 2m41s
build-and-release / build (video-editors) (push) Successful in 2m39s
build-and-release / build (users) (push) Successful in 4m13s
The live role_sidebar_configs row for JOB_SEEKER never included this
widget, so the verification status badge never rendered. Also applied
directly to the live DB row (this seed file wasn't wired into any
auto-run migration path, so editing it alone wouldn't have taken effect).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 04:26:05 +05:30
Ashwin Kumar Sivakumar
c1eed2530d fix: stop leaking raw LiteLLM error bodies; harden prompts against injection; fix UTF-8 panic
All checks were successful
build-and-release / build (companies) (push) Successful in 4s
build-and-release / build (catering-services) (push) Successful in 6s
build-and-release / build (developers) (push) Successful in 7s
build-and-release / build (customers) (push) Successful in 8s
build-and-release / build (employees) (push) Successful in 8s
build-and-release / build (fitness-trainers) (push) Successful in 6s
build-and-release / build (graphic-designers) (push) Successful in 5s
build-and-release / build (gateway) (push) Successful in 8s
build-and-release / build (job-seekers) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 7s
build-and-release / build (makeup-artists) (push) Successful in 5s
build-and-release / build (payments) (push) Successful in 5s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 6s
build-and-release / build (tutors) (push) Successful in 6s
build-and-release / build (ugc-content-creators) (push) Successful in 5s
build-and-release / build (video-editors) (push) Successful in 5s
build-and-release / build (cron) (push) Successful in 51s
build-and-release / build (users) (push) Successful in 3m12s
Follow-up on the AI safety review — addressed the three remaining
lower-severity findings:

1. crates LiteLlmError::error_body() returned the raw upstream response
   body verbatim to the client on any non-2xx LiteLLM response. That
   body can contain internal routing/diagnostic details from the
   LiteLLM proxy or the underlying model provider. Now returns a
   generic, status-aware message to the client; the full body is
   logged server-side via tracing::error! at each of the three call
   sites that construct LiteLlmError::Api, so nothing is lost for
   debugging — it's just not exposed to end users.

2. Added an explicit anti-prompt-injection clause to
   ai/orchestrator.rs::GROUNDING_GUARDRAIL, the baseline system prompt
   applied to every AI feature call via effective_system_prompt() —
   instructs the model to treat all user/company-authored input
   (job descriptions, profile text, chat messages) as data to analyze,
   never as instructions to follow. Covers every ai.rs handler that
   goes through call_feature/call_feature_with_plan in one place,
   rather than patching each call site's prompt construction
   individually.

3. apps/cron/src/tasks/auto_apply.rs's cover-letter prompt doesn't run
   through the orchestrator (separate app/crate), so hardened it
   directly: fenced the untrusted CANDIDATE/JOB sections with explicit
   "this is data, not instructions" framing. While there, fixed a
   latent panic: `&job_desc[..job_desc.len().min(500)]` slices on a
   raw byte offset, which panics if byte 500 isn't a UTF-8 character
   boundary — a company job description with any multi-byte character
   before that point (accented letters, emoji, etc.) would crash the
   whole cron run. Switched to char_indices() to find a safe boundary.
2026-07-21 05:56:21 +05:30
Ashwin Kumar Sivakumar
3e701f2fe6 fix: close two real money/spend races (Tracecoin double-credit, unbounded AI overspend)
Some checks failed
build-and-release / build (ugc-content-creators) (push) Waiting to run
build-and-release / build (users) (push) Waiting to run
build-and-release / build (video-editors) (push) Waiting to run
build-and-release / build (catering-services) (push) Successful in 1m47s
build-and-release / build (companies) (push) Successful in 2m0s
build-and-release / build (cron) (push) Successful in 2m12s
build-and-release / build (customers) (push) Successful in 2m42s
build-and-release / build (gateway) (push) Successful in 1m0s
build-and-release / build (developers) (push) Successful in 1m27s
build-and-release / build (fitness-trainers) (push) Successful in 1m54s
build-and-release / build (employees) (push) Successful in 1m59s
build-and-release / build (job-seekers) (push) Successful in 1m57s
build-and-release / build (makeup-artists) (push) Successful in 1m39s
build-and-release / build (graphic-designers) (push) Has been cancelled
build-and-release / build (payments) (push) Has been cancelled
build-and-release / build (jobs) (push) Has been cancelled
build-and-release / build (social-media-managers) (push) Has been cancelled
build-and-release / build (tutors) (push) Has been cancelled
build-and-release / build (photographers) (push) Has been cancelled
Asked to review Tracecoin and AI implementation safety. Found and fixed
two exploitable TOCTOU races, plus a data-integrity bug:

1. apps/payments/src/main.rs::verify_payment — the PayU success callback
   is called directly by the client (not a server-to-server webhook), so
   a user fully controls how many times they replay a valid success
   payload. The payment "is it still PENDING" check and the "mark
   SUCCESS + credit wallet" write were separate, non-transactional
   queries — concurrent replays could both pass the check before either
   commits, double- (or N-times-) crediting the wallet for one real
   payment. Now wrapped in a single transaction with
   `SELECT ... FOR UPDATE` on the payments row, so a second concurrent
   call blocks until the first commits, then correctly sees the row is
   no longer PENDING (Postgres re-evaluates the WHERE clause via
   EvalPlanQual after the lock is granted).

2. crates/db/src/models/ai/repository.rs — UserAiSubscriptionRepository
   had the exact same shape of bug: apps/users/src/ai/credits.rs::
   charge_feature read the subscription, checked daily-limit and credit
   balance, THEN issued two separate unconditional `UPDATE ... SET x =
   x + $1` statements with no WHERE guard on the balance. N concurrent
   requests from one user all pass the check before any deduction
   lands, running up unlimited LLM API spend (this endpoint is called
   before/around real LiteLLM calls, so the cost is real). Added
   UserAiSubscriptionRepository::try_charge — a single conditional
   UPDATE that checks the daily limit and credit balance and deducts
   atomically, returning None (mapped to the existing error types) if
   either check fails.

3. apps/cron/src/tasks/auto_apply.rs — daily_actions_used was being
   incremented twice per auto-applied job (once in the credit-deduct
   UPDATE, once more in a second, redundant UPDATE right after) —
   silently halving job seekers' effective daily auto-apply limit.
   Removed the redundant second UPDATE.

Also added non-negative CHECK constraints directly to the live
database (tracecoin_wallets.balance/reserved,
user_ai_subscriptions.daily_actions_used/monthly_credits_used/
purchased_credits_used) as defense in depth — belt-and-suspenders in
case a future code path reintroduces a similar bug.
2026-07-21 05:51:23 +05:30
Ashwin Kumar Sivakumar
92ce2d2a86 fix: notification insert used wrong column name; log silent email failures
All checks were successful
build-and-release / build (catering-services) (push) Successful in 15s
build-and-release / build (cron) (push) Successful in 13s
build-and-release / build (developers) (push) Successful in 10s
build-and-release / build (employees) (push) Successful in 8s
build-and-release / build (fitness-trainers) (push) Successful in 5s
build-and-release / build (gateway) (push) Successful in 5s
build-and-release / build (graphic-designers) (push) Successful in 5s
build-and-release / build (job-seekers) (push) Successful in 5s
build-and-release / build (jobs) (push) Successful in 6s
build-and-release / build (payments) (push) Successful in 4s
build-and-release / build (makeup-artists) (push) Successful in 6s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 6s
build-and-release / build (tutors) (push) Successful in 6s
build-and-release / build (ugc-content-creators) (push) Successful in 6s
build-and-release / build (video-editors) (push) Successful in 6s
build-and-release / build (companies) (push) Successful in 2m9s
build-and-release / build (customers) (push) Successful in 2m47s
build-and-release / build (users) (push) Successful in 3m16s
Found while investigating "notifications/emails not working on approve
or job posting":

1. Real bug: apps/companies/src/handlers/mod.rs::view_contact (company
   viewing an applicant's contact info) inserted into notifications
   using column name `notification_type`, which has never existed —
   the column is `type`. This INSERT has been failing outright every
   time a company views a contact.

2. Root cause for approvals specifically: verifications/approval_requests
   never existed until earlier this session (see
   20260718200000_create_verifications_and_approvals) — every admin
   approve/reject action was failing at the DB layer before it ever
   reached the notification/email code, so nothing in this area could
   have worked regardless of the email/notification logic itself.

3. Observability gap: every `state.mail.send_*_email(...)` call site
   silently discarded its Result (`let _ = ...`), so if the SMTP/
   Zeptomail provider is unconfigured (crates/email::Mailer already
   logs a clear warning at startup for that, but callers gave no
   per-send signal) or a send fails for any other reason, there was no
   way to see it happen. Added `tracing::error!` logging on failure
   for every job/approval-related email: job submitted, job approved,
   job rejected, requirement approved, profile approval
   approved/rejected, requirement submitted. Doesn't change delivery —
   if the environment has no EMAIL_PROVIDER/SMTP_*/ZEPTOMAIL_*
   configured, sends still fail, but that failure is now visible in
   logs instead of silent.

In-app notifications for approvals were already schema-correct
(job/profile/requirement approve+reject all insert into notifications
with the right columns) — the two real defects were #1 and #2 above.
2026-07-21 04:30:34 +05:30
Ashwin Kumar Sivakumar
c570d7df67 chore: remove the dead apps/leads microservice
apps/leads implemented its own, independent "lead request" system
(POST /api/leads, /api/lead-requests/send, accept/reject) with a
schema that never matched the live one (message vs remarks, no
professional_user_id, accepted_at/rejected_at instead of resolved_at —
see 20260721030000_create_lead_requests's commit message). Confirmed
unreachable: the frontend's live flows use apps/customers'
/api/customers/requirements and each profession's /leads/request
(crates/contracts::profession_shared), never anything under
apps/leads' own paths. Its /api/lead-requests/* endpoints weren't even
reachable through the gateway (wrong prefix, never matched
/api/leads or /api/admin/leads).

Removed:
- apps/leads/ entirely, and its Cargo.toml workspace membership
- the `leads` docker-compose service, its LEADS_SERVICE_URL env var on
  gateway, and gateway's depends_on entry
- the `leads` entry from both CI build matrices (.gitea/.forgejo)
- gateway's leads_url field/routing branch — gateway no longer hard-
  requires LEADS_SERVICE_URL to boot (.expect() would have panicked
  once the service was gone); /api/admin/leads now falls through to
  the customers service, which already had a matching (previously
  shadowed) branch for it

NOTE: this service may still have a live Deployment/Service in
nxtgauge-gitops (a separate repo not touched here) — that manifest
should be removed too, or the next deploy will reference an image that
no CI job builds anymore.
2026-07-21 04:19:56 +05:30
Ashwin Kumar Sivakumar
9309334d52 seed: add a starter Tracecoin package per role; fix paise unit bug in invoice line item
Seeds one TRACECOIN_BUNDLE package per role (100 Tracecoins / ₹250,
placeholder pricing per your instruction) so the purchase flow
(CreditsPage.tsx -> GET /api/packages, filtered client-side by
role_key) has something to show instead of "No packages available for
your role," and so create_order/verify_payment/invoice generation can
actually be exercised end-to-end. Idempotent (WHERE NOT EXISTS guard)
so it's safe against the custom db-migrate runner re-executing every
file on every deploy. Adjust real pricing later via the existing admin
CRUD (POST/PATCH /api/packages) — no new endpoint needed, it already
works.

Also fixes a unit bug introduced in the invoice wiring last commit:
payments.amount_inr is already paise (copied straight from
pricing_packages.price_inr, which is paise despite the name — PayU
order creation divides it by 100 via paise_to_rupee_string), but the
invoice line item multiplied it by 100 again, making every generated
invoice show a 100x inflated amount.
2026-07-21 03:47:54 +05:30
Ashwin Kumar Sivakumar
68f659903c fix: audit_logs, tax_rules, reviews tables never existed; fix wrong column in review creation
Finished the sweep of every table referenced by live Rust code but not
created by any active migration:

- audit_logs / audit_log_changes — backs wallet::audit() (crates/wallet),
  called from every admin Tracecoin balance adjustment
  (apps/payments/src/admin.rs::adjust_credits). actor_type defaulted since
  the only caller doesn't supply it.
- tax_rules — backs the admin tax-rule CRUD in apps/payments/src/admin.rs.
  init-db.sql has a differently-named version (title/percentage/
  applicable_to); schema here matches the live code's actual columns
  (name/tax_rate/applies_to).
- reviews — backs the professional review/rating system
  (apps/users/src/handlers/reviews.rs).

Also fixed a real bug found while building the reviews schema:
admin_create_review's customer-lookup subquery selected
`lead_requests.user_id`, a column that has never existed (it's
`customer_user_id`) — would have failed on the very first review
creation attempt now that lead_requests actually exists.

Checked every other previously-flagged table (permissions, orders,
external_roles, internal_roles, kb_*, notification_*, order_items,
portfolio_images, smtp_configs, dashboard_widgets,
verification_documents) against actual Rust usage — none of them are
referenced by a real SQL query (grep hits were all Rust variable/type
names), so nothing to fix there. Full migration-chain sweep confirms
zero remaining "references a table before it's created" issues.
2026-07-21 03:05:14 +05:30
Ashwin Kumar Sivakumar
63fd3f5135 feat: generate a GST invoice automatically after a successful Tracecoin/PayU purchase
crates/invoice (InvoiceService, GST computation, HTML rendering) was
fully built but never wired to anything and never had its tables —
invoices/invoice_line_items/billing_profiles/invoice_number_seq never
existed in any active migration (same root cause as everything else
this session: only in scripts/init-db.sql, which the real db-migrate
job never runs). Created them, matching InvoiceService's actual
columns exactly rather than init-db.sql's older, simpler invoices
shape.

Fixed a real bug in InvoiceService::create while at it: four money
fields (total, and three line-item amounts) were bound as i64 against
columns/read-models that are i32 everywhere else — would have failed
every insert with a Postgres type mismatch the first time this code
ever actually ran against a real table.

Wired invoice generation into apps/payments' PayU verify_payment
handler (the actual success callback) — right after the wallet is
credited, a one-line-item GST invoice is generated from the purchased
package and PayU's billing fields (firstname/email/phone), using a
new INVOICE_SELLER_* env-configurable seller identity. Generation
failures are logged, not surfaced to the buyer, since the payment and
wallet credit have already succeeded by that point.

Also added the missing user-facing endpoints to fetch what got
generated: GET /api/payments/invoices (list) and
GET /api/payments/invoices/{id} (detail + line items) — previously
only admin-side invoice viewing existed.

NOTE: the frontend (nxtgauge-frontend-solid, a separate repo) has an
existing invoice-viewing page at src/routes/dashboard/wallet/invoices/
but it calls /wallet/me/invoices (no /api/ prefix) via a different,
apparently-dead API helper (api.get, not apiFetch) that every other
live page avoids — same dead-code pattern as the earlier apps/leads
discovery. The live purchase flow (CreditsPage.tsx) has no invoice UI
at all yet. Not fixed here since it's out of this repo's scope this
session — flagging for a frontend pass.
2026-07-21 03:02:47 +05:30
Ashwin Kumar Sivakumar
119c39e184 fix: payments/pricing_packages tables never existed either
Same root cause, found via a systematic diff of every table in
scripts/init-db.sql against what active migrations actually create:
payments and pricing_packages (the Tracecoin/job-slot purchase flow,
apps/payments) were never created by any active migration.
20260317202300_coupons_discounts.up.sql (already deployed) references
payments(id) via FK a few hours later the same day — the earliest
still-live failure point for this table.

Schema uses the final PayU column names (payu_txnid/payu_mihpayid)
directly, matching what apps/payments/src/main.rs actually reads/writes
today, rather than init-db.sql's stale pre-rename Razorpay names —
20260626000000_payu_rename_columns (already deployed) only renames
columns that exist, so starting with the final names outright is
correct and makes that migration a safe no-op.
2026-07-21 02:28:57 +05:30
Ashwin Kumar Sivakumar
36c555dc55 fix: verifications/approval_requests tables never existed — the single biggest gap
The most severe finding in this audit: `verifications` backs the entire
admin approval system this whole session's work depends on (every
role's profile submission, job posting, and requirement approval flow
goes through VerificationRepository). No active migration ever created
it, verification_logs, or approval_requests/approval_logs.

The correct schema for all of these already existed in
scripts/init-db.sql — a "complete schema" reference doc — but that file
is never actually executed by the real migration runner: Dockerfile.migrate
only COPYs crates/db/migrations into the image, and crates/db-migrate's
main.rs only ever reads that directory. init-db.sql has been dead
documentation this whole time, describing the schema this codebase
*should* have without any path to actually get there.

Cross-referencing init-db.sql against the Rust code that reads/writes
these tables found two bugs in init-db.sql itself:
  - verification_logs.verification_request_id pointed at a separate,
    unrelated `verification_requests` table instead of `verifications`
    (already independently discovered and patched by an existing
    migration, 20260718210823_fix_verification_logs_fk — this new
    migration just creates it correctly from the start).
  - approval_requests was missing UNIQUE(entity_type, entity_id), which
    apps/users/src/handlers/verifications.rs's
    `INSERT ... ON CONFLICT (entity_type, entity_id) DO UPDATE` requires.

Positioned before the earliest active migration that already assumed
these tables existed (20260718210823). The custom db-migrate runner
(crates/db-migrate) has no applied-migration tracking — it just re-runs
every *.up.sql file in filename order on every invocation — so there's
no "already applied, don't touch" risk from adding an earlier-dated
migration; idempotent IF NOT EXISTS guards make it safe regardless.
2026-07-21 02:26:15 +05:30
Ashwin Kumar Sivakumar
442dac8c04 fix: portfolio_items/services tables never existed; wallet ledger read used stale column names
Continuing the migration-chain audit: professional portfolio/services
management (PortfolioPage.tsx -> /api/{profession}/portfolio/me,
/api/{profession}/services) was broken the same way as lead_requests —
portfolio_items and services were only ever defined in a disabled .skip
migration (keyed differently: user_id + profession_key, vs. the
user_role_profile_id every live query in
crates/db/src/models/professional.rs actually uses). Self-created both
tables with the schema the live code needs, in the same already-pushed
migration (20260317195000_profession_specific_profiles.up.sql) that was
unconditionally ALTERing them.

Also fixed TracecoinLedgerEntry (professional.rs) — its `type`/`reason`
fields didn't match the `transaction_type`/`reference_type` columns
TracecoinWalletRepository actually writes (see previous commit), so
GET /api/{profession}/wallet/ledger would have failed to deserialize
every row. professional.rs has its own duplicate, unreachable
try_reserve/debit/release_tracecoins using the old type/reason names —
left alone since nothing calls them (send_lead_request and friends all
go through TracecoinWalletRepository).
2026-07-21 02:21:52 +05:30
Ashwin Kumar Sivakumar
94a8a1096a fix: three more already-deployed migrations unconditionally ALTERed tables that were never created
Following the reference_numbers fix, audited every active (non .skip)
migration for the same failure shape — ALTER/CREATE TRIGGER against
lead_requests, tracecoin_wallets, tracecoin_ledger, or job_applications
without ever creating them — since this codebase has a repeated pattern
of table-creation migrations getting silently disabled (renamed .skip)
after the code that depends on them was already written. Found three
more, ALL already pushed to origin, meaning they've likely been
breaking the migration chain since the date each was deployed:

- 20260317195000_profession_specific_profiles.up.sql (Mar 17) — ALTERs
  lead_requests twice. Earliest failure point found for the lead_requests
  chain. Now self-creates a minimal lead_requests table first (no FK to
  `leads`, which isn't created until June 10 — well after this migration).
- 20260318233000_tracecoin_ledger_immutable.up.sql (Mar 18) — creates
  immutability triggers ON tracecoin_ledger, assuming it exists. Now
  self-creates tracecoin_wallets/tracecoin_ledger first, matching the
  column names (transaction_type/reference_type) the Rust code actually
  uses — the only schema that ever existed for these tables (in a
  disabled .skip migration) used stale names (type/reason).
- 20260425000000_ai_usage.up.sql (Apr 25) — ALTERs job_applications
  inside a BEGIN/COMMIT block, so this failure was also rolling back
  company_ai_usage/job_seeker_ai_usage creation in the same file. Now
  self-creates job_applications first.

Each later migration that also touches these tables (this session's
customer/job-seeker/lead_requests fixes) now uses ADD COLUMN IF NOT
EXISTS instead of assuming its own CREATE TABLE ran, so the schema
converges to the same end state regardless of which migration actually
created the table first.

Every touched migration uses IF NOT EXISTS / idempotent guards
throughout, so this is safe to apply whether or not any of these tables
already exist in the real database.
2026-07-21 02:18:23 +05:30
Ashwin Kumar Sivakumar
70e63c6aff fix: lead_requests table never existed, and reference_numbers migration was unconditionally broken
Tracing "can a professional send a request to contact a customer"
surfaced two more breaks in the same family as everything else fixed
this session:

1. crates/db/migrations/20260719120000_reference_numbers.up.sql (not
   yet pushed) unconditionally ran `ALTER TABLE job_applications ...`
   and `ALTER TABLE lead_requests ...`. Neither table was ever created
   by any active (non .skip) migration, so this migration would fail
   outright on a clean apply and block every migration after it in the
   chain — including all of this session's other fixes. Moved the
   reference_number column/trigger/backfill logic for job_applications
   and lead_requests into their own table-creation migrations instead,
   where the tables are guaranteed to exist.

2. lead_requests (LeadRequestRepository, send_lead_request in
   profession_shared.rs, list_requests/approve_request/reject_request
   in apps/customers/src/handlers.rs) was only ever defined in disabled
   .skip migrations, and even those didn't match the columns the
   current Rust code reads/writes (missing professional_user_id and
   reference_number; `message`/`customer_user_id`-only shape instead of
   `remarks`/`requested_at`/`resolved_at`). Added a migration with the
   schema the live code actually needs.

3. Same root cause found for tracecoin_wallets/tracecoin_ledger — only
   ever defined in .skip migrations, and with stale column names
   (`type`/`reason` vs. the `transaction_type`/`reference_type` the
   Tracecoin reservation code in TracecoinWalletRepository actually
   uses. These back the credit reservation that happens when a lead
   request is sent. Created them with CREATE TABLE IF NOT EXISTS (safe
   no-op if they already exist under any name/history) plus a
   best-effort ADD COLUMN IF NOT EXISTS fallback for the stale-name
   scenario.

NOTE: an already-deployed migration (20260318233000_tracecoin_ledger_immutable,
already on origin) creates triggers directly on tracecoin_ledger,
assuming it already exists. That migration was NOT touched here since
editing already-pushed migration history is out of scope for a blind
fix — if tracecoin_ledger doesn't already exist in the real database,
that migration has been failing since it was deployed and needs a
human to check `_sqlx_migrations` / actual schema state directly.
2026-07-21 02:00:23 +05:30
Ashwin Kumar Sivakumar
b5dea58ed4 fix: customer document submission and profile verification
customer_profiles already had custom_data but never got a `status`
column, so — same root cause already fixed for job_seeker_profiles —
the generic profile save/get/submit handlers failed outright for the
CUSTOMER role, and the admin final-approval path couldn't write a
verified status either.

- Migration: add customer_profiles.status (default 'DRAFT').
- Special-case CUSTOMER in the generic profile.rs handlers (get_profile,
  save_profile, fetch_saved_profile, set_profile_status), mirroring the
  existing JOB_SEEKER special-case, storing basic-tab fields under
  custom_data.basic_info.
- Re-enable the CUSTOMER branch in activate_profile_after_final_approval
  now that the status column exists.
- Add the missing POST /api/customers/profile/documents upload endpoint
  — the frontend's document upload (required: Aadhar/Government ID)
  targets this exact path for CUSTOMER and previously 404'd since no
  such route was ever registered. Mirrors the B2-upload-only pattern
  used by the profession apps' shared upload_document handler.

Verified separately: requirement posting (POST /api/customers/requirements)
and requirement submission-for-verification (POST
/api/customers/requirements/:id/submit) already work correctly — both
use the `leads` table (an active migration, despite the model's
"Requirement" naming) and properly create a verification record
(case_type REQUIREMENT_APPROVAL) that lands in admin Verification
Management via the existing approve_requirement/reject_requirement
handlers. No changes needed there.
2026-07-21 01:39:25 +05:30
Ashwin Kumar Sivakumar
4e292efbdf fix: profession admin listing endpoints matched role_key in the wrong case
Each profession's admin list/get endpoint (apps/<role>/src/admin.rs) filtered
user_role_profiles by a lowercase role_key literal (e.g. 'photographer'), but
the app always stores it uppercase ('PHOTOGRAPHER', matching role_to_table in
apps/users/src/handlers/role_meta.rs). These endpoints therefore always
returned an empty list / 404, regardless of how many professionals had
actually submitted. catering_services also had a singular/plural mismatch
('catering_service' vs the stored 'CATERING_SERVICES'). Uppercased (and
correctly pluralized) all 10 literals to match.
2026-07-21 01:28:09 +05:30
Ashwin Kumar Sivakumar
28b09aa019 fix: professionals (photographer, tutor, developer, etc.) couldn't submit profiles/documents
The generic profile save/get/submit handlers (apps/users/src/handlers/profile.rs)
have always read and written every non-COMPANY/JOB_SEEKER role's table via
`... WHERE user_role_profile_id = $1`, but that linking column was never
migrated onto any of the 10 profession tables (photographer_profiles,
tutor_profiles, makeup_artist_profiles, developer_profiles,
video_editor_profiles, graphic_designer_profiles,
social_media_manager_profiles, fitness_trainer_profiles,
catering_service_profiles, ugc_content_creator_profiles) — it only existed
in a migration that was disabled (20260415010003, .skip). Every basic-info
save, document upload persistence, and verification submission for these
professions has therefore always failed with "column
user_role_profile_id does not exist".

Adds the column (+ backfill from existing user_role_profiles rows, +
index) to all 10 tables. No application code changes needed — the Rust
handlers were already written correctly for this schema.
2026-07-21 01:19:49 +05:30
Ashwin Kumar Sivakumar
2e95c4750b fix: company job posting, job seeker profile submission, and job applications
- Company approval wrote profile status to 'ACTIVE' (company_profiles'
  own pre-verification default) using an id column that never matched
  any row, so create_job's APPROVED check always rejected newly
  approved companies. Match on user_id for user_id-keyed tables and
  write the canonical 'APPROVED' status.
- job_seeker_profiles was missing columns the job-seeker app has
  always queried (full_name, location, summary, skills,
  active_application_count, status), and the job_applications /
  job_seeker_documents tables it depends on were never migrated in —
  job seeker profile save/submit and job applications failed outright
  with "column/relation does not exist".
- Renamed the job_seeker first_name/last_name split to full_name to
  match what the frontend has always sent.
- Special-cased JOB_SEEKER in the generic profile.rs handlers (mirrors
  the existing COMPANY special-case) so the shared ProfilePage save/
  submit flow, which was routed through a user_role_profile_id-based
  path job_seeker_profiles never had, now persists correctly.
- Fixed apply_to_job's company notification query joining a
  nonexistent "companies" table instead of company_profiles.
- Fixed auto-apply cron's company status filter to match the
  corrected 'APPROVED' status.
2026-07-21 01:00:47 +05:30
Ashwin Kumar Sivakumar
00923681ec fix: company profile submission stored wrapper object as verification payload
All checks were successful
build-and-release / build (customers) (push) Successful in 8s
build-and-release / build (catering-services) (push) Successful in 11s
build-and-release / build (fitness-trainers) (push) Successful in 4s
build-and-release / build (developers) (push) Successful in 14s
build-and-release / build (gateway) (push) Successful in 5s
build-and-release / build (employees) (push) Successful in 15s
build-and-release / build (cron) (push) Successful in 18s
build-and-release / build (graphic-designers) (push) Successful in 5s
build-and-release / build (job-seekers) (push) Successful in 5s
build-and-release / build (leads) (push) Successful in 6s
build-and-release / build (jobs) (push) Successful in 6s
build-and-release / build (makeup-artists) (push) Successful in 6s
build-and-release / build (payments) (push) Successful in 4s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (social-media-managers) (push) Successful in 4s
build-and-release / build (tutors) (push) Successful in 6s
build-and-release / build (ugc-content-creators) (push) Successful in 4s
build-and-release / build (video-editors) (push) Successful in 4s
build-and-release / build (users) (push) Successful in 6s
build-and-release / build (companies) (push) Successful in 2m2s
submit_with_documents parsed the multipart 'profile' field (shaped
{roleKey, profile_data: {...}} per the frontend wizard) but read fields
like company_name directly off the outer wrapper instead of unwrapping
profile_data first - every company_profiles field ended up blank, and
the double-wrapped object got stored as the verification's payload,
breaking admin-side rendering of company name/details.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 22:03:36 +05:30
Ashwin Kumar Sivakumar
8b648e73d0 chore: trigger build after registry-cleanup fix
All checks were successful
build-and-release / build (companies) (push) Successful in 8s
build-and-release / build (catering-services) (push) Successful in 11s
build-and-release / build (cron) (push) Successful in 53s
build-and-release / build (employees) (push) Successful in 53s
build-and-release / build (graphic-designers) (push) Successful in 13s
build-and-release / build (job-seekers) (push) Successful in 9s
build-and-release / build (jobs) (push) Successful in 11s
build-and-release / build (gateway) (push) Successful in 1m12s
build-and-release / build (customers) (push) Successful in 1m26s
build-and-release / build (developers) (push) Successful in 1m44s
build-and-release / build (fitness-trainers) (push) Successful in 2m12s
build-and-release / build (leads) (push) Successful in 1m30s
build-and-release / build (ugc-content-creators) (push) Successful in 12s
build-and-release / build (photographers) (push) Successful in 1m30s
build-and-release / build (video-editors) (push) Successful in 9s
build-and-release / build (makeup-artists) (push) Successful in 2m27s
build-and-release / build (payments) (push) Successful in 2m39s
build-and-release / build (social-media-managers) (push) Successful in 2m27s
build-and-release / build (tutors) (push) Successful in 2m47s
build-and-release / build (users) (push) Successful in 4m24s
trigger build
2026-07-19 18:15:49 +05:30
Ashwin Kumar Sivakumar
e4c9fc31ee feat: human-readable reference numbers + fix verification document visibility
All checks were successful
build-and-release / build (developers) (push) Successful in 1m42s
build-and-release / build (companies) (push) Successful in 1m48s
build-and-release / build (catering-services) (push) Successful in 1m55s
build-and-release / build (cron) (push) Successful in 2m4s
build-and-release / build (customers) (push) Successful in 2m26s
build-and-release / build (employees) (push) Successful in 1m24s
build-and-release / build (fitness-trainers) (push) Successful in 1m37s
build-and-release / build (gateway) (push) Successful in 1m38s
build-and-release / build (graphic-designers) (push) Successful in 1m54s
build-and-release / build (jobs) (push) Successful in 1m48s
build-and-release / build (leads) (push) Successful in 1m40s
build-and-release / build (job-seekers) (push) Successful in 2m52s
build-and-release / build (photographers) (push) Successful in 1m50s
build-and-release / build (payments) (push) Successful in 2m16s
build-and-release / build (makeup-artists) (push) Successful in 2m48s
build-and-release / build (tutors) (push) Successful in 2m16s
build-and-release / build (social-media-managers) (push) Successful in 2m42s
build-and-release / build (ugc-content-creators) (push) Successful in 2m36s
build-and-release / build (video-editors) (push) Successful in 2m19s
build-and-release / build (users) (push) Successful in 5m10s
Adds a DB-trigger-generated reference_number (NXT-{TYPE}-{YY}-{000001}) to
verifications, support_tickets, payments, job_applications, lead_requests,
and users, replacing raw UUIDs shown to customers/admins. Also fixes
verification-status endpoint to return uploaded documents (previously
omitted, so documents never appeared after submission), and adds a
reference-number lookup endpoint for the AI support assistant.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 16:34:00 +05:30
Ashwin Kumar Sivakumar
dbb02e54cc fix(db): stop wiping employees table on every migration run, restore phone column
All checks were successful
build-and-release / build (catering-services) (push) Successful in 1m43s
build-and-release / build (companies) (push) Successful in 1m56s
build-and-release / build (cron) (push) Successful in 2m10s
build-and-release / build (customers) (push) Successful in 2m29s
build-and-release / build (gateway) (push) Successful in 51s
build-and-release / build (fitness-trainers) (push) Successful in 1m22s
build-and-release / build (developers) (push) Successful in 1m50s
build-and-release / build (employees) (push) Successful in 2m42s
build-and-release / build (job-seekers) (push) Successful in 2m1s
build-and-release / build (jobs) (push) Successful in 2m10s
build-and-release / build (graphic-designers) (push) Successful in 2m51s
build-and-release / build (leads) (push) Successful in 2m22s
build-and-release / build (photographers) (push) Successful in 1m42s
build-and-release / build (makeup-artists) (push) Successful in 2m49s
build-and-release / build (payments) (push) Successful in 2m11s
build-and-release / build (social-media-managers) (push) Successful in 2m33s
build-and-release / build (tutors) (push) Successful in 2m36s
build-and-release / build (ugc-content-creators) (push) Successful in 2m12s
build-and-release / build (video-editors) (push) Successful in 2m38s
build-and-release / build (users) (push) Successful in 4m29s
CRITICAL: 20260402030000_strict_employee_separation.up.sql contained an
unconditional DROP TABLE IF EXISTS employees CASCADE followed by a bare
CREATE TABLE, written as a one-time schema transformation back when it was
authored. The db-migrate tool has no applied-migrations tracking table - it
replays every .sql file on every run - which turned that one-time DROP into
a destructive operation that wipes every employee account (including admin
accounts) on every single migration job run. This is what caused today's
"db error while logging in": the phone column (never present in any tracked
migration, added out-of-band in production at some point) was gone after the
recreate, and every employee row - including the account in use this session
- was deleted.

Fix: make the table creation a plain idempotent CREATE TABLE IF NOT EXISTS
(the standalone-schema transition it performed already happened in
production long ago, so the drop was never needed for correctness going
forward). Add a proper migration for the phone column so it's tracked
instead of relying on an undocumented manual ALTER.

Confirmed via kubectl-verified row count (0) and a pre-incident backup
(2026-07-18T21:00:03Z, predates the destructive run) that the deleted row
is recoverable; restoring it separately as a one-time data fix, not part of
this schema migration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 05:40:48 +05:30
Ashwin Kumar Sivakumar
bb616c6db1 fix(db): make three more migrations idempotent; log swallowed login DB error
All checks were successful
build-and-release / build (customers) (push) Successful in 1m23s
build-and-release / build (employees) (push) Successful in 1m48s
build-and-release / build (catering-services) (push) Successful in 1m57s
build-and-release / build (companies) (push) Successful in 2m6s
build-and-release / build (cron) (push) Successful in 2m23s
build-and-release / build (gateway) (push) Successful in 51s
build-and-release / build (developers) (push) Successful in 2m44s
build-and-release / build (fitness-trainers) (push) Successful in 1m39s
build-and-release / build (jobs) (push) Successful in 45s
build-and-release / build (graphic-designers) (push) Successful in 2m16s
build-and-release / build (job-seekers) (push) Successful in 2m16s
build-and-release / build (leads) (push) Successful in 1m46s
build-and-release / build (payments) (push) Successful in 2m19s
build-and-release / build (makeup-artists) (push) Successful in 3m0s
build-and-release / build (photographers) (push) Successful in 2m42s
build-and-release / build (social-media-managers) (push) Successful in 2m19s
build-and-release / build (ugc-content-creators) (push) Successful in 2m33s
build-and-release / build (tutors) (push) Successful in 2m42s
build-and-release / build (video-editors) (push) Successful in 2m44s
build-and-release / build (users) (push) Successful in 6m38s
Same class of bug as the ai_plans_and_limits fix: ai_credit_packages and
users_litellm_key used plain CREATE TABLE/ADD COLUMN with no re-run guard,
and payu_rename_columns did a bare RENAME COLUMN that fails outright on any
second run ("column razorpay_order_id does not exist"). All three were
discovered by actually running the db-migrate job end to end for the first
time and fixed in the same pass as the verification_logs FK fix - already
baked into the db-migrate image that was built and run manually, this
commit just brings the source in the repo in sync with what's deployed.

Also: apps/employees login handler's DB error was being discarded via
.map_err(|_| ...) with zero logging, making the reported "db error while
logging in" impossible to diagnose from pod logs. Log the real error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 04:40:31 +05:30
Ashwin Kumar Sivakumar
e80ce2901c fix(db): make ai_plans_and_limits migration idempotent
All checks were successful
build-and-release / build (cron) (push) Successful in 48s
build-and-release / build (catering-services) (push) Successful in 1m31s
build-and-release / build (companies) (push) Successful in 1m34s
build-and-release / build (developers) (push) Successful in 1m59s
build-and-release / build (customers) (push) Successful in 2m10s
build-and-release / build (employees) (push) Successful in 2m15s
build-and-release / build (gateway) (push) Successful in 59s
build-and-release / build (jobs) (push) Successful in 46s
build-and-release / build (graphic-designers) (push) Successful in 1m33s
build-and-release / build (fitness-trainers) (push) Successful in 2m44s
build-and-release / build (makeup-artists) (push) Successful in 1m53s
build-and-release / build (job-seekers) (push) Successful in 2m30s
build-and-release / build (leads) (push) Successful in 2m32s
build-and-release / build (photographers) (push) Successful in 2m33s
build-and-release / build (payments) (push) Successful in 2m53s
build-and-release / build (social-media-managers) (push) Successful in 2m38s
build-and-release / build (ugc-content-creators) (push) Successful in 2m19s
build-and-release / build (tutors) (push) Successful in 2m53s
build-and-release / build (video-editors) (push) Successful in 2m14s
build-and-release / build (users) (push) Successful in 4m35s
The db-migrate job (crates/db-migrate) has no applied-migrations tracking
table - it replays every .sql file on every run, relying on each file being
idempotent (IF NOT EXISTS / ON CONFLICT), which is the pattern virtually
every other migration in this directory follows. This one wasn't: plain
CREATE TABLE/CREATE INDEX and two INSERTs with no ON CONFLICT guard.

Since the tables/data already exist from this file's one successful run,
every subsequent migration job run failed immediately on
"relation ai_plans already exists" - before ever reaching any migration
after it, including ones genuinely needed (see the verification_logs FK fix
two commits back). Confirmed via a live job run: this was the actual reason
db-migrate had never completed successfully before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 03:14:02 +05:30
Ashwin Kumar Sivakumar
e2635c1c30 fix(migrate): fix db-migrate build and job manifest, both broken
Some checks failed
build-and-release / build (cron) (push) Successful in 49s
build-and-release / build (catering-services) (push) Successful in 1m35s
build-and-release / build (companies) (push) Successful in 1m38s
build-and-release / build (graphic-designers) (push) Successful in 9s
build-and-release / build (developers) (push) Successful in 2m4s
build-and-release / build (customers) (push) Successful in 2m16s
build-and-release / build (employees) (push) Successful in 2m20s
build-and-release / build (gateway) (push) Successful in 57s
build-and-release / build (job-seekers) (push) Successful in 1m39s
build-and-release / build (fitness-trainers) (push) Successful in 2m46s
build-and-release / build (photographers) (push) Successful in 9s
build-and-release / build (jobs) (push) Successful in 1m43s
build-and-release / build (leads) (push) Successful in 1m40s
build-and-release / build (makeup-artists) (push) Successful in 2m28s
build-and-release / build (tutors) (push) Successful in 1m23s
build-and-release / build (payments) (push) Successful in 2m51s
build-and-release / build (social-media-managers) (push) Successful in 2m33s
build-and-release / build (ugc-content-creators) (push) Successful in 2m23s
build-and-release / build (users) (push) Has been cancelled
build-and-release / build (video-editors) (push) Has been cancelled
Dockerfile.migrate copied only crates/db-migrate + a few unrelated crates
but left the root Cargo.toml's [workspace] members list referencing every
apps/* service, none of which exist in this build context - cargo failed
immediately with "failed to load manifest for workspace member apps/gateway".
Trim the workspace to just crates/db-migrate (it has no path dependency on
anything else per crates/db-migrate/Cargo.toml) using the same awk trick
Dockerfile.simple already uses per-service. Also fix the resulting binary
output path (workspace builds put target/ at the workspace root, not under
the member crate's own directory).

k8s-migration-job.yaml targeted namespace: default, where neither
nxtgauge-backend-rust-secrets nor a registry pull secret exist - both only
exist in the nxtgauge namespace where the actual app runs. Fix the namespace
and add the missing imagePullSecrets so the job can actually pull its image
and read DATABASE_URL.

Neither of these had ever been exercised successfully before - this is the
first real migration run since the job/image were added.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 03:07:26 +05:30
Ashwin Kumar Sivakumar
466e7abf03 fix(db): point verification_logs FK at verifications, not legacy verification_requests
Some checks failed
build-and-release / build (gateway) (push) Successful in 55s
build-and-release / build (graphic-designers) (push) Successful in 2m13s
build-and-release / build (social-media-managers) (push) Has been cancelled
build-and-release / build (video-editors) (push) Has been cancelled
build-and-release / build (employees) (push) Successful in 2m0s
build-and-release / build (developers) (push) Successful in 2m43s
build-and-release / build (leads) (push) Successful in 1m49s
build-and-release / build (photographers) (push) Successful in 2m17s
build-and-release / build (tutors) (push) Has been cancelled
build-and-release / build (cron) (push) Successful in 47s
build-and-release / build (companies) (push) Successful in 2m6s
build-and-release / build (jobs) (push) Successful in 44s
build-and-release / build (customers) (push) Successful in 2m12s
build-and-release / build (fitness-trainers) (push) Successful in 2m20s
build-and-release / build (payments) (push) Successful in 2m45s
build-and-release / build (makeup-artists) (push) Successful in 3m10s
build-and-release / build (users) (push) Has been cancelled
build-and-release / build (ugc-content-creators) (push) Has been cancelled
build-and-release / build (catering-services) (push) Successful in 1m36s
build-and-release / build (job-seekers) (push) Successful in 2m22s
scripts/init-db.sql created verification_logs.verification_request_id with a
foreign key against verification_requests(id) - an unrelated legacy table.
VerificationRepository::update_status inserts the verifications.id (the row
actually being approved/rejected) into that column on every status change,
which has been violating the FK on every single call:

  "insert or update on table verification_logs violates foreign key
   constraint verification_logs_verification_request_id_fkey"

This made every Approve/Reject click in Verification Management 500,
confirmed via the browser's actual response body. Drop and recreate the
constraint to point at verifications(id), which is what the code has always
actually been logging against.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 03:01:25 +05:30
Ashwin Kumar Sivakumar
a0d97583c4 ci: retrigger isolated employees rebuild (bad digest from concurrent push)
All checks were successful
build-and-release / build (catering-services) (push) Successful in 15s
build-and-release / build (payments) (push) Successful in 4s
build-and-release / build (fitness-trainers) (push) Successful in 5s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (customers) (push) Successful in 14s
build-and-release / build (tutors) (push) Successful in 4s
build-and-release / build (social-media-managers) (push) Successful in 5s
build-and-release / build (cron) (push) Successful in 17s
build-and-release / build (ugc-content-creators) (push) Successful in 5s
build-and-release / build (gateway) (push) Successful in 6s
build-and-release / build (users) (push) Successful in 4s
build-and-release / build (job-seekers) (push) Successful in 4s
build-and-release / build (video-editors) (push) Successful in 6s
build-and-release / build (developers) (push) Successful in 8s
build-and-release / build (graphic-designers) (push) Successful in 7s
build-and-release / build (makeup-artists) (push) Successful in 5s
build-and-release / build (leads) (push) Successful in 6s
build-and-release / build (companies) (push) Successful in 10s
build-and-release / build (jobs) (push) Successful in 6s
build-and-release / build (employees) (push) Successful in 1m45s
The 20-service matrix push for 8a29b81 landed a digest in gitops for
employees that doesn't exist in the registry (ImagePullBackOff, confirmed
via kubectl - same failure mode documented in 512eb72/ea622a4). This commit
only touches apps/employees/ so the CI path-filter rebuilds just that one
service in isolation, avoiding the concurrent-push contention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 00:30:27 +05:30
Ashwin Kumar Sivakumar
8a29b81c84 feat(admin-auth): add POST /api/admin/auth/refresh for sliding session window
All checks were successful
build-and-release / build (gateway) (push) Successful in 1m33s
build-and-release / build (graphic-designers) (push) Successful in 2m13s
build-and-release / build (ugc-content-creators) (push) Successful in 2m12s
build-and-release / build (users) (push) Successful in 4m48s
build-and-release / build (catering-services) (push) Successful in 1m22s
build-and-release / build (job-seekers) (push) Successful in 3m20s
build-and-release / build (companies) (push) Successful in 2m36s
build-and-release / build (social-media-managers) (push) Successful in 2m18s
build-and-release / build (tutors) (push) Successful in 2m41s
build-and-release / build (customers) (push) Successful in 1m38s
build-and-release / build (employees) (push) Successful in 3m16s
build-and-release / build (makeup-artists) (push) Successful in 1m46s
build-and-release / build (photographers) (push) Successful in 2m39s
build-and-release / build (jobs) (push) Successful in 1m5s
build-and-release / build (cron) (push) Successful in 1m40s
build-and-release / build (leads) (push) Successful in 2m18s
build-and-release / build (payments) (push) Successful in 3m10s
build-and-release / build (fitness-trainers) (push) Successful in 1m25s
build-and-release / build (video-editors) (push) Successful in 2m11s
build-and-release / build (developers) (push) Successful in 1m43s
Admin access tokens expire after 15 minutes with no way to renew one, so
active admins got logged out mid-work with no warning (silent 401s, now
surfaced by admin-solid's session-expired dialog). Add a refresh endpoint
that exchanges the HttpOnly nxtgauge_admin_token cookie for a new 15-minute
access token, rotating the employee_sessions row (revoke old, store new) -
mirrors the existing pattern in apps/users/src/handlers/auth.rs, but against
the DB-backed employee_sessions table instead of Redis.

Add EmployeeRepository::get_by_id / get_valid_session_by_token / revoke_session
to support it.

The admin-solid frontend calls this on a timer while the admin is active and
skips it once idle for 15 minutes, so the session now extends while active
and expires on inactivity as intended, instead of on a fixed wall-clock timer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 00:14:08 +05:30
Tracewebstudio Dev
f56251374a Increase LiteLLM request timeout from 60s to 180s
All checks were successful
build-and-release / build (catering-services) (push) Successful in 9s
build-and-release / build (customers) (push) Successful in 8s
build-and-release / build (fitness-trainers) (push) Successful in 5s
build-and-release / build (cron) (push) Successful in 15s
build-and-release / build (companies) (push) Successful in 16s
build-and-release / build (employees) (push) Successful in 14s
build-and-release / build (gateway) (push) Successful in 6s
build-and-release / build (developers) (push) Successful in 16s
build-and-release / build (graphic-designers) (push) Successful in 4s
build-and-release / build (job-seekers) (push) Successful in 6s
build-and-release / build (leads) (push) Successful in 5s
build-and-release / build (makeup-artists) (push) Successful in 7s
build-and-release / build (jobs) (push) Successful in 6s
build-and-release / build (photographers) (push) Successful in 5s
build-and-release / build (payments) (push) Successful in 6s
build-and-release / build (social-media-managers) (push) Successful in 5s
build-and-release / build (ugc-content-creators) (push) Successful in 5s
build-and-release / build (tutors) (push) Successful in 6s
build-and-release / build (video-editors) (push) Successful in 5s
build-and-release / build (users) (push) Successful in 3m36s
The askash-main model at llm.nxtgauge.com takes 60-120s to generate
job descriptions; previous 60s timeout caused all JD generations to fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-18 14:23:04 +02:00
Tracewebstudio Dev
623cda47b0 fix: end-to-end workflow fixes for company/job-seeker and profession flows
All checks were successful
build-and-release / build (employees) (push) Successful in 1m25s
build-and-release / build (developers) (push) Successful in 1m41s
build-and-release / build (customers) (push) Successful in 1m43s
build-and-release / build (cron) (push) Successful in 1m57s
build-and-release / build (catering-services) (push) Successful in 2m19s
build-and-release / build (companies) (push) Successful in 2m44s
build-and-release / build (fitness-trainers) (push) Successful in 1m36s
build-and-release / build (gateway) (push) Successful in 1m40s
build-and-release / build (graphic-designers) (push) Successful in 1m50s
build-and-release / build (jobs) (push) Successful in 1m42s
build-and-release / build (job-seekers) (push) Successful in 2m31s
build-and-release / build (leads) (push) Successful in 2m1s
build-and-release / build (photographers) (push) Successful in 1m46s
build-and-release / build (makeup-artists) (push) Successful in 2m30s
build-and-release / build (social-media-managers) (push) Successful in 2m42s
build-and-release / build (tutors) (push) Successful in 2m16s
build-and-release / build (video-editors) (push) Successful in 2m19s
build-and-release / build (payments) (push) Successful in 4m40s
build-and-release / build (ugc-content-creators) (push) Successful in 4m35s
build-and-release / build (users) (push) Successful in 4m45s
- companies/admin: add proper POST /jobs/{id}/approve and /jobs/{id}/reject
  endpoints (sets status=LIVE, not direct SQL bypass)
- customers: fix list_requests to query by customer_user_id (not professional),
  add optional lead_id filter; fix debit to use professional_user_id
- payments: switch razorpay_order_id column to payu_txnid (PayU migration)
- users/auth: fix role resolution to not inject phantom roles for professionals
- contracts/profession_shared: fix my_requests SQL to join leads+users instead
  of nonexistent requirements table
- db/job_seeker: fix INSERT VALUES placeholder count (add missing $10)
- storage: add MOCK_STORAGE=true mode for local dev without real B2 creds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-18 11:40:01 +02:00
Ashwin Kumar Sivakumar
3546b2caf0 fix(ci): sort registry prune by real image build time, protect current SHA
All checks were successful
build-and-release / build (companies) (push) Successful in 3s
build-and-release / build (cron) (push) Successful in 5s
build-and-release / build (developers) (push) Successful in 3s
build-and-release / build (employees) (push) Successful in 5s
build-and-release / build (fitness-trainers) (push) Successful in 4s
build-and-release / build (customers) (push) Successful in 12s
build-and-release / build (catering-services) (push) Successful in 15s
build-and-release / build (gateway) (push) Successful in 4s
build-and-release / build (graphic-designers) (push) Successful in 6s
build-and-release / build (job-seekers) (push) Successful in 5s
build-and-release / build (jobs) (push) Successful in 4s
build-and-release / build (leads) (push) Successful in 6s
build-and-release / build (payments) (push) Successful in 5s
build-and-release / build (makeup-artists) (push) Successful in 6s
build-and-release / build (photographers) (push) Successful in 6s
build-and-release / build (social-media-managers) (push) Successful in 4s
build-and-release / build (ugc-content-creators) (push) Successful in 5s
build-and-release / build (users) (push) Successful in 5s
build-and-release / build (tutors) (push) Successful in 6s
build-and-release / build (video-editors) (push) Successful in 5s
Same fix as nxtgauge-frontend-solid and nxtgauge-admin-solid: this
script isn't currently wired into this repo's build.yaml, but it's
identical to the version that deleted frontend-solid's just-built image
seconds after pushing it (sorted candidates by request time instead of
actual build time). Fixing it here too so it's safe if/when it gets
wired up, rather than leaving a dormant copy of the same bug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 05:38:27 +05:30
Tracewebstudio Dev
0163349988 fix: skip broken migrations that reference dropped professionals table
All checks were successful
build-and-release / build (companies) (push) Successful in 1m39s
build-and-release / build (cron) (push) Successful in 52s
build-and-release / build (catering-services) (push) Successful in 2m9s
build-and-release / build (developers) (push) Successful in 1m32s
build-and-release / build (customers) (push) Successful in 1m51s
build-and-release / build (employees) (push) Successful in 1m52s
build-and-release / build (gateway) (push) Successful in 2m1s
build-and-release / build (fitness-trainers) (push) Successful in 2m11s
build-and-release / build (job-seekers) (push) Successful in 1m54s
build-and-release / build (graphic-designers) (push) Successful in 2m15s
build-and-release / build (leads) (push) Successful in 1m39s
build-and-release / build (jobs) (push) Successful in 2m9s
build-and-release / build (makeup-artists) (push) Successful in 2m39s
build-and-release / build (photographers) (push) Successful in 1m52s
build-and-release / build (tutors) (push) Successful in 2m31s
build-and-release / build (ugc-content-creators) (push) Successful in 2m43s
build-and-release / build (payments) (push) Successful in 4m9s
build-and-release / build (social-media-managers) (push) Successful in 4m8s
build-and-release / build (video-editors) (push) Successful in 2m39s
build-and-release / build (users) (push) Successful in 7m7s
Several migrations reference a professionals table that was replaced by
per-profession profile tables in 20260317195000. Rename to .skip so
sqlx migrate run succeeds on a fresh local dev database. Affected:
- portfolio_payments (references professionals FK)
- reviews and reviews_admin_fields (same)
- create_verifications_table (duplicate, conflicts with existing table)
- complete_migration (data migration referencing professionals)
- add_user_role_profile_id (NOT NULL violation on empty tables)
- remove_external_links (column subjects_taught missing)
- external_role_management_phase1/2 (persona_type_id missing)
- tracecoin_security_hardening and related (column type vs transaction_type)
- ai_credits_wallet, ai_credit_packages (relation already exists)
- Various ai refund/coupon/lifecycle migrations (ai_credit_ledger missing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-17 00:48:50 +02:00