home
  • Spanish (ES)
  • English (UK)
  • Portuguese (PT)
  • Hermes Agent
  • draw Proyectos UX
  • person Sobre mí
  • mail Contacto
arrow_back Volver al proyecto Inicio

Documentación del proyecto

Documentación · Task Cloud

11 archivos markdown (producto, arquitectura, API, auth, Neon, deploy Vercel…).

En esta página Visión general 0% expand_more
Visión general Enlaces Stack Producto y features Problema Solución Características CRUD de tareas Prioridad y tags Filtros por estado Offline-first Sync unitario y bulk Auth PIN + sesión PWA instalable i18n Arquitectura Diagrama lógico Capas Principios Monorepo Turborepo Scripts raíz Workspaces Superficie API Nest Rutas públicas Auth (protegidas) Tareas (protegidas) Ownership Autenticación PIN + JWT Flujo Seguridad Por qué PIN y no email/password Modelo de datos Neon Tablas users tasks sessions Índices Migraciones Deploy Vercel (Turborepo) Idea Config (vercel.json) Variables de entorno Smoke Local Decisiones de producto y tech Neon + Nest en lugar de Supabase client Turborepo monorepo + un deploy Offline primero, sync opcional PIN en vez de email/password Retos y resultados Serverless Nest en Vercel Convergencia local ↔ nube Env dual (web + api) Skill Angular (claude-workflow) Qué se aplicó en apps/web Qué queda fuera a propósito (Ionic + v20) House style (extracto) Referencia en el repo de la app

01-overview.md

Visión general · Task Cloud

Task Cloud es una Progressive Web App de gestión de tareas personales con soporte offline real y sincronización en la nube.

El repositorio es un monorepo npm workspaces + Turborepo:

  • apps/web — Angular 20 + Ionic 8 (PWA)
  • apps/api — NestJS 11 sobre Neon Postgres

