Compare commits

..

1 Commits

Author SHA1 Message Date
Paperclip 121dc5724e fix: remove VITE_MOCK_AUTH bypass from production code
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-14 15:37:24 +00:00
19 changed files with 43 additions and 266 deletions
+8 -123
View File
@@ -13,7 +13,6 @@ concurrency:
permissions: permissions:
contents: write contents: write
packages: write packages: write
security-events: write
env: env:
REGISTRY: ghcr.io REGISTRY: ghcr.io
@@ -152,44 +151,17 @@ jobs:
type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build Docker image - name: Build and push Docker image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
load: true push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
target: prod target: prod
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
- name: Scan frontend image for vulnerabilities
uses: anchore/scan-action@v5
id: scan
with:
image: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}"
fail-build: true
severity-cutoff: high
only-fixed: "true"
output-format: sarif
- name: Upload frontend scan results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
- name: Push Docker image
if: github.event_name == 'push'
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
target: prod
cache-from: type=gha
- name: Create git tag - name: Create git tag
if: github.event_name == 'push' && github.ref == 'refs/heads/main' if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: | run: |
@@ -249,43 +221,14 @@ jobs:
type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build Docker image - name: Build and push auth Docker image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: ./auth context: ./auth
file: ./auth/Dockerfile file: ./auth/Dockerfile
load: true push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan auth image for vulnerabilities
uses: anchore/scan-action@v5
id: scan
with:
image: "${{ env.REGISTRY }}/${{ env.AUTH_IMAGE_NAME }}:sha-${{ github.sha }}"
fail-build: true
severity-cutoff: high
only-fixed: "true"
output-format: sarif
- name: Upload auth scan results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
- name: Push Docker image
if: github.event_name == 'push'
uses: docker/build-push-action@v6
with:
context: ./auth
file: ./auth/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
build-and-push-receiptwitness: build-and-push-receiptwitness:
runs-on: runners-cartsnitch runs-on: runners-cartsnitch
@@ -335,43 +278,14 @@ jobs:
type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build Docker image - name: Build and push receiptwitness image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./receiptwitness/Dockerfile file: ./receiptwitness/Dockerfile
load: true push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan receiptwitness image for vulnerabilities
uses: anchore/scan-action@v5
id: scan
with:
image: "${{ env.REGISTRY }}/${{ env.RECEIPTWITNESS_IMAGE_NAME }}:sha-${{ github.sha }}"
fail-build: true
severity-cutoff: high
only-fixed: "true"
output-format: sarif
- name: Upload receiptwitness scan results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
- name: Push Docker image
if: github.event_name == 'push'
uses: docker/build-push-action@v6
with:
context: .
file: ./receiptwitness/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
build-and-push-api: build-and-push-api:
runs-on: runners-cartsnitch runs-on: runners-cartsnitch
@@ -421,43 +335,14 @@ jobs:
type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build Docker image - name: Build and push API Docker image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: ./api context: ./api
file: ./api/Dockerfile file: ./api/Dockerfile
load: true push: ${{ github.event_name == 'push' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan api image for vulnerabilities
uses: anchore/scan-action@v5
id: scan
with:
image: "${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }}:sha-${{ github.sha }}"
fail-build: true
severity-cutoff: high
only-fixed: "true"
output-format: sarif
- name: Upload api scan results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
- name: Push Docker image
if: github.event_name == 'push'
uses: docker/build-push-action@v6
with:
context: ./api
file: ./api/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
deploy-dev: deploy-dev:
runs-on: runners-cartsnitch runs-on: runners-cartsnitch
+1 -4
View File
@@ -1,6 +1,6 @@
# Stage 1: Build # Stage 1: Build
FROM node:20-alpine AS build FROM node:20-alpine AS build
RUN apk update && apk upgrade --no-cache
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
@@ -11,9 +11,6 @@ RUN npm run build
# Stage 2: Production — uses nginxinc/nginx-unprivileged which runs as non-root (UID 101) # Stage 2: Production — uses nginxinc/nginx-unprivileged which runs as non-root (UID 101)
FROM nginxinc/nginx-unprivileged:stable-alpine AS prod FROM nginxinc/nginx-unprivileged:stable-alpine AS prod
USER root
RUN apk update && apk upgrade --no-cache
USER 101
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
+2 -2
View File
@@ -1,6 +1,6 @@
FROM python:3.12-slim AS build FROM python:3.12-slim AS build
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev \ libpq-dev \
build-essential \ build-essential \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@@ -12,7 +12,7 @@ RUN pip install --no-cache-dir --prefix=/install .
FROM python:3.12-slim AS prod FROM python:3.12-slim AS prod
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
RUN adduser --system --group --uid 1000 app RUN adduser --system --group --uid 1000 app
@@ -1,38 +0,0 @@
"""Add GIN index on upc_variants and alter column to JSONB.
Revision ID: 009_add_gin_index_upc_variants
Revises: 008_create_domain_tables
Create Date: 2026-04-14
"""
import sqlalchemy as sa
from alembic import op
revision = "009_add_gin_index_upc_variants"
down_revision = "008_create_domain_tables"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.alter_column(
"normalized_products",
"upc_variants",
type_=sa.dialects.postgresql.JSONB(),
postgresql_using="upc_variants::jsonb",
)
op.create_index(
"ix_normalized_products_upc_variants_gin",
"normalized_products",
["upc_variants"],
postgresql_using="gin",
)
def downgrade() -> None:
op.drop_index("ix_normalized_products_upc_variants_gin", table_name="normalized_products")
op.alter_column(
"normalized_products",
"upc_variants",
type_=sa.JSON(),
)
-2
View File
@@ -1,5 +1,4 @@
FROM node:22-alpine AS builder FROM node:22-alpine AS builder
RUN apk update && apk upgrade --no-cache
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json* ./ COPY package.json package-lock.json* ./
RUN npm ci RUN npm ci
@@ -8,7 +7,6 @@ COPY src/ src/
RUN npm run build RUN npm run build
FROM node:22-alpine FROM node:22-alpine
RUN apk update && apk upgrade --no-cache
WORKDIR /app WORKDIR /app
ENV NODE_ENV=production ENV NODE_ENV=production
COPY package.json package-lock.json* ./ COPY package.json package-lock.json* ./
+3 -3
View File
@@ -941,9 +941,9 @@
} }
}, },
"node_modules/defu": { "node_modules/defu": {
"version": "6.1.7", "version": "6.1.4",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
"integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/delegates": { "node_modules/delegates": {
+6 -12
View File
@@ -4,23 +4,17 @@ import pg from "pg";
const { Pool } = pg; const { Pool } = pg;
const pool = new Pool({
connectionString:
process.env.DATABASE_URL ??
"postgresql://cartsnitch:cartsnitch@localhost:5432/cartsnitch",
});
const secret = process.env.BETTER_AUTH_SECRET; const secret = process.env.BETTER_AUTH_SECRET;
if (!secret) { if (!secret) {
throw new Error("BETTER_AUTH_SECRET environment variable is required"); throw new Error("BETTER_AUTH_SECRET environment variable is required");
} }
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
console.warn(
"WARNING: DATABASE_URL is not set — using default localhost connection. " +
"Set DATABASE_URL for production deployments."
);
}
export const pool = new Pool({
connectionString: databaseUrl ?? "postgresql://cartsnitch:cartsnitch@localhost:5432/cartsnitch",
});
export const auth = betterAuth({ export const auth = betterAuth({
database: pool, database: pool,
basePath: "/auth", basePath: "/auth",
+3 -17
View File
@@ -1,6 +1,6 @@
import { createServer } from "node:http"; import { createServer } from "node:http";
import { toNodeHandler } from "better-auth/node"; import { toNodeHandler } from "better-auth/node";
import { auth, pool } from "./auth.js"; import { auth } from "./auth.js";
const port = parseInt(process.env.PORT ?? "3001", 10); const port = parseInt(process.env.PORT ?? "3001", 10);
@@ -9,22 +9,8 @@ const handler = toNodeHandler(auth);
const server = createServer(async (req, res) => { const server = createServer(async (req, res) => {
// Health check // Health check
if (req.url === "/health" && req.method === "GET") { if (req.url === "/health" && req.method === "GET") {
try { res.writeHead(200, { "Content-Type": "application/json" });
const client = await pool.connect(); res.end(JSON.stringify({ status: "ok" }));
try {
await Promise.race([
client.query("SELECT 1"),
new Promise((_, reject) => setTimeout(() => reject(new Error("DB timeout")), 2000)),
]);
} finally {
client.release();
}
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "ok", db: "connected" }));
} catch {
res.writeHead(503, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "error", db: "unreachable" }));
}
return; return;
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Submodule cartsnitch deleted from a53daddb9a
@@ -3,7 +3,6 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from sqlalchemy import JSON, String from sqlalchemy import JSON, String
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
from cartsnitch_common.constants import ProductCategory, SizeUnit from cartsnitch_common.constants import ProductCategory, SizeUnit
@@ -27,9 +26,7 @@ class NormalizedProduct(UUIDPrimaryKeyMixin, TimestampMixin, Base):
brand: Mapped[str | None] = mapped_column(String(200)) brand: Mapped[str | None] = mapped_column(String(200))
size: Mapped[str | None] = mapped_column(String(50)) size: Mapped[str | None] = mapped_column(String(50))
size_unit: Mapped[SizeUnit | None] = mapped_column(String(10)) size_unit: Mapped[SizeUnit | None] = mapped_column(String(10))
upc_variants: Mapped[list[str] | None] = mapped_column( upc_variants: Mapped[list[str] | None] = mapped_column(JSON, default=list)
JSON().with_variant(JSONB(), "postgresql"), default=list
)
# Relationships # Relationships
purchase_items: Mapped[list["PurchaseItem"]] = relationship(back_populates="normalized_product") purchase_items: Mapped[list["PurchaseItem"]] = relationship(back_populates="normalized_product")
@@ -10,7 +10,6 @@ test.describe('J1: Registration and Login', () => {
await page.fill('[placeholder="Password (min. 8 characters)"]', 'TestPass123!'); await page.fill('[placeholder="Password (min. 8 characters)"]', 'TestPass123!');
await page.click('button[type="submit"]'); await page.click('button[type="submit"]');
// With VITE_MOCK_AUTH=true the app navigates to "/" on success
await expect(page).toHaveURL('http://localhost:5173/'); await expect(page).toHaveURL('http://localhost:5173/');
await expect(page.getByRole('heading', { name: /cart/i })).toBeVisible(); await expect(page.getByRole('heading', { name: /cart/i })).toBeVisible();
}); });
+3 -3
View File
@@ -9805,9 +9805,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.4.2", "version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
}, },
], ],
webServer: { webServer: {
command: 'VITE_MOCK_AUTH=true npm run dev', command: 'npm run dev',
url: 'http://localhost:5173', url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
}, },
+2 -2
View File
@@ -5,7 +5,7 @@ WORKDIR /app
# build-essential and libpq-dev are needed to compile any C-extension wheels # build-essential and libpq-dev are needed to compile any C-extension wheels
# (e.g. psycopg2 fallback). No git needed — common/ is copied from the repo root. # (e.g. psycopg2 fallback). No git needed — common/ is copied from the repo root.
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev \ libpq-dev \
build-essential \ build-essential \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@@ -25,7 +25,7 @@ FROM python:3.12-slim AS prod
WORKDIR /app WORKDIR /app
# Install Playwright system dependencies for Chromium # Install Playwright system dependencies for Chromium
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
libnss3 \ libnss3 \
libatk1.0-0 \ libatk1.0-0 \
libatk-bridge2.0-0 \ libatk-bridge2.0-0 \
@@ -5,14 +5,12 @@ Matches products across retailers by:
2. Fuzzy name matching via token-based Jaccard similarity (lower confidence) 2. Fuzzy name matching via token-based Jaccard similarity (lower confidence)
""" """
import json
import re import re
from dataclasses import dataclass from dataclasses import dataclass
from enum import StrEnum from enum import StrEnum
from cartsnitch_common.models.product import NormalizedProduct from cartsnitch_common.models.product import NormalizedProduct
from sqlalchemy import cast, func, select, String from sqlalchemy import select
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -98,24 +96,17 @@ def jaccard_similarity(a: str, b: str) -> float:
def match_by_upc(session: Session, upc: str) -> MatchResult | None: def match_by_upc(session: Session, upc: str) -> MatchResult | None:
"""Find a normalized product by exact UPC match. """Find a normalized product by exact UPC match.
Uses PostgreSQL JSONB containment (@>) for production efficiency. Loads products with upc_variants and checks membership in Python
Falls back to LIKE on SQLite for test compatibility. for cross-database compatibility (works on both PostgreSQL and SQLite).
""" """
dialect_name = session.bind.dialect.name if session.bind else "default" # TODO: Use PostgreSQL JSON containment query (@>) for production.
if dialect_name == "postgresql": # Current approach loads all products into memory — acceptable for tests
stmt = select(NormalizedProduct).where( # and small datasets, but will not scale.
cast(NormalizedProduct.upc_variants, JSONB).op("@>")( stmt = select(NormalizedProduct).where(NormalizedProduct.upc_variants.is_not(None))
func.cast(json.dumps([upc]), JSONB) products = session.execute(stmt).scalars().all()
) for product in products:
) if product.upc_variants and upc in product.upc_variants:
else: return MatchResult(product=product, confidence=1.0, method=MatchMethod.UPC)
stmt = select(NormalizedProduct).where(
NormalizedProduct.upc_variants.is_not(None),
cast(NormalizedProduct.upc_variants, String).contains(upc),
)
product = session.execute(stmt).scalars().first()
if product:
return MatchResult(product=product, confidence=1.0, method=MatchMethod.UPC)
return None return None
-17
View File
@@ -1,25 +1,8 @@
import { useEffect } from 'react'
import { Navigate, Outlet } from 'react-router-dom' import { Navigate, Outlet } from 'react-router-dom'
import { authClient } from '../lib/auth-client.ts' import { authClient } from '../lib/auth-client.ts'
import { useAuthStore } from '../stores/auth.ts'
export function ProtectedRoute() { export function ProtectedRoute() {
const isMockAuth = import.meta.env.VITE_MOCK_AUTH === 'true'
const { data: session, isPending } = authClient.useSession() const { data: session, isPending } = authClient.useSession()
const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
const setAuthenticated = useAuthStore((s) => s.setAuthenticated)
useEffect(() => {
if (!isMockAuth) {
setAuthenticated(!!session)
}
}, [session, setAuthenticated, isMockAuth])
// In mock auth mode, rely on Zustand store (set by Login/Register pages)
if (isMockAuth) {
if (!isAuthenticated) return <Navigate to="/login" replace />
return <Outlet />
}
if (isPending) { if (isPending) {
return ( return (
+1 -8
View File
@@ -1,7 +1,6 @@
import { useState } from 'react' import { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import { authClient } from '../lib/auth-client.ts' import { authClient } from '../lib/auth-client.ts'
import { useAuthStore } from '../stores/auth.ts'
export function Login() { export function Login() {
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
@@ -9,7 +8,6 @@ export function Login() {
const [error, setError] = useState('') const [error, setError] = useState('')
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const navigate = useNavigate() const navigate = useNavigate()
const setAuthenticated = useAuthStore((s) => s.setAuthenticated)
async function handleSubmit(e: React.FormEvent) { async function handleSubmit(e: React.FormEvent) {
e.preventDefault() e.preventDefault()
@@ -40,12 +38,7 @@ export function Login() {
setError('Sign in failed. Please try again.') setError('Sign in failed. Please try again.')
} }
} catch { } catch {
if (import.meta.env.VITE_MOCK_AUTH === 'true') { setError('Invalid email or password. Please try again.')
setAuthenticated(true)
navigate('/')
} else {
setError('Invalid email or password. Please try again.')
}
} finally { } finally {
setLoading(false) setLoading(false)
} }
+1 -8
View File
@@ -1,7 +1,6 @@
import { useState } from 'react' import { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import { authClient } from '../lib/auth-client.ts' import { authClient } from '../lib/auth-client.ts'
import { useAuthStore } from '../stores/auth.ts'
export function Register() { export function Register() {
const [name, setName] = useState('') const [name, setName] = useState('')
@@ -10,7 +9,6 @@ export function Register() {
const [error, setError] = useState('') const [error, setError] = useState('')
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const navigate = useNavigate() const navigate = useNavigate()
const setAuthenticated = useAuthStore((s) => s.setAuthenticated)
async function handleSubmit(e: React.FormEvent) { async function handleSubmit(e: React.FormEvent) {
e.preventDefault() e.preventDefault()
@@ -48,12 +46,7 @@ export function Register() {
setError('Account created! Please sign in.') setError('Account created! Please sign in.')
} }
} catch { } catch {
if (import.meta.env.VITE_MOCK_AUTH === 'true') { setError('Registration failed. Please try again.')
setAuthenticated(true)
navigate('/')
} else {
setError('Registration failed. Please try again.')
}
} finally { } finally {
setLoading(false) setLoading(false)
} }