Skip to content

feat: migrate company verification from Retool to private APIs#3930

Merged
idoshamun merged 2 commits into
mainfrom
eng-1630-migrate-company-verification-from-retool-to-private-apis
Jun 7, 2026
Merged

feat: migrate company verification from Retool to private APIs#3930
idoshamun merged 2 commits into
mainfrom
eng-1630-migrate-company-verification-from-retool-to-private-apis

Conversation

@idoshamun
Copy link
Copy Markdown
Member

Replaces the Retool company-verification write actions with service-only private REST endpoints in daily-api, so the review workflow can be driven by AI agents (and humans) through a stable, validated API instead of raw SQL. Read/listing queries are out of scope.

Endpoints (under /p/company-verification)

  • POST /companies — create a company. Generates the id via generateShortId() when not supplied; uploads the provided image URL to Cloudinary (uploadLogoFromUrl) and stores the hosted URL; normalizes domains. Returns 201.
  • POST /link-domain — link user_company rows to a company by email domain (404 if the company doesn't exist). Returns affected-row count.
  • POST /reject-domain — set flags.rejected = true for matching rows via updateFlagsStatement. Idempotent. Returns affected-row count.

Key decisions

  • REST, mirroring contributions.ts — the closest existing private-router pattern: preHandler service guard (404 when !req.service), Zod-validated bodies, query-builder access, handlers that always return res.send(...).
  • Domain normalization — inputs are .trim().toLowerCase()'d in Zod and matched against split_part(lower(email), '@', 2) (parameterized, no raw con.query), so stored-email casing can't cause missed matches — a deliberate improvement over Retool's exact match.
  • Logos always re-hosted on Cloudinary rather than storing arbitrary external URLs; upload happens before persist so a failed upload aborts creation (no partial company).
  • Create-only semantics — supplying an existing id returns 409 instead of silently upserting, since company ids are referenced by user_company.companyId.
  • Additive only — the enrichment worker and cron are left untouched; these endpoints are the manual/agent review fallback.

Shared parseSchema helper extracted to src/routes/private/utils.ts and reused by both contributions and companyVerification.

Tests

Integration tests in __tests__/routes/private/companyVerification.ts (Cloudinary mocked): service guards, create (generated/provided id, hosted image, domain normalization, validation, 409 conflict), link (mixed-case matching, 404), reject (mixed-case, idempotency).

Closes ENG-1630


Created by Huginn 🐦‍⬛

idoshamun and others added 2 commits June 7, 2026 13:32
Migrate the three Retool company-verification write actions to
service-only private REST endpoints under /p/company-verification:

- POST /companies: create a company, re-hosting the logo on Cloudinary
  via uploadLogoFromUrl and generating a short id when omitted.
- POST /link-domain: link user_company rows to a company by email
  domain (404 when the company is missing).
- POST /reject-domain: set flags.rejected on matching rows.

Domains/inputs are normalized (trim + lowercase) and matched against
split_part(lower(email), '@', 2) so stored-email casing cannot cause
missed matches. Includes Zod validation and integration tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review follow-ups:

- POST /company-verification/companies now returns 409 when a caller
  supplies an id that already exists, instead of silently upserting
  (Company ids are referenced by user_company.companyId, so overwriting
  an existing company is destructive). The conflict is checked before
  the Cloudinary upload to avoid wasted work.
- Extract the duplicated parseSchema helper into src/routes/private/utils.ts
  and import it from both contributions and companyVerification routers
  (CLAUDE.md single-source-of-truth guidance).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pulumi
Copy link
Copy Markdown

pulumi Bot commented Jun 7, 2026

🍹 The Update (preview) for dailydotdev/api/prod (at d24120b) was successful.

✨ Neo Code Review

Routine deployment of a new internal company-verification API route and a minor utility refactor; database and Clickhouse migration jobs run as part of the standard deploy process. ✅ Low Risk

This is a routine code deployment rolling out commit 9888a4ca across all workloads. The PR adds a new internal service-auth-gated API route (/p/company-verification) with three endpoints for creating companies, linking email domains to companies, and rejecting domains. It also extracts the shared parseSchema utility into src/routes/private/utils.ts — a pure refactor with no behavior change to existing routes.

The migration jobs (both DB and Clickhouse) are being recycled as expected — old jobs tied to the previous commit hash are deleted and new ones for the current commit are created.

Resource Changes

    Name                                                       Type                           Operation
~   vpc-native-validate-active-users-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-history-day-clickhouse-cron      kubernetes:batch/v1:CronJob    update
~   vpc-native-rotate-weekly-quests-cron                       kubernetes:batch/v1:CronJob    update
~   vpc-native-worker-job-deployment                           kubernetes:apps/v1:Deployment  update
~   vpc-native-post-lifecycle-state-clickhouse-cron            kubernetes:batch/v1:CronJob    update
~   vpc-native-update-highlighted-views-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-opportunities-cron                 kubernetes:batch/v1:CronJob    update
~   vpc-native-hourly-notification-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-update-source-public-threshold-cron             kubernetes:batch/v1:CronJob    update
~   vpc-native-rotate-daily-quests-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-calculate-top-readers-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-expire-super-agent-trial-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-bg-deployment                                   kubernetes:apps/v1:Deployment  update
~   vpc-native-deployment                                      kubernetes:apps/v1:Deployment  update
+   vpc-native-api-clickhouse-migration-9888a4ca               kubernetes:batch/v1:Job        create
~   vpc-native-personalized-digest-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-temporal-deployment                             kubernetes:apps/v1:Deployment  update
~   vpc-native-ws-deployment                                   kubernetes:apps/v1:Deployment  update
~   vpc-native-clean-gifted-plus-cron                          kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-analytics-history-clickhouse-cron  kubernetes:batch/v1:CronJob    update
~   vpc-native-sync-subscription-with-cio-cron                 kubernetes:batch/v1:CronJob    update
~   vpc-native-check-analytics-report-cron                     kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-analytics-clickhouse-cron          kubernetes:batch/v1:CronJob    update
~   vpc-native-update-current-streak-cron                      kubernetes:batch/v1:CronJob    update
~   vpc-native-generate-search-invites-cron                    kubernetes:batch/v1:CronJob    update
~   vpc-native-post-analytics-clickhouse-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-channel-highlights-cron                         kubernetes:batch/v1:CronJob    update
~   vpc-native-daily-digest-cron                               kubernetes:batch/v1:CronJob    update
~   vpc-native-materialize-yearly-best-post-archives-cron      kubernetes:batch/v1:CronJob    update
-   vpc-native-api-clickhouse-migration-26b0e1ee               kubernetes:batch/v1:Job        delete
+   vpc-native-api-db-migration-9888a4ca                       kubernetes:batch/v1:Job        create
~   vpc-native-clean-zombie-images-cron                        kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-user-companies-cron                kubernetes:batch/v1:CronJob    update
~   vpc-native-update-tag-materialized-views-cron              kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-zombie-users-cron                         kubernetes:batch/v1:CronJob    update
~   vpc-native-update-achievement-rarity-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-materialize-monthly-best-post-archives-cron     kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-channel-highlights-cron                   kubernetes:batch/v1:CronJob    update
~   vpc-native-user-profile-updated-sync-cron                  kubernetes:batch/v1:CronJob    update
~   vpc-native-clean-expired-better-auth-sessions-cron         kubernetes:batch/v1:CronJob    update
~   vpc-native-private-deployment                              kubernetes:apps/v1:Deployment  update
... and 13 other changes

@idoshamun idoshamun merged commit 5b85a9c into main Jun 7, 2026
9 checks passed
@idoshamun idoshamun deleted the eng-1630-migrate-company-verification-from-retool-to-private-apis branch June 7, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant