Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The repository is a monorepo for the VitNode framework, which includes a backend
- **Frontend:**
- TanStack Start on Vite, file-based routes under `apps/web/src/routes/`
- Navigation: use `@vitnode/core/tanstack/layout`'s `RouterLink`, or TanStack
Router's own `Link` / `useNavigate`. Next.js is not a dependency.
Router's own `Link` / `useNavigate`.
- Forms: Use `react-hook-form@7`, `createServerFn` for mutations
- UI: Shadcn UI, Tailwind CSS 4, dark/light mode with system detection
- i18n: Use `use-intl`, `t('key')` for translations, `createTranslator`
Expand Down
4 changes: 2 additions & 2 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ REDIS_URL=redis://localhost:6379
# Origin of the web application (`apps/web`, `vite dev --port 3000`). This API
# trusts it for CORS and CSRF on credentialed browser requests, so a wrong
# port here shows up as a blocked upload rather than an obvious error.
NEXT_PUBLIC_WEB_URL=http://localhost:3000
VITNODE_WEB_URL=http://localhost:3000
# Public origin of this API. Used to build absolute upload URLs for the Local
# storage adapter, so it must match where the server is reachable.
NEXT_PUBLIC_API_URL=http://localhost:8000
VITNODE_API_URL=http://localhost:8000

# === CRON Secret for Internal API Calls ===
CRON_SECRET=your-secure-cron-secret-key
Expand Down
5 changes: 3 additions & 2 deletions apps/api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ services:
volumes:
- ../../docker/dev:/var/lib/postgresql/data
ports:
- "5432:5432"
# Loopback only - these are development containers with a default password.
- "127.0.0.1:5432:5432"
networks:
- vitnode_dev

Expand All @@ -23,7 +24,7 @@ services:
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD-root}
ports:
- "6379:6379"
- "127.0.0.1:6379:6379"
networks:
- vitnode_dev

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
WITH normalized AS (
SELECT "id", "email", lower(btrim("email")) AS "normalized" FROM "core_users"
), split AS (
SELECT
"id",
"email",
"normalized",
CASE WHEN length("normalized") - length(replace("normalized", '@', '')) = 1
THEN split_part("normalized", '@', 1) END AS "localPart",
CASE WHEN length("normalized") - length(replace("normalized", '@', '')) = 1
THEN split_part("normalized", '@', 2) END AS "domain"
FROM normalized
), canonical AS (
SELECT
"id",
"email",
CASE
WHEN "localPart" IS NULL OR "localPart" = '' OR "domain" = '' OR left("localPart", 1) = '"'
THEN "normalized"
WHEN "domain" IN ('gmail.com', 'googlemail.com')
THEN CASE
WHEN replace(split_part("localPart", '+', 1), '.', '') = '' THEN "normalized"
ELSE replace(split_part("localPart", '+', 1), '.', '') || '@gmail.com'
END
WHEN "domain" IN (
'fastmail.com', 'fastmail.fm', 'gmx.com', 'gmx.de', 'gmx.net',
'icloud.com', 'mac.com', 'me.com', 'pm.me', 'proton.me',
'protonmail.com', 'yandex.com', 'yandex.ru', 'zoho.com'
) OR "domain" ~ '^(outlook|hotmail|live|msn)\.[a-z]{2,}(\.[a-z]{2,})?$'
THEN CASE
WHEN split_part("localPart", '+', 1) = '' THEN "normalized"
ELSE split_part("localPart", '+', 1) || '@' || "domain"
END
ELSE "normalized"
END AS "canonical"
FROM split
)
UPDATE "core_users" AS u SET "email" = c."canonical"
FROM canonical c
WHERE c."id" = u."id"
AND c."canonical" <> u."email"
AND NOT EXISTS (
SELECT 1 FROM "core_users" taken
WHERE taken."email" = c."canonical" AND taken."id" <> c."id"
)
AND NOT EXISTS (
SELECT 1 FROM canonical rival
WHERE rival."canonical" = c."canonical" AND rival."id" < c."id"
);
Loading