// Portfolio Project — Piano Operativo v2

SynapsisForge Plan

Angular · NestJS · PostgreSQL · MongoDB · Redis · Braintree/PayPal · AWS · Docker (tardivo) · CI/CD GitLab
📊 Progressi del progetto
0%
Imposta la data di inizio con il pulsante "Data Inizio" per attivare le stime di completamento.
📅 Gantt — Avanzamento per Fase
Pianificato Completato In ritardo Oggi
Angular 18 NestJS Node.js 20 TypeScript PostgreSQL MongoDB Redis · BullMQ Braintree · PayPal AWS S3 Docker · GitLab CI
Giorni totali
125
~3 mesi
Ore totali
~500
a 4h/giorno
Fasi
10
01–10
Mese 1
G 1–56
Core features
Mese 2
G 57–100
Redis·Jobs·Pay·Deploy
Mese 3
G 101–125
Test·Security·Portfolio
Tipo → Studio/Teoria Implementazione DevOps/Infra Milestone
01
Setup, TypeScript & Database
Giorni 1–7 · 7 giorniSetup / Studio
// Obiettivo della fase
Prima di scrivere una sola riga di codice applicativo, costruisci un ambiente solido e padroneggi TypeScript — il linguaggio comune di NestJS e Angular. I database (PostgreSQL, MongoDB, Redis) girano in Docker Compose fin da subito, così eviti conflitti di installazione locale e ti concentri sul codice.
giorno1
Architettura & ambiente di sviluppo
  • Installa: Node.js 20, VS Code, Git, Postman, MongoDB Compass, pgAdmin
  • Crea il monorepo: SynapsisForge-hub/backend/, frontend/, infra/
  • Disegna su carta o Excalidraw l'architettura completa: Angular → NestJS → PostgreSQL + MongoDB + Redis → S3 → Braintree
  • Inizializza il repository Git, crea .gitignore per Node e il file .env.example
giorno2
Docker Compose — solo infrastruttura DB
  • Crea infra/docker-compose.yml con tre servizi: postgres:16, mongo:7, redis:7
  • Configura volumi per la persistenza dei dati, esponi le porte standard (5432, 27017, 6379)
  • Avvia con docker-compose up -d e verifica le connessioni con pgAdmin, MongoDB Compass e redis-cli
  • Crea il database SynapsisForge in PostgreSQL e sperimenta i tipi UUID, JSONB, TIMESTAMPTZ
💡 Solo i DB in Docker: l'app la tieni locale per ora — hot-reload più veloce, debug più semplice. Imparerai a containerizzare l'app nella Fase 09.
giorno3
TypeScript — tipi, interfacce, generics
  • Studia (4h): tipizzazione statica, differenza tra interface e type, enum, generics di base come <T>, readonly, optional chaining (?.)
  • Esercizio: riscrivi in TypeScript una classe JavaScript che già conosci (es. un service CRUD con tipizzazione completa)
  • Capire perché TypeScript è obbligatorio in NestJS — ogni decoratore come @Controller o @Injectable è una feature TS
giorno4
TypeScript — decoratori, utility types
  • Studia (4h): class decorator e method decorator, Partial<T>, Pick<T>, Omit<T>, union types, type guards, abstract class
  • Esercizio pratico: scrivi un decoratore custom @Log che stampa in console gli argomenti di ogni metodo che decora
  • Esplora il file tsconfig.json: capisci le opzioni strict, experimentalDecorators, emitDecoratorMetadata
giorno5
PostgreSQL vs MySQL & MongoDB vs SQL — confronto guidato
  • PostgreSQL vs MySQL: PG ha JSONB con indici, UUID nativo, array columns, window functions più potenti. MySQL è più semplice ma meno espressivo
  • MongoDB vs SQL: MongoDB non ha schema fisso — ideale per contenuto delle lezioni (ogni lezione può avere strutture diverse). PostgreSQL per relazioni forti (utenti, pagamenti)
  • Esegui query di esempio su entrambi i database per familiarizzare con le sintassi diverse
giorno6
Schema del database — progettazione
  • Disegna il diagramma ER completo: users, courses, lessons, enrollments, reviews, certificates, payments, categories
  • Decidi cosa va in PostgreSQL (relazioni, pagamenti, iscrizioni) e cosa in MongoDB (contenuto lezioni con struttura variabile)
  • Disegna anche la struttura del documento MongoDB per una lezione: videoUrl, transcript, attachments, quiz array
giorno7
Schema DB & definizione API
  • Revisiona il diagramma ER e correggi eventuali relazioni mancanti o cicliche
  • Elenca tutti gli endpoint REST che l'app dovrà esporre: metodo HTTP, path, payload atteso, risposta attesa
  • Crea il file API_SPEC.md nel repo con la lista degli endpoint — sarà il tuo contratto di sviluppo
🎯 Milestone: ambiente pronto, TypeScript padroneggiato, schema DB progettato. Si costruisce.
02
NestJS — Core: Module, Controller, Service, DTO, Pipe, Guard, Interceptor
Giorni 8–24 · 17 giorniStudio + Build
// I building block di NestJS — nell'ordine giusto
Module → unità organizzativa. Ogni feature è un modulo (UsersModule, CoursesModule).
Controller → gestisce le richieste HTTP, estrae parametri, delega la logica al Service.
Service → contiene la logica di business, è @Injectable() e viene iniettato tramite DI.
DTO → classe TypeScript che descrive i dati in ingresso, usata con class-validator.
Pipe → trasforma o valida i dati prima che arrivino al controller.
Guard → decide se la richiesta può procedere (autenticazione, autorizzazione).
Interceptor → wrappa la richiesta/risposta per logging, caching o trasformazione.
giorno8
NestJS — primo progetto e struttura
  • Installa la CLI: npm i -g @nestjs/cli e crea il progetto: nest new backend
  • Esplora la struttura generata: app.module.ts, app.controller.ts, app.service.ts
  • Aggiungi un endpoint GET /health che restituisce un JSON con status e timestamp — servirà per i check AWS
  • Lancia il server con npm run start:dev e testa /health con Postman
giorno9
Module system & Dependency Injection
  • Studia (1.5h): come funziona il DI container — providers, exports, imports nel decoratore @Module()
  • Genera il modulo Users con la CLI: nest g module users, poi nest g controller users, nest g service users
  • Osserva come Controller e Service vengono automaticamente registrati nel modulo
  • Inietta UsersService nel costruttore di UsersController e verifica che NestJS risolva la dipendenza
giorno10
DTOs & ValidationPipe globale
  • Installa: npm i class-validator class-transformer
  • Crea create-user.dto.ts con i decoratori: @IsEmail(), @IsString(), @MinLength(8), @IsEnum(UserRole)
  • Abilita la ValidationPipe globale in main.ts con whitelist: true e transform: true
  • Testa con Postman: invia un payload invalido e verifica la risposta 400 con dettagli degli errori
💡 whitelist: true è fondamentale: rimuove automaticamente i campi extra dal body, impedendo che un utente invii "isAdmin": true e se lo ritrovi nel service.
giorno11
TypeORM setup & entity User
  • Installa: npm i @nestjs/typeorm typeorm pg e configura TypeOrmModule.forRoot() in AppModule
  • Crea la entity User: @Entity(), @PrimaryGeneratedColumn("uuid"), @Column(), @CreateDateColumn()
  • Aggiungi il campo role come enum (student, instructor, admin) con valore di default "student"
  • Usa synchronize: true solo in sviluppo — capire perché è pericoloso in produzione e cosa sono le migrazioni
giorno12
Entity Course & relazioni TypeORM
  • Crea la entity Category: id, name, slug, description
  • Crea la entity Course: id, title, slug, description, price, status (draft/published), thumbnail, @ManyToOne() verso User (instructor) e Category
  • Crea la entity Lesson: id, title, order, durationSeconds, contentId (stringa MongoDB), @ManyToOne() verso Course
  • Esegui le migrazioni e verifica le tabelle create in pgAdmin
