anti-gambling-trader-tw
Free open-source investment anti-fraud, trading statistics and automated trading program tools: default PaperBroker, provides 14 broker/exchange options; trade record analysis runs locally.
AI translation
Manage shared expenses and keep accounts clear
Translation pending
AI summary · website content
FinanceTypeScript · PLpgSQLGitHub @alvarorzhzOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗App web privada, pensada para el móvil, para que dos personas apunten sus gastos comunes, vean quién debe a quién y comparen cada mes lo gastado con lo presupuestado. Versión actual: 3.25.0 (el historial completo está en src/changelog.ts y en la app, Ajustes → Ver novedades).
En producción: https://gastos-compartidos-eight.vercel.app
La barra de abajo tiene Inicio · Gastos · Compra · + · Presupuesto · Personal · Ajustes.
Inicio
Gastos
Compra
Presupuesto
Personal (solo lo ve quien lo apunta, ni la pareja)
Ajustes
Además: tiempo real entre los dos móviles, tema «Jardín» con modo oscuro automático, animaciones suaves cuando pasa algo, carga por secciones (con buena cobertura se bajan todas al entrar), se instala como app (PWA) y se abre sin conexión con los datos de la última vez. Moneda: EUR.
| Pieza | Elección | Por qué |
|---|---|---|
| Interfaz | React + TypeScript + Vite | Web estática, rápida en móvil, se aloja gratis |
| Datos y login | Supabase (Postgres + Auth + Realtime) | Plan gratuito suficiente para dos personas; seguridad por filas (RLS) |
| Avisos y Apple Pay | Edge Function avisar | Manda las notificaciones push y recibe los pagos del atajo |
| Trabajo de cada mañana | pg_cron + pg_net en Supabase | Apunta fijos y manda avisos aunque nadie abra la app |
| Alojamiento | Vercel (plan gratuito) | Publica solo cada cambio que llega a main |
| Lector de tickets | Tesseract y pdf.js | Se ejecutan en el móvil, sin servicios externos |
| Resumen en PDF | pdf-lib | Dibuja el PDF en el móvil; solo se descarga al pulsar el botón |
La lógica de permisos empieza en supabase/migrations/0001_init.sql y cada migración añade las políticas de sus tablas:
households) tiene como máximo dos miembros y cada cuenta pertenece a un único espacio.Cada archivo de supabase/migrations/ se ejecuta una sola vez y en orden. Todas están ya aplicadas en el proyecto de producción.
| Archivo | Qué añade | Versión |
|---|---|---|
0001_init.sql | Espacios, miembros, invitaciones, categorías, gastos, reembolsos, presupuestos y RLS | 1.0.0 |
0002_reparto_y_tiempo_real.sql | Reparto por gasto (owed_cents) y tiempo real | 1.0.0 |
0003_fijos_y_avisos.sql | Gastos fijos y avisos de presupuesto por categoría | 1.1.0 |
0004_personal.sql | Movimientos personales (solo su dueño) | 1.3.0 |
0005_huchas_y_fijos_personales.sql | Huchas comunes y fijos personales | 1.4.0 |
0006_reparto_por_defecto.sql | Reparto por defecto del espacio | 2.2.0 |
0007_lista_compra.sql | Lista de la compra compartida | 3.0.0 |
0008_viajes_y_avisos.sql | Viajes y eventos, suscripciones y claves de los avisos push | 3.1.0 |
0009_compra_y_gastos.sql | Compra enlazada al gasto y precios por producto | 3.2.0 |
0010_automatico.sql | Trabajo de cada mañana con pg_cron y pg_net | 3.3.0 |
0011_presupuesto_personal.sql | Límites mensuales personales | 3.6.0 |
0012_pagos_apple_pay.sql | Pagos por confirmar desde Apple Pay y clave de cada persona | 3.9.0 |
0013_pareja.sql | Perfil (color y emoji) y reacciones a los gastos | 3.12.0 |
0014_copias_e_historial.sql | Copias semanales, historial de cambios, retoques del asesor y registro de migraciones | 3.13.0 |
0015_errores.sql | Errores de la app, que se ven en Ajustes → Tus datos | 3.16.0 |
0016_retos.sql | Retos de pareja | 3.20.0 |
0017_copias_seguras.sql | Copias diarias y completas, que sobreviven 30 días a un borrado | 3.22.0 |
Crea un proyecto en Supabase (plan Free, región UE, por ejemplo Frankfurt o París).
En SQL Editor, pega y ejecuta, en orden, cada archivo de supabase/migrations/ (de 0001 a 0012).
Despliega la Edge Function avisar (Edge Functions → Deploy a new function, nombre avisar, con el código de supabase/functions/avisar/index.ts) y desactiva Verify JWT en sus ajustes: la llamada de cada mañana y la del atajo de Apple Pay no llevan sesión, y la propia función comprueba la sesión o la clave. Las claves para firmar los avisos se crean solas la primera vez.
En Authentication → URL Configuration, pon como Site URL la dirección de la app (por ejemplo https://gastos-xxx.vercel.app) y añádela también en Redirect URLs. Para probar en local, añade http://localhost:5173.
En Project Settings → API copia la Project URL y la clave anon public.
Despliega en Vercel importando este repositorio y define estas variables de entorno:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEYLa clave anon es pública por diseño (va dentro de la web); lo que protege los datos son las políticas RLS. Nunca uses la clave service_role en la app ni la subas al repositorio.
Entra en la app con tu email, crea el espacio y, en Ajustes, invita a tu pareja con su email. Pásale el enlace de la web: al entrar con ese email verá la invitación.
Cuando ya estéis los dos dentro, en Supabase ve a Authentication → Sign In / Providers y desactiva Allow new users to sign up. Así nadie más puede ni siquiera crear una cuenta (aunque tampoco podría ver vuestros datos).
El envío de emails de Supabase en el plan gratuito tiene un límite bajo por hora; para dos personas sobra. Si algún enlace no llega, revisa la carpeta de spam. La app acepta también un código de 6 dígitos del correo, pero solo llega si se configura un servidor de correo propio (Set up SMTP) y se editan las plantillas con
{{ .Token }}.
Vercel publica solo cada cambio que llega a main. Si el cambio trae un archivo nuevo en supabase/migrations/, hay que ejecutarlo una vez en Supabase antes de fusionar, para que la base de datos ya tenga lo que la nueva versión necesita. Si cambia supabase/functions/avisar/index.ts, hay que volver a desplegar la función (manteniendo Verify JWT desactivado).
Con el conector de Supabase activado en Claude, Claude puede aplicar la migración y desplegar la función directamente; si no, se pega el archivo en el SQL Editor.
0010_automatico.sql programa con pg_cron la función run_automation() a las 8:00 UTC: apunta los gastos fijos (comunes y personales) y, con pg_net, llama a la función avisar con la clave guardada en la tabla automation para mandar los avisos del día. La app también apunta los fijos pendientes al abrirse, una sola vez aunque los dos móviles lo hagan a la vez. Ajustes → Lo que la app hace sola dice cuándo trabajó por última vez. Para lanzarlo a mano: select public.run_automation(); en el SQL Editor.
Cada persona crea en su iPhone una automatización de Atajos («Cartera» → al pagar) que manda el importe y la tienda a avisar?pago=CLAVE. La función guarda el pago en pending_payments y avisa al móvil; nada cuenta hasta que se confirma en la app como gasto común o personal. La guía y la dirección con la clave de cada uno están en Ajustes → Apuntar al pagar con Apple Pay.
La app sigue versionado semántico (MAYOR.MENOR.PARCHE): funciones nuevas suben la versión menor (3.11.0), correcciones la de parche (3.11.1) y los cambios muy grandes la mayor (3.0.0 fue la lista de la compra). Cada versión publicada:
version en package.json (npm version 3.12.0 --no-git-tag-version).src/changelog.ts con sus novedades, mejoras y correcciones. La app la muestra en Ajustes → Ver novedades y avisa una vez al abrir la versión nueva.Un test comprueba que ambas versiones coinciden. Los cambios que no tocan la app (como este README) no suben versión.
npm install
cp .env.example .env.local # y rellena las dos variables
npm run dev # http://localhost:5173
Pruebas:
npm test # saldo, presupuestos, importes, tickets, extractos, compra, función avisar…
npm run lint # revisión del código (ESLint, reglas de los hooks de React)
npm run test:screens # pruebas de pantallas con Playwright contra un Supabase de mentira
deno check --node-modules-dir=none supabase/functions/avisar/index.ts # tipos de la función avisar (necesita Deno)
DATABASE_URL=postgres://… scripts/test-db.sh # migraciones + pruebas de acceso en un Postgres vacío
scripts/test-db.sh aplica todas las migraciones sobre una imitación mínima del esquema de Supabase (supabase/tests/00_stub_supabase.sql) y comprueba, entre otras cosas, que una tercera cuenta no puede ver ni modificar gastos ajenos ni entrar en un espacio lleno, y que lo personal no lo ve la pareja. GitHub Actions ejecuta todo esto y la compilación en cada PR.
Cada gasto pagado por una persona hace que la otra le deba su parte: la mitad por defecto o lo que se haya elegido en ese gasto (owed_cents). Un reembolso de A a B reduce lo que A debe a B. El cálculo está en src/lib/balance.ts y trabaja en céntimos; solo se redondea al mostrarlo.
El lector (Tesseract compilado a WebAssembly y el modelo de español, unos 6 MB) se copia a public/ocr/ al ejecutar npm run dev o npm run build (scripts/copy-ocr.mjs) y se sirve desde la propia web, sin CDNs externos. Solo se descarga la primera vez que alguien escanea. Antes de leer una foto se recorta el papel del ticket (src/lib/ocr.ts); los PDF se leen directamente como texto con pdf.js (src/lib/pdfText.ts). El texto se interpreta en src/lib/receipt.ts: total, fecha, tienda, categoría y cada producto con su precio (con formatos propios para tiendas como E.Leclerc y Carrefour). src/lib/receiptDoubts.ts decide qué conviene revisar antes de guardar (lo que el formulario marca en amarillo).
Exporta antes desde Ajustes → Tus datos si quieres guardar una copia.
Las copias de seguridad del espacio o de la cuenta borrados se guardan 30 días más (nadie las ve desde la app) por si fue un error, y luego se borran solas.
Hay dos copias, en sitios distintos:
run_backup, migraciones 0014 y 0017): una foto en JSON de lo común de cada espacio y de lo personal de cada persona, en la tabla backups. Se quedan las 14 últimas y la de cada lunes de las 8 últimas semanas. Se descargan desde Ajustes → Copias de seguridad..github/workflows/copia.yml): GitHub descarga la base de datos entera con pg_dump, comprueba que se carga en un Postgres vacío con las mismas filas y la guarda cifrada 90 días en Actions → Copia fuera de Supabase → Artifacts. Necesita los secretos SUPABASE_DB_URL (dirección Session pooler del botón Connect de Supabase, con la contraseña de la base de datos) y BACKUP_PASSPHRASE (una frase larga que no se guarda en ningún otro sitio). Se puede lanzar a mano con Run workflow.Recuperar una copia de GitHub (si se perdiera el proyecto de Supabase):
gpg -d copia-AAAA-MM-DD.tar.gz.gpg | tar -xz # pide la frase BACKUP_PASSPHRASE
# En un proyecto de Supabase nuevo, con su dirección en $DB:
psql "$DB" -f copia/cuentas.sql # primero las cuentas
psql "$DB" -f copia/app.sql # luego tablas, datos y funciones
Después, desplegar avisar y volver a programar los trabajos de 0010 y 0017 (sus bloques cron.schedule). copia/filas.txt dice cuántas filas tenía cada tabla.
Recuperar desde la tabla backups (algo borrado por error): en el SQL Editor, select taken_at, data from backups where household_id = '…' order by taken_at desc da la copia en JSON, y cada tabla se puede volver a meter con jsonb_populate_recordset.
0018_deseos.sql | Deseos y caprichos con los votos de cada uno | 3.23.0 |