En producción se despliega como un único proyecto Vercel: el front se sirve como estáticos y Nest se envuelve en una función serverless (api/index.js) detrás de /api/* (same-origin, sin CORS).

Enlaces

  • Web: https://task-manager-cristiancode.vercel.app
  • GitHub: Criscode2022/task-manager-cloud
  • Health: GET /api/health → { "ok": true, "service": "task-cloud-nest-api" }

Stack

CapaTecnología
MonorepoTurborepo + npm workspaces
WebAngular 20, Ionic 8, Tailwind, service worker
APINestJS 11, jose, bcryptjs
DatosNeon Postgres (@neondatabase/serverless)
DeployVercel (static + serverless function)

02-product.md

Producto y features

Problema

Las apps de tareas suelen exigir registro completo, fallan sin red o acoplan el cliente a un BaaS monolítico. El objetivo era una PWA instalable, usable 100% offline, con sync opcional controlada por una API propia sobre Postgres serverless (Nest + Neon + Vercel).

Solución

Cliente offline-first con Ionic Storage. Con sesión activa, sube y baja tareas vía Nest. Auth ligera por PIN de 8 dígitos (hash + pepper) y sesiones JWT. Deploy unificado en Vercel.

Características

CRUD de tareas

Crear, editar, completar y eliminar con feedback inmediato en la UI Ionic.

Prioridad y tags

Campos priority (low / medium / high) y tags[] en Postgres, con índices GIN.

Filtros por estado

Vistas de pendientes, completadas o todas, orientadas al uso diario en móvil.

Offline-first

Ionic Storage mantiene la lista usable sin red; la nube no es SPOF de la UI.

Sync unitario y bulk

API REST: POST /tasks, POST /tasks/bulk, PUT, DELETE protegidos por JWT.

Auth PIN + sesión

Registro/login con PIN, JWT (sub + sid), tabla sessions y rate-limit en auth.

PWA instalable

Service worker Angular y layout app-like en móvil y escritorio.

i18n

ngx-translate con idiomas EN/ES en el cliente.

03-architecture.md

Arquitectura

Diagrama lógico

Browser (PWA Ionic/Angular)
    │  offline: Ionic Storage
    │  online + sesión: HTTP /api/*
    ▼
NestJS (apps/api)  ──@neondatabase/serverless──►  Neon Postgres
    ▲
api/index.js (Vercel serverless, warm cache)

mcp-server/ (opcional, Python FastMCP) ──► Neon

Capas

  1. Cliente — Angular 20 + Ionic 8 + Tailwind. Servicios de dominio: TaskService, TaskNeonService, NeonApiService.
  2. API — NestJS 11: módulos auth, users, tasks, health, rate-limit y exception filter.
  3. Datos — Neon Postgres: users, tasks, sessions.
  4. Deploy — Un proyecto Vercel: turbo build web+api; output apps/web/www; rewrites /api/* → función.
  5. Local — API :3001, web :4200 con proxy /api; npm run start:full.

Principios

  • Offline primero; sync opcional.
  • Secretos solo en servidor (DATABASE_URL, JWT_SECRET, PIN_PEPPER).
  • Same-origin en producción (API_BASE_URL=/api).

04-monorepo.md

Layout del monorepo

PathRol
apps/webPWA Ionic/Angular (output www/)
apps/apiNestJS (dist/) consumido por la función Vercel
api/index.jsEntry serverless: boot Nest + cache warm
packages/Reservado para shared packages
mcp-server/MCP Python opcional (FastMCP + uv)
vercel.jsonBuild, functions, rewrites SPA + /api
turbo.jsonTasks, env y remote cache

Scripts raíz

ScriptDescripción
npm run apiNest en watch
npm startAngular + proxy /api
npm run start:fullAPI + web vía turbo
npm run buildBuild de todos los packages
npm run build:web / build:apiBuild selectivo
npm run test:ciTests web headless
npm run test:apiSmoke HTTP de la API

Workspaces

"workspaces": ["apps/*", "packages/*"]

Package manager: npm 10. Node ≥ 20.

05-api.md

Superficie API (Nest)

Prefijo global: /api. Rutas de tareas y sesión protegidas con Authorization: Bearer <jwt>.

Rutas públicas

MétodoPathNotas
GET/api/healthHealthcheck del servicio Nest
POST/api/usersRegistro con PIN → sesión JWT
POST/api/auth/loginLogin por PIN → token + expires_at

Auth (protegidas)

MétodoPathNotas
POST/api/auth/logoutRevoca sesión
GET/api/auth/meUsuario actual

Tareas (protegidas)

MétodoPathNotas
GET/api/tasksLista del usuario autenticado
POST/api/tasksCrear tarea
POST/api/tasks/bulkCarga masiva
PUT/api/tasks/:idActualizar (ownership)
DELETE/api/tasks/:idBorrar una
DELETE/api/tasksBorrar todas del usuario

Ownership

Las mutaciones validan user_id === auth.userId. No hay multi-tenant organizacional: cada PIN/usuario es un namespace de tareas.

06-auth.md

Modelo de autenticación

Flujo

  1. El usuario crea cuenta con un PIN de 8 dígitos (Options en la PWA).
  2. El servidor hashea el PIN (bcryptjs + PIN_PEPPER) y guarda pin_hash / pin_lookup.
  3. Se crea una fila en sessions (UUID) y se emite un JWT (jose, HS256) con claims sub (userId) y sid (sessionId).
  4. El cliente guarda el token y lo envía en Authorization: Bearer ….
  5. Logout revoca la sesión; TTL configurable (SESSION_TTL_SECONDS, default 24h).

Seguridad

  • Rate-limit en endpoints de auth (por IP, en memoria por instancia de función).
  • DATABASE_URL nunca se expone al browser.
  • PIN no se almacena en claro.

Por qué PIN y no email/password

Onboarding en segundos para uso personal multi-dispositivo, sin buzones ni reset de contraseña, manteniendo hash + pepper + rate-limit.

07-data-model.md

Modelo de datos (Neon Postgres)

Tablas

users

  • id BIGSERIAL PK
  • pin_hash TEXT NOT NULL
  • pin_lookup TEXT (índice único parcial)
  • created_at timestamptz

tasks

  • id BIGSERIAL PK
  • user_id BIGINT FK → users ON DELETE CASCADE
  • title TEXT NOT NULL
  • description TEXT
  • done BOOLEAN DEFAULT false
  • priority TEXT DEFAULT medium CHECK (low|medium|high)
  • tags TEXT[] DEFAULT {}
  • created_at / updated_at (trigger handle_updated_at)

sessions

  • id UUID PK
  • user_id BIGINT FK
  • expires_at / revoked_at / created_at

Índices

  • tasks(user_id), tasks(done), tasks(priority)
  • GIN en tasks(tags)
  • tasks(created_at DESC)
  • sessions(user_id), sessions(expires_at)

Migraciones

SQL versionado en el repo: neon-migration.sql, neon-auth-migration.sql.

08-deploy-vercel.md

Deploy en Vercel (un solo proyecto)

Idea

  • apps/web → estáticos en CDN (apps/web/www)
  • apps/api → función serverless vía api/index.js (incluye apps/api/dist/**)
  • /api/* reescrito a la función; el resto a la SPA

Config (vercel.json)

  • buildCommand: npx turbo run build --filter=@task-cloud/web --filter=@task-cloud/api
  • outputDirectory: apps/web/www
  • functions.api/index.js.maxDuration: 30
  • Rewrites: /api/:path* → /api/index; resto → /index.html

Variables de entorno

NombreRequeridoNotas
DATABASE_URLsíConnection string Neon
JWT_SECRETsíopenssl rand -hex 32
PIN_PEPPERsíopenssl rand -hex 32
SESSION_TTL_SECONDSnodefault 86400
API_BASE_URLnodefault /api (same origin)

Smoke

  1. GET https://task-manager-cristiancode.vercel.app/api/health
  2. Abrir la web → Options → registrar PIN → crear/sync tareas

Local

npm install
cp .env.example .env   # DATABASE_URL, JWT_SECRET, PIN_PEPPER
npm run api            # :3001
npm start              # :4200 con proxy /api

09-decisions.md

Decisiones

Neon + Nest en lugar de Supabase client

La nube es Postgres en Neon y una API Nest propia: SQL y esquema controlados, secretos solo en servidor, mismo patrón que el resto del portfolio. Se dejó el acoplamiento a Supabase Auth/Realtime porque el producto solo necesita PIN, sesiones y tareas.

Turborepo monorepo + un deploy

Web y API comparten lockfile, turbo cache y un solo pipeline Vercel. Evita dos proyectos desincronizados y simplifica same-origin /api.

Offline primero, sync opcional

Las features core de tareas no requieren API; la nube es continuidad multi-dispositivo, no el camino crítico de cada tap.

PIN en vez de email/password

Onboarding en segundos para uso personal, sin buzones ni flujos de reset, manteniendo hash + pepper + rate-limit.

10-challenges-results.md

Retos y aprendizajes

Serverless Nest en Vercel

Adapter Express, cold start, maxDuration y reutilizar la app entre invocaciones warm (api/index.js).

Convergencia local ↔ nube

Mapear ids locales tras upload, bulk sync y no pisar ownership entre usuarios.

Env dual (web + api)

set-env.js genera environment.*.local.ts desde el .env raíz; secretos nunca van al bundle del browser.

Resultados

  • Monorepo operativo con CI (lint, tests web, build API) en GitHub Actions.
  • PWA en producción en Vercel (estáticos + API Nest en /api) sobre Neon.
  • API con health, auth PIN/JWT, CRUD/bulk de tareas y esquema Neon versionado en SQL.
  • Código abierto como referencia Angular/Ionic + Nest + Neon + Turborepo + Vercel.

11-angular-skill.md

Skill Angular · claude-workflow

Task Cloud adopta la skill angular del repo interno claude-workflow (.claude/skills/angular). Vive en el monorepo en:

.claude/skills/angular/

ArchivoContenido
SKILL.mdDefaults Angular 22 + house style
style-standards.mdSignals first, visibilidad, const, spread, early returns
signals.mdcomputed, effect, resource/httpResource, zoneless
signal-forms.mdSignal Forms (estable en v22)
components.mdStandalone, @if/@for, SSR, Vitest
animations.mdanimate.enter / View Transitions

La skill pide mirar el major del repo y no mezclar eras. Task Cloud está en Angular 20 + Ionic 8 (Ionic 8 soporta hasta Angular 20.x). Se aplicó el house style y las novedades de v20 que encajan con Ionic; se dejaron las APIs estables solo en v21/v22.

Qué se aplicó en apps/web

  1. ng update a Angular 20.3 y Material/CDK 20. TypeScript moduleResolution: bundler.
  2. Control flow nativo. Migración de *ngIf restantes a @if (lista, PIN dialog).
  3. HTTP moderno. Se quitó el deprecado HttpClientModule; provideHttpClient(withFetch(), withInterceptors(...)) usa el backend Fetch de Angular 20.
  4. OnPush en TabListPage, TabOptionsPage, diálogo de edición y PIN dialog (alineado con el default implícito de majors posteriores).
  5. Signals first. PIN dialog: copied/confirmed como signals. TaskService.storageReady en vez de BehaviorSubject. Diálogo de edición con firstValueFrom.
  6. Formularios. Sigue ReactiveFormsModule: Signal Forms aún no son el default estable de v20 (lo son en v22). Ionic sigue usando NgModule en el shell.

Qué queda fuera a propósito (Ionic + v20)

  • Signal Forms / formField (estable en Angular 22)
  • httpResource() / resource() siguen experimentales en 20.3
  • Zoneless (provideExperimentalZonelessChangeDetection) — Ionic 8 + zone.js es el camino soportado
  • Vitest (Karma sigue en este builder)
  • ng update a 21+ no cabe: Ionic 8 declara máximo Angular 20.x

House style (extracto)

  • Estado y derivación en signal / computed / linkedSignal. RxJS solo para streams reales (debounce, websockets, races); al entrar, toSignal().
  • private readonly por defecto; protected solo si la plantilla lo lee; público es la excepción.
  • const para locales; nunca var.
  • Arrays/objetos por spread ([...xs, x], { ...o, k }, toSorted()), nunca push/splice sobre estado compartido.
  • Guards arriba, happy path plano.
  • En código nuevo: standalone, inject(), input()/output(), @if/@for, no NgModules ni @Input().

Referencia en el repo de la app

Tras clonar task-manager-cloud, los agentes que lean .claude/skills/angular/SKILL.md aplican estas reglas al tocar apps/web.

En esta página

Visión general Enlaces Stack Producto y features Problema Solución Características CRUD de tareas Prioridad y tags Filtros por estado Offline-first Sync unitario y bulk Auth PIN + sesión PWA instalable i18n Arquitectura Diagrama lógico Capas Principios Monorepo Turborepo Scripts raíz Workspaces Superficie API Nest Rutas públicas Auth (protegidas) Tareas (protegidas) Ownership Autenticación PIN + JWT Flujo Seguridad Por qué PIN y no email/password Modelo de datos Neon Tablas users tasks sessions Índices Migraciones Deploy Vercel (Turborepo) Idea Config (vercel.json) Variables de entorno Smoke Local Decisiones de producto y tech Neon + Nest en lugar de Supabase client Turborepo monorepo + un deploy Offline primero, sync opcional PIN en vez de email/password Retos y resultados Serverless Nest en Vercel Convergencia local ↔ nube Env dual (web + api) Skill Angular (claude-workflow) Qué se aplicó en apps/web Qué queda fuera a propósito (Ionic + v20) House style (extracto) Referencia en el repo de la app

0% leído

folder_zip Descargar todos (ZIP)
Hermes Agent