giorno13
Entity Enrollment, Review, Certificate
  • Entity Enrollment: id, student (FK User), course (FK Course), progressPercent, enrolledAt, completedAt
  • Entity Review: id, rating (1-5), comment, @ManyToOne() verso Enrollment — vincolo unique su coppia student+course
  • Entity Certificate: id, @OneToOne() verso Enrollment, issuedAt, pdfUrl
  • Entity Payment: id, user, course, amount, currency, gatewayId, status, createdAt
giorno14
CoursesService — CRUD con Repository
  • Inietta il repository con @InjectRepository(Course) nel CoursesService
  • Implementa findAll() con paginazione (skip + take) e filtri per categoria e status
  • Implementa findBySlug(), create(), update(), remove()
  • Usa QueryBuilder per la ricerca testuale su titolo e descrizione
giorno15
CoursesController & Swagger
  • Crea CoursesController con tutti gli endpoint REST mappati ai metodi del service
  • Installa @nestjs/swagger, configura SwaggerModule in main.ts su /api/docs
  • Decora i DTO con @ApiProperty() e i controller con @ApiOperation(), @ApiResponse()
  • Testa tutti gli endpoint con Postman e verifica la documentazione Swagger
giorno16
MongoDB + Mongoose — LessonContent
  • Installa @nestjs/mongoose mongoose e configura MongooseModule.forRoot()
  • Crea lo Schema LessonContent: lessonId, videoUrl, transcript, attachments (array), quiz (array di oggetti con question/options/correctAnswer)
  • Crea LessonsService con metodi per creare e aggiornare il contenuto MongoDB
  • Endpoint GET /lessons/:id/content: fonde dati PostgreSQL + MongoDB in una risposta unica
giorno17
UsersService & EnrollmentsService
  • UsersService: getProfile, updateProfile, listCourses per instructor
  • EnrollmentsService: metodo enroll(userId, courseId) che verifica unicità e crea il record
  • Metodo updateProgress(enrollmentId, lessonId) che ricalcola progressPercent
  • Usa EventEmitter2 per emettere un evento quando progressPercent raggiunge 100%
giorno18
Interceptors — logging, transform, timeout
  • Studia (1h): ciclo di vita di un Interceptor in NestJS con RxJS tap() e map()
  • Crea TransformInterceptor globale: wrappa ogni risposta in { data, statusCode, timestamp }
  • Crea LoggingInterceptor: logga method, path, status code e tempo di risposta
  • Crea TimeoutInterceptor: se un endpoint non risponde entro 10 secondi, risponde 504
giorno19
Exception filters — gestione errori uniforme
  • Studia (1h): eccezioni built-in di NestJS: NotFoundException, ForbiddenException, ConflictException
  • Crea un HttpExceptionFilter globale che standardizza TUTTI gli errori: { error, message, statusCode, path }
  • Intercetta anche gli errori TypeORM (unique constraint violation) e convertili in ConflictException
  • Testa ogni tipo di errore con Postman e verifica la consistenza del formato
giorno20
Custom Pipes — validazioni avanzate
  • Crea ParseUuidPipe: valida che un parametro sia un UUID v4 valido, lancia BadRequestException se no
  • Applica la pipe a livello di parametro: @Param("id", ParseUuidPipe) su ogni endpoint che riceve un ID
  • Crea ParsePositiveIntPipe: per parametri di paginazione (page, limit) che devono essere interi positivi
  • Aggiungi throttling globale con @nestjs/throttler: max 100 req/minuto per IP
giorno21
ReviewsService & CertificateService base
  • ReviewsService: crea/aggiorna/elimina recensione — verifica che l'utente sia iscritto e abbia completato il corso
  • Regola una sola recensione per coppia student+course con unique constraint TypeORM
  • CertificateService: listener sull'evento "enrollment.completed" che crea il record Certificate
  • Endpoint GET /certificates/verify/:id — pubblico, verifica l'autenticità di un certificato
giorno22
Admin module — backoffice endpoints
  • Crea AdminModule con endpoints dedicati protetti dal ruolo admin
  • Endpoint per listare tutti gli utenti con filtri per ruolo e stato
  • Endpoint per approvare/rifiutare corsi (cambia status da "pending" a "published")
  • Endpoint per le statistiche globali: totale utenti, corsi pubblicati, revenue del mese
giorno23
Seed data & testing API completa
  • Crea uno script di seed con TypeORM DataSource: 3 categorie, 2 instructor, 5 corsi, 10 studenti, iscrizioni
  • Esegui il seed e verifica tutti i dati con pgAdmin e MongoDB Compass
  • Fai una sessione completa di testing con Postman: ogni endpoint con caso positivo e negativo
  • Aggiorna la documentazione Swagger con i tag corretti su ogni controller
giorno24
Backend review & fix
  • Revisiona tutti gli endpoint: nomi consistenti? DTO completi? Errori standardizzati?
  • Aggiungi select_related equivalente TypeORM dove mancano le relazioni caricate (evita N+1 query)
  • Verifica che la ValidationPipe blocchi i payload malformati su TUTTI gli endpoint
  • Scrivi i primi test con @nestjs/testing + Supertest per i 5 endpoint più critici
🎯 Milestone: backend NestJS completo con tutti i building block. API documentata e testabile.
03
Autenticazione — JWT, OAuth2 (Google/GitHub), RBAC
Giorni 25–35 · 11 giorniStudio + Build
// JWT, OAuth2 e RBAC — la triade dell'auth moderna
JWT: token firmato (Header.Payload.Signature) che il client invia in ogni richiesta nell'header Authorization: Bearer <token>. Il server verifica la firma senza query al DB — è stateless. Access token breve (15 min), refresh token lungo (7 giorni).

OAuth2: protocollo di delega. Google certifica l'identità dell'utente, il tuo backend emette poi il proprio JWT. Non è alternativo a JWT — sono complementari.

RBAC (Role-Based Access Control): ogni utente ha un ruolo, ogni ruolo mappa a permessi. In NestJS si implementa con i Guards, che è il posto architetturalmente corretto.
giorno25
JWT — teoria e setup Passport.js
  • Studia (2h): anatomia JWT, algoritmo HMAC-SHA256, differenza access token (15 min) e refresh token (7 giorni)
  • Dove salvare i token: access token in memoria JS, refresh token in cookie HttpOnly (non accessibile da JS)
  • Installa: @nestjs/passport passport passport-jwt @nestjs/jwt bcrypt
  • Crea AuthModule con JwtModule.registerAsync() che legge la secret dal ConfigService
giorno26
Implementazione login & JWT emissione
  • Endpoint POST /auth/login: verifica email + password con bcrypt, genera access e refresh token
  • Crea JwtStrategy che estende PassportStrategy: estrae il payload, verifica che l'utente esista
  • Crea JwtAuthGuard: applica globalmente e usa decoratore @Public() per gli endpoint aperti
  • Salva l'hash del refresh token sul DB User — serve per invalidarlo al logout
giorno27
Refresh token & logout
  • Endpoint POST /auth/refresh: riceve il refresh token dal cookie HttpOnly, verifica hash sul DB, emette nuovo access token
  • Endpoint POST /auth/logout: rimuove l'hash del refresh token dal DB (invalida il token)
  • Configura la risposta del login per impostare il cookie HttpOnly con il refresh token (sameSite, secure, httpOnly)
  • Testa il ciclo completo con Postman: login → chiamata protetta → scadenza simulata → refresh → nuova chiamata
