尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 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 |