Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5ed863ab1 | |||
| 8d0552f73f | |||
| 3a75ee7aee | |||
| 30d670a257 | |||
| cfa4d8fa91 | |||
| 39e8d5c9f9 |
@@ -111,6 +111,8 @@ jobs:
|
|||||||
build-and-push-auth:
|
build-and-push-auth:
|
||||||
runs-on: runners-cartsnitch
|
runs-on: runners-cartsnitch
|
||||||
needs: [lint, test]
|
needs: [lint, test]
|
||||||
|
outputs:
|
||||||
|
calver_tag: ${{ steps.calver.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -161,7 +163,7 @@ jobs:
|
|||||||
|
|
||||||
deploy-dev:
|
deploy-dev:
|
||||||
runs-on: runners-cartsnitch
|
runs-on: runners-cartsnitch
|
||||||
needs: [build-and-push]
|
needs: [build-and-push, build-and-push-auth]
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
steps:
|
steps:
|
||||||
- name: Generate GitHub App token
|
- name: Generate GitHub App token
|
||||||
@@ -191,6 +193,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd infra/apps/overlays/dev
|
cd infra/apps/overlays/dev
|
||||||
kustomize edit set image ghcr.io/cartsnitch/cartsnitch:${{ needs.build-and-push.outputs.calver_tag }}
|
kustomize edit set image ghcr.io/cartsnitch/cartsnitch:${{ needs.build-and-push.outputs.calver_tag }}
|
||||||
|
kustomize edit set image ghcr.io/cartsnitch/auth:${{ needs.build-and-push-auth.outputs.calver_tag }}
|
||||||
|
|
||||||
- name: Commit and push to infra
|
- name: Commit and push to infra
|
||||||
run: |
|
run: |
|
||||||
@@ -198,5 +201,5 @@ jobs:
|
|||||||
git config user.name "cartsnitch-ci[bot]"
|
git config user.name "cartsnitch-ci[bot]"
|
||||||
git config user.email "cartsnitch-ci[bot]@users.noreply.github.com"
|
git config user.email "cartsnitch-ci[bot]@users.noreply.github.com"
|
||||||
git add apps/overlays/dev/kustomization.yaml
|
git add apps/overlays/dev/kustomization.yaml
|
||||||
git commit -m "ci(dev): update cartsnitch image to ${{ needs.build-and-push.outputs.calver_tag }}"
|
git commit -m "ci(dev): update cartsnitch and auth images to ${{ needs.build-and-push.outputs.calver_tag }}"
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|||||||
@@ -1,45 +1 @@
|
|||||||
# CartSnitch Monorepo
|
# CartSnitch
|
||||||
|
|
||||||
CartSnitch is a self-hosted grocery price intelligence platform. This repo consolidates the core services and the flagship frontend PWA.
|
|
||||||
|
|
||||||
## Services
|
|
||||||
|
|
||||||
| Directory | Service | Purpose |
|
|
||||||
|-----------|---------|---------|
|
|
||||||
| `/` (root) | **Frontend** | React 18 PWA — mobile-first price intelligence UI |
|
|
||||||
| `api/` | **API Gateway** | FastAPI — frontend-facing REST API |
|
|
||||||
| `common/` | **Common** | Shared Python models, schemas, Alembic migrations |
|
|
||||||
| `receiptwitness/` | **ReceiptWitness** | Purchase ingestion via retailer scrapers |
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Frontend (root)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
npm run dev # http://localhost:5173
|
|
||||||
npm run build # production build
|
|
||||||
npm run test # unit tests (Vitest)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Python Services
|
|
||||||
|
|
||||||
Each Python service uses [uv](https://github.com/astral-sh/uv) and has its own `pyproject.toml`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd api # or common / receiptwitness
|
|
||||||
uv sync
|
|
||||||
uv run pytest
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
|
|
||||||
- **Never push directly to main.** Always open a PR from a feature branch.
|
|
||||||
- Branch naming: `feature/<description>` or `fix/<description>`
|
|
||||||
- Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
For full details see [CLAUDE.md](./CLAUDE.md) or the per-service `CLAUDE.md` in each subdirectory.
|
|
||||||
|
|
||||||
CartSnitch is a polyrepo-style monorepo: each service can be built and deployed independently, but sharing code between `common/` and the other Python services is done via local path dependencies in `pyproject.toml`.
|
|
||||||
|
|||||||
@@ -1,8 +1,36 @@
|
|||||||
import { createAuthClient } from "better-auth/react"
|
import { createAuthClient } from "better-auth/react"
|
||||||
|
import type { BetterFetchPlugin } from "@better-fetch/fetch"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps 'name' -> 'display_name' in register requests to match the API's RegisterRequest schema.
|
||||||
|
*/
|
||||||
|
const displayNameMapper: BetterFetchPlugin = {
|
||||||
|
id: "display-name-mapper",
|
||||||
|
name: "display-name-mapper",
|
||||||
|
hooks: {
|
||||||
|
onRequest: async (context) => {
|
||||||
|
const url = typeof context.url === "string" ? context.url : context.url.pathname
|
||||||
|
if (
|
||||||
|
url.endsWith("/auth/register") &&
|
||||||
|
context.method === "POST" &&
|
||||||
|
context.body &&
|
||||||
|
"name" in context.body
|
||||||
|
) {
|
||||||
|
context.body = {
|
||||||
|
...context.body,
|
||||||
|
display_name: context.body.name as string,
|
||||||
|
name: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export const authClient = createAuthClient({
|
export const authClient = createAuthClient({
|
||||||
baseURL: import.meta.env.VITE_AUTH_URL ?? "http://localhost:3001",
|
baseURL: import.meta.env.VITE_AUTH_URL ?? "http://localhost:3001",
|
||||||
basePath: "/auth",
|
basePath: "/auth",
|
||||||
|
fetchPlugins: [displayNameMapper],
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { useSession, signIn, signUp, signOut } = authClient
|
export const { useSession, signIn, signUp, signOut } = authClient
|
||||||
|
|||||||
Reference in New Issue
Block a user