giorno28
OAuth2 — teoria flusso Authorization Code
  • Studia (2h): i 4 grant types OAuth2, perché Authorization Code è il più sicuro per web app
  • Traccia il flusso Google passo per passo: click → redirect Google → consenso → redirect con code → scambio code/token → emissione JWT proprio
  • Registra le app su Google Cloud Console e GitHub Developer Settings (gratuito) — ottieni client_id e client_secret
giorno29
OAuth2 — implementazione Google login
  • Installa passport-google-oauth20 e crea GoogleStrategy
  • Il metodo validate() riceve il profilo Google: crea l'utente se non esiste, collega il provider se esiste già
  • Endpoint GET /auth/google → redirect a Google. Endpoint GET /auth/google/callback → emette JWT e redirect al frontend
  • Testa il flusso completo aprendo il link nel browser
giorno30
OAuth2 — GitHub login
  • Installa passport-github2 e crea GithubStrategy con la stessa logica di Google
  • Endpoint GET /auth/github e GET /auth/github/callback
  • Gestisci il caso in cui GitHub non fornisce email pubblica (email può essere null)
  • Testa entrambi i provider e verifica che gli utenti vengano creati correttamente nel DB
giorno31
RBAC — decoratori custom e RolesGuard
  • Crea il decoratore @Roles("instructor", "admin") usando SetMetadata
  • Crea RolesGuard: usa Reflector per leggere i ruoli dai metadati, confronta col ruolo dell'utente corrente
  • Configura l'ordine corretto: prima JwtAuthGuard (autenticazione), poi RolesGuard (autorizzazione)
  • Applica @Roles("instructor") agli endpoint di creazione/modifica corsi e @Roles("admin") al modulo Admin
giorno32
Registrazione & email verification
  • Endpoint POST /auth/register: crea utente con password hashata, invia email di verifica (per ora con console backend)
  • Endpoint GET /auth/verify-email/:token: attiva l'account (token UUID salvato nel DB)
  • Endpoint POST /auth/password/reset e POST /auth/password/confirm
  • Aggiungi campo isVerified su User — gli utenti non verificati non possono iscriversi ai corsi
giorno33
CORS & security headers
  • Configura app.enableCors() in main.ts: origin solo dal dominio del frontend, credentials true
  • Installa helmet e abilitalo in main.ts: imposta automaticamente 12+ security header HTTP
  • Testa CORS con una chiamata fetch dal browser verso il backend
  • Verifica i security header con securityheaders.com
giorno34
Test Auth — tutti gli scenari
  • Testa con Postman: utente non autenticato su endpoint protetto → 401
  • Student che tenta di creare un corso → 403. Instructor che modifica il corso di un altro → 403
  • Admin su qualsiasi endpoint → 200. Token scaduto → 401. Refresh con token invalido → 401
  • Verifica che il cookie HttpOnly sia impostato correttamente nel browser (non visibile in JS)
giorno35
Auth review & integrazione con tutti i moduli
  • Applica @UseGuards(JwtAuthGuard, RolesGuard) e @Roles() correttamente su OGNI endpoint
  • Verifica che gli endpoint pubblici (lista corsi, dettaglio corso, verifica certificato) funzionino senza token
  • Aggiungi @ApiBearerAuth() su Swagger per gli endpoint protetti
  • Scrivi test di integrazione Auth: login → token → endpoint protetto → logout
🎯 Milestone: sistema auth completo. JWT + OAuth2 Google/GitHub + RBAC 3 livelli.
04
Angular — SPA, PWA, Routing, Auth Client, UI
Giorni 36–60 · 25 giorniImplementazione
// Angular vs React — le differenze che contano
Già conosci React, quindi componenti, props e stato non ti sono nuovi. Le differenze chiave di Angular: è un framework completo (routing, HTTP client, forms, DI — tutto incluso). Usa TypeScript nativo. Il sistema di Dependency Injection è identico a NestJS. I Signals (Angular 17+) rimpiazzano gradualmente RxJS per lo state locale.

PWA: Angular ha un pacchetto ufficiale @angular/pwa che aggiunge automaticamente Service Worker (via Workbox) e Web App Manifest con un singolo comando.
giorno36
Angular — installazione e fondamentali template
  • Installa Angular CLI: npm i -g @angular/cli e crea il progetto: ng new frontend --standalone --routing --style=scss
  • Studia (2h): sintassi template (@if, @for, two-way binding con [(ngModel)])
  • Capire @Input() e @Output() + EventEmitter — equivalente di props/emit in React
  • Lifecycle hooks: ngOnInit(), ngOnDestroy() — quando usarli
giorno37
Services & Dependency Injection in Angular
  • Studia (1.5h): @Injectable({ providedIn: "root" }) — come Angular risolve le dipendenze nel costruttore (identico a NestJS)
  • Crea un ApiService con HttpClient che wrappa tutte le chiamate al backend
  • Configura il base URL del backend tramite environment file (environment.ts)
  • Testa una chiamata GET alla lista corsi e stampa il risultato in console
giorno38
Signals & RxJS base
  • Studia (1.5h): Angular Signals — signal(), computed(), effect(). Sono reattivi ma più semplici di RxJS
  • RxJS essenziale: Observable, subscribe, map, switchMap, takeUntilDestroyed
  • Confronto: quando usare Signal (state locale UI) vs Observable (stream asincroni da HTTP)
  • Esercizio: crea un componente con un Signal counter e uno con un Observable HTTP
giorno39
Routing, layout e route guards
  • Configura le route principali con lazy loading: loadComponent per le standalone components
  • Crea le route annidate: DashboardLayout con figli my-courses, profile, certificates
  • Crea AuthGuard (canActivate): controlla se autenticato, altrimenti redirect a /login
  • Crea RoleGuard: legge data: { roles: ["instructor"] } dalla route e confronta con il ruolo utente
giorno40
HTTP Interceptor & JWT refresh automatico
  • Crea AuthInterceptor: aggiunge automaticamente Authorization: Bearer <token> ad ogni richiesta verso la tua API
  • Implementa il refresh automatico: intercetta le risposte 401, chiama /auth/refresh, reinvia la richiesta originale
  • Usa una coda di richieste pendenti durante il refresh per evitare race conditions
  • Crea AuthService con Signal: currentUser = signal<User | null>(null)
giorno41
Pagine auth — Login e Register
  • Installa Tailwind CSS con Angular: npm i tailwindcss postcss autoprefixer
  • Pagina di login con Reactive Forms (FormGroup, FormControl, Validators)
  • Pagina di registrazione con validazione cross-field (password === confirmPassword) tramite Validator custom
  • Bottoni OAuth: link a /api/auth/google e /api/auth/github — il redirect avviene lato backend
giorno42
Homepage — hero, corsi in evidenza, categorie
  • Hero section con titolo, sottotitolo e CTA
  • Sezione corsi in evidenza: griglia 3 colonne con chiamata API GET /courses?featured=true
  • Sezione categorie: pill/badge cliccabili che portano al catalogo con il filtro preselezionato
  • Sezione statistiche: totale studenti, corsi, ore di contenuto (dati statici per ora)
giorno43
Catalogo corsi — griglia e filtri
  • Griglia di course card con immagine, titolo, instructor, rating, prezzo
  • Filtri laterali: categoria, prezzo (range), livello — sincronizza con URL queryParams con ActivatedRoute
  • Ricerca testuale con debounce: debounceTime(300) + distinctUntilChanged() + switchMap()
  • Paginazione: bottone "Carica altri" o paginazione numerica
giorno44
Pagina dettaglio corso
  • Header: thumbnail, titolo, descrizione, rating aggregato, numero iscritti
  • Accordion del curriculum: sezioni espandibili con lista lezioni, durata e icona "bloccata" per non iscritti
  • Sezione istruttore: avatar, bio, altri corsi dell'instructor
  • Sidebar sticky: prezzo, CTA "Acquista" o "Continua a studiare" se già iscritto, progress bar
giorno45
Player lezioni — layout e video
  • Layout a due colonne: video player (sinistra) + navigazione lezioni (destra)
  • Video player HTML5 nativo: evento timeupdate salva il timestamp ogni 10 secondi via API
  • Navigazione lezioni: lista con stato completamento (checked/unchecked/locked)
  • Recupero del signed URL per il video dalla API (per ora URL diretto, signed URL in Fase 08)
giorno46
Player lezioni — quiz interattivo
  • Componente Quiz: mostra le domande una alla volta con animazione di transizione
  • Feedback immediato: risposta corretta → verde + spiegazione, sbagliata → rossa + risposta corretta
  • Salva il completamento del quiz via API (POST /enrollments/:id/progress)
  • Al completamento dell'ultima lezione, mostra un modale di congratulazioni con link al certificato
giorno47
Dashboard studente
  • Lista corsi iscritti con progress bar per ognuno e link "Continua"
  • Sezione corsi completati con badge di completamento e bottone download certificato
  • Storico attività: ultime 10 lezioni viste (data, corso, lezione)
  • Profilo utente: avatar, nome, bio, pulsante modifica
giorno48
Dashboard instructor — lista corsi e analytics
  • Lista dei propri corsi con status badge (draft/published) e metriche rapide (iscritti, rating)
  • Grafici con ng-charts: iscrizioni negli ultimi 30 giorni (line chart), distribuzione rating (bar chart)
  • Tabella top lezioni per tempo di visualizzazione
  • CTA "Crea nuovo corso" prominente
giorno49
Dashboard instructor — form creazione corso
  • Form multi-step con Angular Stepper: Info base → Curriculum → Prezzo → Anteprima
  • Step 1: titolo, slug (auto-generato), categoria, descrizione, livello, lingua, thumbnail upload
  • Step 2: aggiunta sezioni e lezioni in ordine drag-and-drop con @angular/cdk/drag-drop
  • Step 3: prezzo, scelta free/paid, data pubblicazione
giorno50
Dashboard instructor — editor lezione
  • Form lezione: titolo, ordine, tipo (video/testo/quiz)
  • Upload video: chiama prima POST /uploads/presigned-url, poi fa PUT direttamente su S3 con progress bar
  • Builder quiz: aggiungi/rimuovi domande, imposta la risposta corretta, aggiungi spiegazione
  • Salva la lezione e aggiorna la lista del curriculum in tempo reale
giorno51
Admin panel — utenti e moderazione
  • Sezione Admin (route /admin, solo role=admin)
  • Tabella utenti con @angular/material DataTable: sorting, paginazione lato server, filtro per ruolo
  • Azioni: cambia ruolo utente, sospendi account
  • Lista corsi in attesa di approvazione: mostra dettagli e bottoni Approva/Rifiuta
giorno52
Admin panel — dashboard KPI
  • KPI cards: nuovi iscritti oggi/settimana/mese, revenue totale, corsi pubblicati, tasso completamento
  • Grafico revenue mensile degli ultimi 6 mesi (bar chart)
  • Tabella ultimi pagamenti ricevuti
  • Top 5 corsi per numero di iscrizioni
giorno53
PWA — Service Worker e manifest
  • Studia (1.5h): Service Worker lifecycle (install → activate → fetch), strategie di cache (Cache First, Network First)
  • Aggiungi il pacchetto PWA ufficiale: ng add @angular/pwa — genera manifest, icone e Service Worker
  • Configura ngsw-config.json: asset statici in Cache First, chiamate API in Network First
  • Testa offline: metti offline il browser in DevTools e verifica che la shell dell'app si carichi
giorno54
Responsive design & mobile UX
  • Revisiona ogni pagina su viewport mobile (375px): fix layout brutti, testi troncati, click target troppo piccoli
  • Navbar: hamburger menu su mobile con pannello slide-in
  • Pagina player: su mobile il pannello lezioni si nasconde e appare come bottom sheet
  • Testa su dispositivo reale: installazione PWA funzionante? Scroll e gesture corretti?
giorno55
Lighthouse audit & performance fix
  • Esegui Lighthouse su ogni pagina principale: punta a Performance >85, PWA 100, Accessibility >90
  • Fix comuni: lazy loading immagini (loading="lazy"), compressione bundle, defer script non critici
  • Aggiungi @defer block di Angular 17 per i componenti pesanti (editor, chart)
  • Verifica che il bundle produzione sia <1 MB gzippato
giorno56
Integration test Frontend ↔ Backend
  • Flusso completo studente: registrazione → verifica email → login → catalogo → acquisto (sandbox) → lezione → quiz → certificato
  • Flusso instructor: login → crea corso → aggiunge lezioni → pubblica → verifica visibilità
  • Flusso admin: login → approva corso → gestisce utente → vede KPI
  • Testa su Chrome, Firefox e Safari — fix eventuali incompatibilità CSS
🎯 Milestone (Fine Mese 1): applicazione full-stack funzionante end-to-end. Tutti e 3 i ruoli operativi, PWA installabile.
05
Redis — Caching & Rate Limiting
Giorni 57–66 · 10 giorniImplementazione
// Redis — sub-millisecondo, la cache condivisa tra istanze
Redis è un database in-memory con latenza sub-millisecondo. In questa app lo usi per: caching delle risposte API frequenti (lista corsi, dettaglio corso), rate limiting distribuito (funziona correttamente anche con più istanze del backend), e come broker per BullMQ nella fase successiva.
giorno57
Redis — strutture dati e CLI
  • Studia (2h): 5 strutture dati Redis: String, Hash, List, Set, Sorted Set — con esempio d'uso per ognuna
  • Connettiti al container Redis con redis-cli: prova SET/GET, HSET/HGETALL, EXPIRE/TTL, INCR, ZADD/ZRANGE
  • Naming convention delle chiavi: course:list:page:1, course:detail:{slug}, rate_limit:{ip}
  • Capisci la differenza tra DEL (sincrono) e UNLINK (asincrono) — usa UNLINK in produzione
giorno58
Cache manager in NestJS — configurazione
  • Installa @nestjs/cache-manager cache-manager e il Redis store: @keyv/redis keyv
  • Configura CacheModule.registerAsync() con la connessione Redis nel AppModule
  • Inietta CACHE_MANAGER nel CoursesService
  • Testa che la connessione funzioni con una semplice operazione get/set
giorno59
Caching lista corsi e dettaglio
  • Aggiungi cache manuale in CoursesService.findAll(): controlla Redis prima della query PostgreSQL
  • Se cache miss → query DB → salva in Redis con TTL 15 minuti
  • Aggiungi cache su findBySlug() con TTL 30 minuti
  • Misura il miglioramento: confronta i tempi di risposta con e senza cache
giorno60
Cache invalidation
  • Quando un instructor modifica un corso, cancella le chiavi cache relative: course:detail:{slug}
  • Quando viene pubblicato un nuovo corso, cancella le chiavi della lista: usa SCAN + UNLINK con pattern course:list:*
  • Crea un CacheService centralizzato con metodi invalidateCourse(slug), invalidateCourseList()
  • Verifica che dopo la modifica la risposta API sia aggiornata (non cached)
giorno61
Rate limiting con Redis store
  • Configura @nestjs/throttler con Redis store per il rate limiting distribuito
  • Regole differenziate: endpoint login → 5 tentativi/minuto per IP. API pubblica → 100 req/minuto. API autenticata → 300/minuto
  • Aggiungi gli header di risposta: X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After
  • Testa il lockout: uno script Node.js che fa 10 richieste rapide e verifica il 429
giorno62
Redis per refresh token storage
  • Migra lo storage del refresh token hash da PostgreSQL a Redis (più veloce per lookup frequenti)
  • Chiave Redis: refresh:{userId} → hash. TTL uguale alla durata del refresh token (7 giorni)
  • Al logout: cancella la chiave Redis invece di aggiornare il DB
  • Verifica che il refresh e il logout funzionino ancora correttamente
giorno63
Redis Pub/Sub — notifiche enrollment
  • Configura Redis Pub/Sub in NestJS con ioredis
  • Publisher: quando uno studente si iscrive a un corso, pubblica un evento sul canale enrollment:new
  • Subscriber: ascolta il canale e aggiorna un contatore Redis (course:{id}:enrollments)
  • Usa questo contatore nelle API invece di contare dal DB ogni volta
giorno64
Metriche Redis — monitoraggio
  • Installa RedisInsight (GUI gratuita) per monitorare le chiavi e le performance
  • Aggiungi un endpoint admin GET /admin/cache-stats: numero chiavi, hit rate approssimativo, memoria usata
  • Imposta un maxmemory e una policy di eviction (allkeys-lru) nel container Redis
  • Documenta nel README la strategia di caching adottata
giorno65
Performance benchmark con e senza Redis
  • Usa autocannon (npm): npx autocannon -c 50 -d 10 http://localhost:3000/api/courses
  • Esegui il test senza cache (disabilita temporaneamente), poi con cache — confronta req/sec e latenza P99
  • Esegui un secondo test su GET /courses/:slug — il miglioramento sarà ancora più marcato
  • Salva i risultati nel README con screenshot — ottimo contenuto per il portfolio
giorno66
Redis review & cleanup
  • Revisiona tutte le chiavi Redis: nomenclatura consistente? TTL ragionevoli?
  • Verifica che la cache invalidation copra tutti i casi (update, delete, publish)
  • Assicurati che il rate limiter non blocchi gli utenti legittimi in scenari normali
  • Scrivi test per il CacheService: mock Redis e verifica il comportamento
🎯 Milestone: caching distribuito e rate limiting operativi. L'app regge carichi maggiori senza query ridondanti al DB.
06
BullMQ — Job Queue, Workers & Task Asincroni
Giorni 67–77 · 11 giorniImplementazione
// BullMQ — il Celery del mondo Node.js
BullMQ usa Redis come broker. Con @nestjs/bullmq si integra perfettamente: definisci una coda con @Processor("email") e i worker con @Process(). Supporta retry automatici con exponential backoff, job scheduling cron, priorità e concorrenza. Fondamentale per non bloccare il ciclo HTTP con operazioni lente: email, PDF, notifiche.
giorno67
BullMQ — architettura e primo job
  • Studia (1.5h): architettura Producer-Broker-Consumer, differenza tra job e worker, concetto di coda FIFO, retry policy
  • Installa @nestjs/bullmq bullmq e configura BullModule.forRoot() con la connessione Redis
  • Crea il primo modulo con una coda: BullModule.registerQueue({ name: "email" })
  • Scrivi un job semplice test-job che stampa in console — verifica l'esecuzione
giorno68
Email system — Nodemailer + Mailgun
  • Configura @nestjs-modules/mailer con Nodemailer e il provider Mailgun (free tier: 5000 email/mese)
  • Crea template HTML con Handlebars: welcome.hbs, enrollment-confirm.hbs, certificate-ready.hbs
  • Testa l'invio di una email di test in sandbox Mailgun e verifica il template
giorno69
Job — welcome email & enrollment confirmation
  • Job send-welcome-email: triggered alla registrazione, retry 3 volte con backoff 30s/90s/270s
  • Job send-enrollment-confirmation: triggered all'iscrizione o al pagamento riuscito
  • Configura il Processor con concurrency: 3 — processa 3 email in parallelo
  • Testa registrando un nuovo utente e verificando l'email ricevuta
giorno70
Job — generazione certificati PDF
  • Job generate-certificate: triggered dall'evento enrollment.completed
  • Usa pdfkit per generare il PDF: nome studente, corso, data, UUID di verifica, QR code con qrcode
  • Salva il PDF nella cartella uploads/certificates/ (migrerai su S3 nella Fase 08)
  • Aggiorna il record Certificate nel DB con il path e l'URL di download
giorno71
Job — digest giornaliero studenti
  • Job schedulato cron 0 9 * * *: invia agli studenti con corsi attivi il riassunto giornaliero
  • Contenuto: prossima lezione suggerita, streak di studio (giorni consecutivi), percentuale completamento
  • Configura con BullModule e job repeatble: repeat: { pattern: "0 9 * * *" }
  • Testa triggando manualmente il job dall'endpoint admin
giorno72
Job — report settimanale instructor
  • Job schedulato cron 0 8 * * 1 (ogni lunedì): genera report per ogni instructor con corsi pubblicati
  • Contenuto email: nuovi iscritti della settimana, revenue, lezione più vista, tasso di completamento
  • Aggiungi un endpoint admin POST /admin/jobs/trigger-instructor-report per triggare manualmente
  • Verifica che l'email arrivi con tutti i dati corretti
giorno73
Job — cleanup token e manutenzione
  • Job notturno cron 0 3 * * *: pulizia chiavi Redis scadute e token di verifica email vecchi
  • Job cleanup-old-backups: elimina i PDF certificati generati più di 1 anno fa
  • Aggiungi Bull Board come dashboard web: installa @bull-board/nestjs su route /admin/queues
  • Testa Bull Board: verifica che mostri job completati, falliti e in coda
giorno74
Retry policy & dead letter queue
  • Configura retry dettagliato per ogni tipo di job: email (3 retry), PDF (2 retry), cleanup (1 retry)
  • Aggiungi un handler per onFailed: logga l'errore e invia una notifica all'admin se il job fallisce dopo tutti i retry
  • Simula un fallimento (disconnetti Mailgun) e verifica che il retry avvenga con i backoff corretti
  • Imposta un defaultJobOptions con removeOnComplete: 100 per non accumulare job completati
giorno75
Stress test delle code
  • Script Node.js: crea 100 iscrizioni in parallelo e verifica che tutti i job vengano accodati e processati
  • Monitora Bull Board durante il test: vedi la coda riempirsi e svuotarsi
  • Simula un crash del worker (kill del processo): verifica che i job non persi vengano ripresi al riavvio
  • Verifica che la concorrenza non crei duplicati (due certificati per lo stesso enrollment)
giorno76
Integrazione job con gli eventi NestJS
  • Assicurati che TUTTI gli eventi applicativi triggino il job corretto via EventEmitter2
  • Mappa degli eventi: user.registered → welcome email, enrollment.created → confirmation email, enrollment.completed → generate certificate
  • Aggiungi logging strutturato su ogni job: jobId, jobName, duration, status
  • Verifica il flusso completo: registrazione → iscrizione → completamento → certificato PDF ricevuto via email
giorno77
BullMQ review
  • Revisiona tutti i job: sono idempotenti? (se un job viene eseguito due volte, il risultato è lo stesso?)
  • Verifica che i template email siano corretti: nessun link rotto, nessun dato undefined
  • Aggiungi metriche: endpoint GET /admin/jobs/stats con conteggio job completati/falliti per tipo
  • Documenta nel README tutti i job e le loro schedule
🎯 Milestone: sistema asincrono completo. Email, PDF, report schedulati — tutto fuori dal ciclo HTTP.
07
Pagamenti — Braintree (Visa/Mastercard + PayPal)
Giorni 78–88 · 11 giorniImplementazione
// Braintree — Visa, Mastercard e PayPal con un unico SDK
Perché Braintree e non Visa/Mastercard diretto: non esiste integrazione diretta con i circuiti card per siti web — servono acquirer bancari e certificazione PCI-DSS. Tutti i siti usano un gateway. Braintree (di PayPal) supporta Visa, Mastercard, Amex e PayPal nativo con un unico SDK.

Il flusso di sicurezza: (1) frontend chiede un client token al backend, (2) Braintree Drop-in UI raccoglie i dati carta (non toccano mai il tuo server), (3) Drop-in restituisce un nonce usa-e-getta, (4) il frontend invia il nonce al backend, (5) il backend crea la transazione.
giorno78
Braintree — setup account e SDK
  • Crea account Braintree Sandbox (gratuito). Ottieni le 3 credenziali: merchantId, publicKey, privateKey
  • Studia (2h): flusso Client Token → Drop-in UI → Nonce → Transaction. Differenza tra authorization e settlement
  • Installa braintree Node.js SDK nel backend e configura il gateway nel PaymentsModule
  • Endpoint GET /payments/client-token: genera e restituisce il token per inizializzare il Drop-in
giorno79
Drop-in UI in Angular
  • Installa braintree-web-drop-in nel frontend Angular
  • Componente CheckoutComponent: al mount chiama il backend per il client token, poi inizializza il Drop-in
  • Il Drop-in mostra automaticamente i campi carta E il pulsante PayPal in un unico widget
  • Al click su "Paga", chiama dropin.requestPaymentMethod() per ottenere il nonce
giorno80
Acquisto singolo corso — backend
  • Endpoint POST /payments/checkout: riceve nonce + courseId, verifica che l'utente non sia già iscritto
  • Crea la transazione Braintree: gateway.transaction.sale({ amount, paymentMethodNonce })
  • Se success: crea il record Payment nel DB, triggera il job email di conferma, crea l'Enrollment
  • Gestisci gli errori Braintree: carta rifiutata, CVV errato, fondi insufficienti — errori specifici al frontend
giorno81
Acquisto singolo corso — frontend
  • Alla risposta success dal backend: redirect alla pagina del corso con messaggio di benvenuto
  • Alla risposta error: mostra il messaggio di errore specifico nel componente checkout (non una generica "errore pagamento")
  • Aggiungi loading state durante il pagamento per evitare doppi click
  • Testa con le carte di test Braintree: 4111 1111 1111 1111 (Visa OK), 4000 1111 1111 1115 (rifiutata)
giorno82
PayPal — configurazione nel Drop-in
  • Il Drop-in mostra già PayPal automaticamente — configura l'opzione paypal: { flow: "checkout" }
  • Il flusso è identico a quello carta: l'utente clicca PayPal → popup → ottieni il nonce → stesso endpoint backend
  • Testa il flusso PayPal con account sandbox Braintree (non serve un account PayPal reale)
  • Verifica che il record Payment salvi correttamente il tipo di metodo usato (card vs paypal)
giorno83
Abbonamento mensile — Braintree Subscription
  • Crea un Plan su Braintree Dashboard: prezzo mensile, nome "SynapsisForge Pro"
  • Endpoint POST /payments/subscribe: crea un Customer Braintree con Vault del metodo di pagamento, avvia la Subscription
  • Salva il subscriptionId Braintree nel record User — serve per gestire rinnovi e cancellazioni
  • Aggiorna il campo plan dell'utente (free/pro) in base allo stato della subscription
giorno84
Webhook Braintree
  • Endpoint POST /payments/webhook: riceve notifiche Braintree per rinnovi, fallimenti, cancellazioni
  • Verifica sempre la firma del webhook: gateway.webhookNotification.parse(signature, payload)
  • Gestisci subscription_charged_successfully: rinnova il piano utente, manda email
  • Gestisci subscription_charged_unsuccessfully: downgrade a free, manda email di avviso
giorno85
Customer Portal — gestione abbonamento
  • Endpoint POST /payments/cancel-subscription: cancella la subscription Braintree e imposta il piano a free
  • Endpoint GET /payments/subscription-status: restituisce stato attuale, prossimo rinnovo, metodo di pagamento
  • Componente Angular "Gestione Abbonamento": mostra lo stato, il metodo salvato (last4 carta o email PayPal), bottone cancellazione
  • Testa il flusso completo: subscribe → rinnovo simulato → cancellazione → downgrade a free
giorno86
Idempotenza e test edge case
  • Verifica idempotenza: un webhook doppio non deve creare due Enrollment o due rinnovi
  • Salva il gatewayId di ogni transazione — se arriva un webhook già processato, ignoralo
  • Testa: acquisto di un corso già acquistato → errore 409. Iscrizione con abbonamento Pro → senza pagamento aggiuntivo
  • Revisiona la tabella Payment nel DB: tutti i campi sono popolati correttamente?
giorno87
Storico pagamenti e ricevute
  • Endpoint GET /payments/history: lista paginata dei pagamenti dell'utente corrente
  • Ogni pagamento include: data, importo, metodo, corso acquistato, link ricevuta
  • Job BullMQ generate-receipt-pdf: genera PDF ricevuta con PdfKit e manda via email
  • Componente Angular "Storico Pagamenti" nella dashboard studente
giorno88
Pagamenti review
  • Testa l'intero flusso di pagamento in sandbox con ogni scenario: carta OK, carta rifiutata, PayPal, abbonamento, cancellazione
  • Verifica che nessuna informazione sensibile della carta sia mai loggata (né in console né nel DB)
  • Aggiungi le transazioni di test al seed script per avere dati nella dashboard
  • Scrivi test di integrazione per l'endpoint /payments/checkout
🎯 Milestone: sistema di pagamento completo. Visa, Mastercard e PayPal via Braintree, con abbonamenti e webhook.
08
AWS S3, Deploy & CI/CD GitLab
Giorni 89–100 · 12 giorniCloud / DevOps
// AWS S3 per lo storage, deploy semplice su EC2 o Render, CI/CD GitLab
AWS S3: storage di oggetti scalabile per video, immagini e PDF. Niente CloudFront o ALB complessi per ora — focus sull'essenziale: upload su S3, URL firmati per i video protetti, deploy dell'app su un server semplice.

CI/CD con GitLab: ogni push su main → test automatici → build → deploy. Gratuito su GitLab.com con 2000 minuti/mese di runner condivisi — più che sufficienti per un progetto personale.
giorno89
AWS IAM & S3 setup
  • Crea account AWS (free tier). Mai usare il root account: crea un utente IAM con permessi solo S3
  • Studia IAM (1h): Users, Groups, Roles, Policy. Principio del minimo privilegio.
  • Crea il bucket S3 SynapsisForge-media (video e immagini pubblici) e SynapsisForge-private (certificati PDF)
  • Installa @aws-sdk/client-s3 @aws-sdk/s3-request-presigner nel backend
giorno90
Presigned URL upload — backend
  • Endpoint POST /uploads/presigned-url: riceve fileName, fileType, fileSize — valida estensione (solo mp4/webm/jpg/png/pdf)
  • Genera un presigned PUT URL valido 10 minuti con PutObjectCommand
  • Restituisce: { uploadUrl, key, publicUrl }
  • Aggiorna il LessonsService per salvare la chiave S3 nel documento MongoDB dopo che il frontend conferma l'upload
giorno91
Presigned URL upload — frontend Angular
  • Componente upload video: dopo aver ottenuto il presigned URL dal backend, usa HttpClient.put() direttamente su S3
  • Progress bar: usa reportProgress: true + HttpEventType.UploadProgress
  • Dopo l'upload completato: invia la key S3 al backend per aggiornarla nel DB
  • Testa l'upload di un video di 50MB e verifica che appaia nel bucket S3
giorno92
Signed URL per video protetti
  • I video delle lezioni non devono essere accessibili senza essere iscritti al corso
  • Endpoint GET /lessons/:id/video-url: verifica iscrizione, genera un presigned GET URL valido 2 ore
  • Il frontend usa questo URL nel player HTML5 invece dell'URL diretto S3
  • Testa che un URL diretto S3 restituisca 403 e che il signed URL funzioni e scada
giorno93
Migra certificati PDF su S3
  • Aggiorna il job generate-certificate in BullMQ: dopo la generazione con PdfKit, carica il PDF su S3 nel bucket private
  • Endpoint GET /certificates/:id/download: genera un presigned GET URL valido 1 ora per il download
  • Il componente Angular usa questo URL per il bottone "Scarica Certificato"
  • Migra anche i vecchi certificati nel filesystem locale su S3 con uno script one-off
giorno94
Dockerizza il backend NestJS
  • Studia (1.5h): multi-stage Docker build — stage builder e stage production, perché riduce la dimensione dell'immagine
  • Scrivi il Dockerfile per il backend: node:20-alpine come base, copia solo dist/ e node_modules production
  • Scrivi il Dockerfile per il frontend Angular: stage builder (ng build --configuration production), stage finale nginx:alpine
  • Testa le immagini localmente: docker build -t SynapsisForge-backend . e verifica che l'app si avvii
giorno95
Docker Compose produzione
  • Aggiorna docker-compose.yml aggiungendo i servizi backend e frontend con le immagini costruite
  • Aggiungi Nginx come reverse proxy: /api/* → backend su porta 3000, /* → frontend su porta 80
  • Configura depends_on con health check: backend aspetta che PostgreSQL sia pronto
  • Testa che docker-compose up avvii tutto correttamente e l'app sia raggiungibile su localhost
giorno96
Deploy su server semplice — Render o EC2
  • Opzione A (più semplice): deploy su Render.com — free tier, zero configurazione server
  • Opzione B: lancia un'istanza EC2 t2.micro (free tier 1 anno), connettiti via SSH, installa Docker
  • Copia il docker-compose.yml sul server, configura le variabili d'ambiente, lancia con docker-compose up -d
  • Verifica che l'app sia raggiungibile all'IP pubblico del server
giorno97
HTTPS con Let's Encrypt o Render
  • Se usi Render: HTTPS è automatico. Se usi EC2: installa Certbot e configura SSL su Nginx
  • Punta il tuo dominio (o un sottodominio gratuito di Render) al server
  • Configura redirect automatico HTTP → HTTPS
  • Verifica il certificato SSL con ssllabs.com/ssltest
giorno98
GitLab CI/CD — configurazione base
  • Crea il progetto su GitLab.com (gratuito: 2000 min CI/mese con runner condivisi)
  • Scrivi .gitlab-ci.yml con 3 stage: test (npm test), build (docker build), deploy (ssh + docker-compose pull + up)
  • Configura le variabili CI/CD in GitLab Settings: credenziali SSH, variabili .env di produzione
  • Testa facendo un push e verificando che la pipeline si avvii
giorno99
GitLab CI/CD — test e deploy automatico
  • Aggiungi al stage test: npm run test:cov e fallisci la pipeline se coverage < 50%
  • Configura il deploy automatico solo su push al branch main (non su feature branch)
  • Testa il deploy completo: modifica una riga di codice → push → pipeline → app aggiornata in automatico
  • Aggiungi notifica Slack o email alla fine della pipeline (GitLab lo supporta nativamente)
giorno100
AWS & deploy review
  • Verifica che tutti i file caricati (video, immagini, PDF) usino S3 correttamente
  • Controlla i costi AWS: S3 storage e transfer — devono essere nel free tier per ora
  • Verifica che la pipeline CI/CD funzioni da zero: cancella il container sul server e fai un push
  • Documenta il processo di deploy nel README: prerequisiti, variabili d'ambiente, comandi
🎯 Milestone (Fine Mese 2): app in produzione su un server reale con HTTPS, S3 per lo storage e CI/CD automatico.
09
Testing & Security
Giorni 101–111 · 11 giorniStudio + Implementazione
// Testing non è opzionale — è quello che differenzia un junior da un mid
Un portfolio project convincente ha test. Non perché i recruiter li leggano tutti, ma perché durante un colloquio ti chiederanno "Come hai testato questa funzionalità?" e la risposta "ho aperto Postman" non è sufficiente.
giorno101
Testing strategy — unit vs integration vs e2e
  • Studia (2h): la piramide del testing — unit (molti, veloci, isolati), integration (alcuni, DB in memoria), e2e (pochi, lenti, critici)
  • In NestJS: unit test con jest e mock dei service, integration test con @nestjs/testing + Supertest
  • In Angular: unit test con TestBed per i component, test dei service con HttpClientTestingModule
  • Obiettivo realistico: 60% coverage backend, 40% coverage frontend
giorno102
Backend unit test — service
  • Scrivi unit test per CoursesService: mock del Repository TypeORM con jest.fn()
  • Testa findAll() con filtri, create() con DTO valido, remove() con ID non esistente
  • Scrivi unit test per AuthService: mock di bcrypt e JwtService
  • Testa login con password corretta, login con password sbagliata, refresh con token invalido
giorno103
Backend integration test — endpoint
  • Configura il modulo di test con @nestjs/testing e un DB SQLite in memoria (o PostgreSQL di test)
  • Scrivi integration test per POST /auth/login: 200 con credenziali corrette, 401 con credenziali sbagliate
  • Test per GET /courses: risposta paginata, filtri funzionanti
  • Test per POST /courses senza token → 401, con token student → 403, con token instructor → 201
giorno104
Backend test — guards e interceptors
  • Testa il RolesGuard: verifica che blocchi le richieste con ruolo insufficiente
  • Testa il TransformInterceptor: verifica che la risposta sia wrappata nel formato corretto
  • Testa l'HttpExceptionFilter: verifica che errori diversi producano il formato standard
  • Testa il ParseUuidPipe con UUID validi e invalidi
giorno105
Frontend unit test — component
  • Configura Jest per Angular (o usa il Karma incluso)
  • Test per CourseCardComponent: verifica che le props vengano renderizzate correttamente
  • Test per LoginComponent: submit con form invalido non chiama il service, submit con form valido lo chiama
  • Test per AuthGuard: redirect a /login se non autenticato, lascia passare se autenticato
giorno106
Frontend test — service e interceptor
  • Test per AuthService: verifica che il signal currentUser si aggiorni dopo il login
  • Test per AuthInterceptor: verifica che l'header Authorization venga aggiunto alle richieste
  • Test per il refresh automatico: simula una risposta 401 e verifica che l'interceptor chiami /auth/refresh
  • Test per CoursesService: mock di HttpClient e verifica le URL chiamate
giorno107
Security — OWASP Top 10 review
  • SQL Injection: TypeORM parametrizza automaticamente le query, ma verifica con QueryBuilder custom
  • XSS: Angular sanitizza automaticamente l'interpolazione, ma controlla i casi di innerHTML e bypassSecurityTrust
  • CSRF: le SPA con JWT non hanno il classico problema CSRF, ma i cookie HttpOnly vanno configurati con sameSite: "strict"
  • Sensitive data exposure: verifica che nessun endpoint esponga password hash, token o dati carta
giorno108
Security — audit dipendenze e header
  • Esegui npm audit --audit-level=high su backend e frontend — aggiorna le dipendenze vulnerabili
  • Verifica che Helmet sia configurato correttamente: Content-Security-Policy, X-Frame-Options, HSTS
  • Controlla la configurazione CORS: solo il dominio del frontend è nella whitelist
  • Testa con OWASP ZAP (gratuito) in modalità baseline scan sul dominio di produzione
giorno109
Security — rate limiting e brute force protection
  • Verifica che il rate limiter blocchi 6 tentativi di login consecutivi dallo stesso IP
  • Aggiungi un delay artificiale di 200ms alla risposta di login indipendentemente dal risultato (previene timing attacks)
  • Implementa un semplice account lockout: dopo 10 tentativi falliti, l'account viene sospeso per 30 minuti
  • Testa il lockout e verifica che l'email di notifica all'utente venga inviata
giorno110
Performance — query optimization
  • Attiva il logging TypeORM in sviluppo: logging: true in TypeOrmModule config
  • Identifica le N+1 query e correggile con leftJoinAndSelect o loadRelationCountAndMap
  • Aggiungi indici mancanti sulle colonne usate nei filtri e nelle join: slug, status, createdAt
  • Misura il miglioramento con autocannon prima e dopo le ottimizzazioni
giorno111
Coverage report & fix
  • Genera il coverage report: npm run test:cov sul backend e ng test --code-coverage sul frontend
  • Identifica le aree con coverage <40% e aggiungi i test mancanti per le funzioni più critiche
  • Configura il coverage report come artefatto nella pipeline GitLab — visibile nella CI/CD UI
  • Commit finale: tutti i test in verde, coverage > target
🎯 Milestone: test suite operativa, security hardening fatto, N+1 query eliminate.
10
Polish, Documentazione & Portfolio
Giorni 112–125 · 14 giorniFinalizzazione
// L'ultima fase è la più importante per il portfolio
Il codice che funziona è il minimo. Un portfolio project convincente ha: un README che racconta le scelte architetturali, una demo live raggiungibile in 10 secondi, un demo video di 5 minuti che mostri le funzionalità chiave. Questa fase ti prepara per i colloqui.
giorno112
Seed data professionale
  • Scrivi un seed completo e realistico: 5 categorie, 8 corsi, 3 instructor, 30 studenti, iscrizioni, pagamenti, certificati
  • I corsi devono avere contenuti reali: almeno 3 lezioni con descrizione significativa per ognuno
  • Popola le recensioni con rating misti e commenti realistici
  • Crea 3 account demo con credenziali nel README: [email protected], [email protected], [email protected]
giorno113
UX polish — micro-interactions e feedback
  • Aggiungi skeleton loaders (placeholder animati) durante il caricamento di corsi e dashboard
  • Toast notifications per azioni utente: iscrizione riuscita, pagamento completato, profilo aggiornato
  • Empty states: messaggi e illustrazioni quando le liste sono vuote (es. "Nessun corso acquistato ancora")
  • Animazioni di transizione pagina con Angular Router animations
giorno114
UX polish — error states e edge cases
  • Error boundary globale in Angular: cattura errori non gestiti e mostra una pagina di errore user-friendly
  • Pagina 404 custom con link alla homepage
  • Gestione offline: quando il Service Worker non riesce a fare fetch, mostra un banner "Connessione assente"
  • Verifica che tutti i form abbiano messaggi di errore specifici e visibili (non solo bordi rossi)
giorno115
Accessibilità base
  • Aggiungi aria-label su tutti i bottoni icon-only e le immagini decorative con alt=""
  • Verifica il contrasto colori con WebAIM Contrast Checker — ratio minimo 4.5:1
  • Assicurati che la navigazione da tastiera funzioni: Tab, Enter, Escape sui modal e dropdown
  • Esegui axe DevTools (estensione Chrome) su ogni pagina e correggi i warning critici
giorno116
README — struttura e architettura
  • Sezione Hero: nome progetto, descrizione in 2 righe, link demo live, link repo, badge CI/CD
  • Sezione Screenshots: 4-5 screenshot delle pagine principali (catalogo, player, dashboard, admin)
  • Sezione Stack: tabella con tecnologia, versione e motivazione della scelta
  • Sezione Architecture: diagramma (Excalidraw o draw.io) con tutti i componenti e le connessioni
giorno117
README — setup locale e API docs
  • Sezione "Avvio locale in 5 comandi": clone → .env setup → docker-compose up → npm install → npm run start:dev
  • Sezione "Architecture Decisions": perché NestJS, perché Braintree, perché BullMQ, perché MongoDB + PostgreSQL
  • Link alla documentazione Swagger live su /api/docs
  • Sezione "Account demo": tabella con ruolo, email, password
giorno118
Swagger — completamento documentazione API
  • Verifica che OGNI endpoint sia documentato: @ApiOperation, @ApiResponse, esempi di payload
  • Aggiungi tag Swagger su ogni controller per raggruppare gli endpoint per dominio
  • Configura @ApiBearerAuth() su tutti gli endpoint protetti — il tasto "Authorize" deve funzionare
  • Testa la Swagger UI su produzione e verifica che ogni endpoint sia invocabile dal browser
giorno119
Performance finale — Lighthouse e bundle
  • Lighthouse su homepage, catalogo, player: punta a Performance >85, Accessibility >90, Best Practices 100
  • Analizza il bundle Angular con npx source-map-explorer dist/frontend/**/*.js
  • Fix: rimuovi le dipendenze inutilizzate, usa import specifici invece di import barrel
  • Verifica che il bundle JS principale sia <500 KB gzippato
giorno120
Load test finale su produzione
  • Usa Artillery o autocannon: simula 50 utenti concorrenti per 60 secondi sul server di produzione
  • Monitora: CPU del server, memoria, tempo di risposta medio — l'app non deve andare in crash
  • Verifica che il rate limiter non blocchi il test (aggiusta i limiti per IP se necessario)
  • Documenta i risultati nel README: req/sec, latenza P95
giorno121
Demo video — preparazione e script
  • Scrivi uno script per il video: 5 minuti, 3 sezioni (student experience, instructor tools, admin panel)
  • Prepara l'ambiente: conta demo con dati realistici, browser pulito, finestre ridimensionate correttamente
  • Installa OBS Studio (gratuito) per la registrazione con audio
  • Fai una prova: registra 2 minuti e rivedili — qualità audio OK? Navigazione fluida?
giorno122
Demo video — registrazione
  • Registra la sezione 1 (5 min): flusso studente completo — browse → acquisto con Braintree → lezione → quiz → certificato
  • Registra la sezione 2 (3 min): flusso instructor — crea corso → aggiungi lezioni → pubblica → vedi analytics
  • Registra la sezione 3 (2 min): admin panel — approva corso, gestisci utente, vedi KPI
  • Monta il video con DaVinci Resolve (gratuito) o CapCut — aggiungi titoli e musica di sottofondo
giorno123
LinkedIn & GitHub — pubblicazione
  • Pubblica il repo su GitHub con README completo, link demo e screenshot
  • Crea un post LinkedIn tecnico: descrivi il progetto in 300 parole, evidenzia 3 sfide tecniche risolte, allega il video
  • Aggiungi il progetto al CV con: stack tecnologico, link GitHub, link demo, bullet points con le funzionalità chiave
  • Apri il repo come pubblico e aggiungi i topic GitHub corretti: nestjs, angular, postgresql, redis, braintree
giorno124
Colloquio prep — technical deep dive
  • Prepara risposte dettagliate per le domande tecniche più probabili: "Perché NestJS?", "Come funziona il refresh token?", "Quando usi Redis?", "Come gestisci i pagamenti?"
  • Prepara la spiegazione dell'architettura in 3 minuti con il diagramma dal README
  • Identifica 3 cose che rifaresti diversamente e perché — mostra maturità tecnica
  • Fai una mock interview con te stesso registrandoti in video
giorno125
Retrospettiva & roadmap futura
  • Scrivi una retrospettiva tecnica nel README: cosa hai imparato, cosa è stato più difficile, cosa rifaresti
  • Elenca le funzionalità che avresti aggiunto con più tempo: video transcoding, ricerca Elasticsearch, notifiche push
  • Identifica le tecnologie che vuoi approfondire per il prossimo progetto o il prossimo ruolo
  • Aggiorna il piano giorno per giorno su questo tracker — hai completato il 100%!
🏆 PROGETTO COMPLETATO — SynapsisForge: Angular · NestJS · PostgreSQL · MongoDB · Redis · Braintree · AWS · Docker · GitLab CI — Portfolio completato!