seo-testing-tool 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/dist/auth/GoogleOAuthService.d.ts +31 -0
  4. package/dist/auth/GoogleOAuthService.d.ts.map +1 -0
  5. package/dist/auth/GoogleOAuthService.js +69 -0
  6. package/dist/auth/GoogleOAuthService.js.map +1 -0
  7. package/dist/auth/TokenManager.d.ts +56 -0
  8. package/dist/auth/TokenManager.d.ts.map +1 -0
  9. package/dist/auth/TokenManager.js +190 -0
  10. package/dist/auth/TokenManager.js.map +1 -0
  11. package/dist/cli/commands.d.ts +36 -0
  12. package/dist/cli/commands.d.ts.map +1 -0
  13. package/dist/cli/commands.js +471 -0
  14. package/dist/cli/commands.js.map +1 -0
  15. package/dist/cli/formatters.d.ts +24 -0
  16. package/dist/cli/formatters.d.ts.map +1 -0
  17. package/dist/cli/formatters.js +175 -0
  18. package/dist/cli/formatters.js.map +1 -0
  19. package/dist/cli.d.ts +15 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +62 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/config/AnalysisConfig.d.ts +13 -0
  24. package/dist/config/AnalysisConfig.d.ts.map +1 -0
  25. package/dist/config/AnalysisConfig.js +10 -0
  26. package/dist/config/AnalysisConfig.js.map +1 -0
  27. package/dist/config/env.d.ts +30 -0
  28. package/dist/config/env.d.ts.map +1 -0
  29. package/dist/config/env.js +77 -0
  30. package/dist/config/env.js.map +1 -0
  31. package/dist/database/DatabaseService.d.ts +106 -0
  32. package/dist/database/DatabaseService.d.ts.map +1 -0
  33. package/dist/database/DatabaseService.js +180 -0
  34. package/dist/database/DatabaseService.js.map +1 -0
  35. package/dist/database/TimeSeriesService.d.ts +53 -0
  36. package/dist/database/TimeSeriesService.d.ts.map +1 -0
  37. package/dist/database/TimeSeriesService.js +122 -0
  38. package/dist/database/TimeSeriesService.js.map +1 -0
  39. package/dist/database/db.d.ts +20 -0
  40. package/dist/database/db.d.ts.map +1 -0
  41. package/dist/database/db.js +60 -0
  42. package/dist/database/db.js.map +1 -0
  43. package/dist/database/schema.d.ts +687 -0
  44. package/dist/database/schema.d.ts.map +1 -0
  45. package/dist/database/schema.js +62 -0
  46. package/dist/database/schema.js.map +1 -0
  47. package/dist/demo.d.ts +13 -0
  48. package/dist/demo.d.ts.map +1 -0
  49. package/dist/demo.js +149 -0
  50. package/dist/demo.js.map +1 -0
  51. package/dist/gsc/GSCDataFetcher.d.ts +100 -0
  52. package/dist/gsc/GSCDataFetcher.d.ts.map +1 -0
  53. package/dist/gsc/GSCDataFetcher.js +398 -0
  54. package/dist/gsc/GSCDataFetcher.js.map +1 -0
  55. package/dist/gsc/GSCPermissionService.d.ts +20 -0
  56. package/dist/gsc/GSCPermissionService.d.ts.map +1 -0
  57. package/dist/gsc/GSCPermissionService.js +84 -0
  58. package/dist/gsc/GSCPermissionService.js.map +1 -0
  59. package/dist/index.d.ts +12 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +41 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/notifications/NotificationService.d.ts +64 -0
  64. package/dist/notifications/NotificationService.d.ts.map +1 -0
  65. package/dist/notifications/NotificationService.js +115 -0
  66. package/dist/notifications/NotificationService.js.map +1 -0
  67. package/dist/orchestrator/SEOExperimentOrchestrator.d.ts +69 -0
  68. package/dist/orchestrator/SEOExperimentOrchestrator.d.ts.map +1 -0
  69. package/dist/orchestrator/SEOExperimentOrchestrator.js +199 -0
  70. package/dist/orchestrator/SEOExperimentOrchestrator.js.map +1 -0
  71. package/dist/services/ExportService.d.ts +52 -0
  72. package/dist/services/ExportService.d.ts.map +1 -0
  73. package/dist/services/ExportService.js +238 -0
  74. package/dist/services/ExportService.js.map +1 -0
  75. package/dist/smoke-test.d.ts +10 -0
  76. package/dist/smoke-test.d.ts.map +1 -0
  77. package/dist/smoke-test.js +73 -0
  78. package/dist/smoke-test.js.map +1 -0
  79. package/dist/stats/StatisticalEngine.d.ts +48 -0
  80. package/dist/stats/StatisticalEngine.d.ts.map +1 -0
  81. package/dist/stats/StatisticalEngine.js +205 -0
  82. package/dist/stats/StatisticalEngine.js.map +1 -0
  83. package/dist/stats/TDistribution.d.ts +28 -0
  84. package/dist/stats/TDistribution.d.ts.map +1 -0
  85. package/dist/stats/TDistribution.js +120 -0
  86. package/dist/stats/TDistribution.js.map +1 -0
  87. package/drizzle/0000_hot_whiplash.sql +51 -0
  88. package/drizzle/0001_open_photon.sql +9 -0
  89. package/drizzle/0002_faulty_the_watchers.sql +1 -0
  90. package/drizzle/meta/0000_snapshot.json +360 -0
  91. package/drizzle/meta/0001_snapshot.json +428 -0
  92. package/drizzle/meta/0002_snapshot.json +420 -0
  93. package/drizzle/meta/_journal.json +27 -0
  94. package/package.json +89 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 seo-testing-tool contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # SEO Testing Tool v1.0.0
2
+
3
+ Strumento CLI per esperimenti SEO con analisi statistica (Welch's t-test), integrazione Google Search Console. Database SQLite locale (zero configurazione).
4
+
5
+ ## Visione
6
+
7
+ Rispondere in modo affidabile alla domanda:
8
+ **"Questa modifica SEO ha probabilmente migliorato o peggiorato le performance su Google?"**
9
+
10
+ Il tool non promette certezze, ma supporta decisioni informate attraverso analisi statistica rigorosa.
11
+
12
+ ## Prerequisiti
13
+
14
+ - Node.js >= 18
15
+ - Credenziali OAuth2 Google (per integrazione GSC)
16
+
17
+ > Il database SQLite viene creato automaticamente in `~/.seo-tool/data.db` al primo avvio. Non serve installare nulla.
18
+
19
+ ## Installazione
20
+
21
+ ### Da npm (utenti)
22
+
23
+ ```bash
24
+ npm install -g seo-testing-tool
25
+ seo-tool --help
26
+ ```
27
+
28
+ > Richiede Node.js >= 18. Su alcune piattaforme `better-sqlite3` potrebbe richiedere build tools (python, make).
29
+
30
+ ### Da sorgente (sviluppatori)
31
+
32
+ ```bash
33
+ git clone https://github.com/svilupp0/SEO-Testing-Tool.git
34
+ cd SEO-Testing-Tool
35
+ npm install
36
+ cp .env.example .env
37
+ ```
38
+
39
+ ### Variabili d'ambiente (.env)
40
+
41
+ ```env
42
+ # Opzionale: default ~/.seo-tool/data.db
43
+ # DATABASE_URL=file:/percorso/personalizzato/data.db
44
+ GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
45
+ GOOGLE_CLIENT_SECRET=xxx
46
+ GOOGLE_REDIRECT_URI=http://localhost:3000/auth/callback
47
+ USER_ID=default-user
48
+ ```
49
+
50
+ ### Setup database
51
+
52
+ ```bash
53
+ # Crea le tabelle nel database SQLite locale (via Drizzle)
54
+ npm run db:push
55
+ ```
56
+
57
+ ### Verifica connessione DB
58
+
59
+ ```bash
60
+ npm run smoke-test
61
+ ```
62
+
63
+ ## Quick Start (Demo Mode)
64
+
65
+ Vuoi provare il tool senza configurare Google Search Console? La modalità demo genera due esperimenti con **70 giorni di metriche simulate** (rumore statistico realistico via Box-Muller):
66
+
67
+ - **Esperimento Positivo** — incremento click del +46% nel periodo post (p-value < 0.01)
68
+ - **Esperimento Neutro** — nessuna differenza significativa tra pre e post
69
+
70
+ ```bash
71
+ # Genera i dati demo nel database locale
72
+ npm run demo
73
+ ```
74
+
75
+ Una volta completato, esplora i risultati:
76
+
77
+ ```bash
78
+ # Elenca tutti gli esperimenti
79
+ seo-tool list
80
+
81
+ # Visualizza dettaglio con grafico ASCII e analisi statistica
82
+ seo-tool status <ID>
83
+
84
+ # Esporta le metriche in Excel o CSV
85
+ seo-tool export <ID>
86
+ ```
87
+
88
+ > L'ID viene mostrato al termine di `npm run demo`. Puoi usare anche solo i primi 8 caratteri.
89
+
90
+ ## Comandi CLI
91
+
92
+ | Comando | Descrizione |
93
+ |---------|-------------|
94
+ | `seo-tool add` | Crea un nuovo test SEO (prompt interattivi) |
95
+ | `seo-tool list` | Mostra tabella con tutti i test |
96
+ | `seo-tool status <id>` | Dettaglio test con analisi statistica e grafico ASCII |
97
+ | `seo-tool run` | Sincronizza tutti i test attivi (fetch GSC + analisi) |
98
+ | `seo-tool export <id>` | Esporta metriche in CSV |
99
+ | `seo-tool delete <id>` | Elimina un test (con conferma interattiva) |
100
+
101
+ Tutti i comandi che accettano `<id>` supportano **ID parziali** (es. `seo-tool status abcd`).
102
+
103
+ ### Esempi
104
+
105
+ ```bash
106
+ # Crea un nuovo test
107
+ seo-tool add
108
+
109
+ # Elenca tutti i test
110
+ seo-tool list
111
+
112
+ # Visualizza dettaglio con grafico ASCII
113
+ seo-tool status abcd1234
114
+
115
+ # Sincronizza dati da GSC
116
+ seo-tool run
117
+
118
+ # Esporta in CSV
119
+ seo-tool export abcd1234
120
+
121
+ # Elimina un test (chiede conferma)
122
+ seo-tool delete abcd1234
123
+ ```
124
+
125
+ ## Sviluppo
126
+
127
+ ```bash
128
+ # Esegui comandi senza build (sviluppo)
129
+ npx tsx src/cli.ts <comando>
130
+
131
+ # Lancia test
132
+ npm test
133
+
134
+ # Test con UI
135
+ npm run test:ui
136
+
137
+ # Test con coverage
138
+ npm run test:coverage
139
+
140
+ # Lint
141
+ npm run lint
142
+
143
+ # Format
144
+ npm run format
145
+ ```
146
+
147
+ ## Deploy su Railway (opzionale)
148
+
149
+ Il progetto include configurazione per Railway con cron job notturno e database SQLite embedded.
150
+
151
+ ### Setup Railway
152
+
153
+ 1. Crea un nuovo progetto su [Railway](https://railway.app)
154
+ 2. Collega il repository Git
155
+ 3. Railway rileverà automaticamente `railway.json` e `Dockerfile`
156
+ 4. Configura le variabili d'ambiente nel pannello Railway (credenziali Google, ecc.)
157
+
158
+ ### Cron Job
159
+
160
+ Il cron job (`railway.json`) esegue `npm run cli:run` ogni notte alle **03:00 UTC**:
161
+ - Recupera nuovi dati da Google Search Console
162
+ - Esegue analisi statistica (Welch's t-test)
163
+ - Invia notifiche se i risultati sono statisticamente significativi
164
+
165
+ ### Build manuale Docker
166
+
167
+ ```bash
168
+ # Compila TypeScript
169
+ npm run build
170
+
171
+ # Build immagine
172
+ docker build -t seo-testing-tool .
173
+
174
+ # Run
175
+ docker run --env-file .env seo-testing-tool
176
+ ```
177
+
178
+ ## Architettura
179
+
180
+ ```
181
+ src/
182
+ cli.ts Entry point CLI (commander)
183
+ cli/commands.ts Comandi: add, list, status, run, export, delete
184
+ cli/formatters.ts Colori, tabelle, grafici ASCII
185
+ demo.ts Script per generazione dati demo
186
+ index.ts Entry point cron job
187
+ smoke-test.ts Verifica connessione DB
188
+ stats/
189
+ StatisticalEngine.ts Welch's t-test, outlier detection
190
+ TDistribution.ts Distribuzione t (calcolo p-value)
191
+ config/
192
+ AnalysisConfig.ts Soglie configurabili
193
+ env.ts Config OAuth2
194
+ database/
195
+ db.ts Connessione Drizzle + SQLite
196
+ schema.ts Schema database (Drizzle ORM)
197
+ DatabaseService.ts CRUD, transazioni, aggregati, paginazione
198
+ TimeSeriesService.ts Gap detection, recovery
199
+ orchestrator/
200
+ SEOExperimentOrchestrator.ts GSC -> Analisi -> Risultati
201
+ services/
202
+ ExportService.ts Export Excel/CSV
203
+ drizzle/ Migrazioni e meta-dati SQL
204
+ railway.json Config deploy Railway (cron 03:00 UTC)
205
+ Dockerfile Build container Node.js 18+
206
+ ```
207
+
208
+ ## Limiti dichiarati (by design)
209
+
210
+ - Non funziona su siti piccoli (traffico insufficiente per significativita' statistica)
211
+ - Non e' realtime (richiede tempo per raccogliere dati)
212
+ - Non garantisce risultati (fornisce probabilita', non certezze)
213
+
214
+ Questi limiti aumentano la fiducia nel tool.
215
+
216
+ ## Documentazione
217
+
218
+ - **[PROGRESS.md](./PROGRESS.md)** — Progresso implementazione
219
+ - **[blueprint.md](./blueprint.md)** — Specifica tecnica completa
220
+
221
+ ## Licenza
222
+
223
+ MIT
224
+
225
+ ---
226
+
227
+ **Versione:** 1.0.0 Stable
228
+ **Last Updated:** 2026-02-16
@@ -0,0 +1,31 @@
1
+ /**
2
+ * GoogleOAuthService - Servizio per l'autenticazione OAuth2 con Google
3
+ */
4
+ interface OAuthConfig {
5
+ clientId: string;
6
+ clientSecret: string;
7
+ redirectUri: string;
8
+ }
9
+ interface TokenResponse {
10
+ access_token: string;
11
+ refresh_token: string;
12
+ expires_in: number;
13
+ token_type: string;
14
+ }
15
+ interface UserInfo {
16
+ email: string;
17
+ verified_email: boolean;
18
+ id: string;
19
+ }
20
+ export declare class GoogleOAuthService {
21
+ private config;
22
+ private sessionStates;
23
+ constructor(config: OAuthConfig);
24
+ getAuthorizationUrl(scopes: string[]): string;
25
+ exchangeCodeForTokens(code: string): Promise<TokenResponse>;
26
+ getUserInfo(accessToken: string): Promise<UserInfo>;
27
+ validateState(state: string): void;
28
+ private generateState;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=GoogleOAuthService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleOAuthService.d.ts","sourceRoot":"","sources":["../../src/auth/GoogleOAuthService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,aAAa;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAA0B;gBAEnC,MAAM,EAAE,WAAW;IAI/B,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;IAgBvC,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA2B3D,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAezD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMlC,OAAO,CAAC,aAAa;CAGtB"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * GoogleOAuthService - Servizio per l'autenticazione OAuth2 con Google
3
+ */
4
+ export class GoogleOAuthService {
5
+ config;
6
+ sessionStates = new Set();
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ getAuthorizationUrl(scopes) {
11
+ const state = this.generateState();
12
+ this.sessionStates.add(state);
13
+ const params = new URLSearchParams({
14
+ client_id: this.config.clientId,
15
+ redirect_uri: this.config.redirectUri,
16
+ response_type: 'code',
17
+ scope: scopes.join(' '),
18
+ access_type: 'offline',
19
+ state: state,
20
+ });
21
+ return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
22
+ }
23
+ async exchangeCodeForTokens(code) {
24
+ try {
25
+ const response = await fetch('https://oauth2.googleapis.com/token', {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Content-Type': 'application/x-www-form-urlencoded',
29
+ },
30
+ body: new URLSearchParams({
31
+ code,
32
+ client_id: this.config.clientId,
33
+ client_secret: this.config.clientSecret,
34
+ redirect_uri: this.config.redirectUri,
35
+ grant_type: 'authorization_code',
36
+ }),
37
+ });
38
+ if (!response.ok) {
39
+ throw new Error('Token exchange failed');
40
+ }
41
+ const data = await response.json();
42
+ return data;
43
+ }
44
+ catch (error) {
45
+ throw new Error('Impossibile connettersi a Google. Riprova.');
46
+ }
47
+ }
48
+ async getUserInfo(accessToken) {
49
+ const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
50
+ headers: {
51
+ Authorization: `Bearer ${accessToken}`,
52
+ },
53
+ });
54
+ if (!response.ok) {
55
+ throw new Error('Impossibile connettersi a Google. Riprova.');
56
+ }
57
+ const data = await response.json();
58
+ return data;
59
+ }
60
+ validateState(state) {
61
+ if (!this.sessionStates.has(state)) {
62
+ throw new Error('Impossibile connettersi a Google. Riprova.');
63
+ }
64
+ }
65
+ generateState() {
66
+ return Math.random().toString(36).substring(2) + Date.now().toString(36);
67
+ }
68
+ }
69
+ //# sourceMappingURL=GoogleOAuthService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleOAuthService.js","sourceRoot":"","sources":["../../src/auth/GoogleOAuthService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqBH,MAAM,OAAO,kBAAkB;IACrB,MAAM,CAAc;IACpB,aAAa,GAAgB,IAAI,GAAG,EAAE,CAAC;IAE/C,YAAY,MAAmB;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,mBAAmB,CAAC,MAAgB;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC/B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;YACrC,aAAa,EAAE,MAAM;YACrB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YACvB,WAAW,EAAE,SAAS;YACtB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,OAAO,gDAAgD,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC7E,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,IAAY;QACtC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,qCAAqC,EAAE;gBAClE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,mCAAmC;iBACpD;gBACD,IAAI,EAAE,IAAI,eAAe,CAAC;oBACxB,IAAI;oBACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;oBAC/B,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;oBACvC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;oBACrC,UAAU,EAAE,oBAAoB;iBACjC,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAC3C,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,IAAqB,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,WAAmB;QACnC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,+CAA+C,EAAE;YAC5E,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,IAAgB,CAAC;IAC1B,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;CACF"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * TokenManager - Gestione dei token OAuth2 e refresh automatico
3
+ */
4
+ import { GoogleOAuthService } from './GoogleOAuthService.js';
5
+ interface TokenManagerConfig {
6
+ clientId: string;
7
+ clientSecret: string;
8
+ }
9
+ interface StoredToken {
10
+ access_token: string;
11
+ refresh_token: string;
12
+ expires_in: number;
13
+ expires_at: number;
14
+ token_type: string;
15
+ }
16
+ interface TokenResponse {
17
+ access_token: string;
18
+ refresh_token?: string;
19
+ expires_in: number;
20
+ token_type: string;
21
+ }
22
+ interface TokenStatus {
23
+ isRevoked: boolean;
24
+ revokedAt: Date | null;
25
+ }
26
+ interface ValidationResult {
27
+ isValid: boolean;
28
+ isRevoked: boolean;
29
+ errorMessage: string;
30
+ }
31
+ interface ValidTokenResult {
32
+ success: boolean;
33
+ error: string | null;
34
+ token: string | null;
35
+ }
36
+ export declare class TokenManager {
37
+ private config;
38
+ private currentToken;
39
+ private readonly REFRESH_THRESHOLD;
40
+ private tokens;
41
+ private revokedTokens;
42
+ constructor(config: TokenManagerConfig | GoogleOAuthService);
43
+ isTokenExpired(token: StoredToken): boolean;
44
+ processToken(token: TokenResponse): StoredToken;
45
+ needsRefresh(token: StoredToken): boolean;
46
+ refreshAccessToken(userIdOrToken: string): Promise<TokenResponse>;
47
+ saveTokens(userIdOrToken: string | StoredToken, tokens?: TokenResponse): Promise<void>;
48
+ getValidAccessToken(): Promise<string>;
49
+ getValidAccessToken(userId: string): Promise<ValidTokenResult>;
50
+ validateAccessToken(_accessToken: string): Promise<ValidationResult>;
51
+ getTokenStatus(userId: string): Promise<TokenStatus>;
52
+ markAsRevoked(userId: string): Promise<void>;
53
+ isAccessRevoked(userId: string): Promise<boolean>;
54
+ }
55
+ export {};
56
+ //# sourceMappingURL=TokenManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenManager.d.ts","sourceRoot":"","sources":["../../src/auth/TokenManager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,WAAW;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,aAAa;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IAC5C,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,aAAa,CAAgC;gBAEzC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB;IAQ3D,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAI3C,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW;IAU/C,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;IAKnC,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAsFjE,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBtF,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IACtC,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgD9D,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IASpE,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAQpD,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGxD"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * TokenManager - Gestione dei token OAuth2 e refresh automatico
3
+ */
4
+ export class TokenManager {
5
+ config;
6
+ currentToken = null;
7
+ REFRESH_THRESHOLD = 600000; // 10 minuti in millisecondi
8
+ tokens = new Map();
9
+ revokedTokens = new Map();
10
+ constructor(config) {
11
+ if ('clientId' in config && 'clientSecret' in config) {
12
+ this.config = config;
13
+ }
14
+ else {
15
+ this.config = { clientId: '', clientSecret: '' };
16
+ }
17
+ }
18
+ isTokenExpired(token) {
19
+ return token.expires_at < Date.now();
20
+ }
21
+ processToken(token) {
22
+ return {
23
+ access_token: token.access_token,
24
+ refresh_token: token.refresh_token || '',
25
+ expires_in: token.expires_in,
26
+ expires_at: Date.now() + token.expires_in * 1000,
27
+ token_type: token.token_type,
28
+ };
29
+ }
30
+ needsRefresh(token) {
31
+ const timeUntilExpiry = token.expires_at - Date.now();
32
+ return timeUntilExpiry < this.REFRESH_THRESHOLD;
33
+ }
34
+ async refreshAccessToken(userIdOrToken) {
35
+ // Se è un userId, recupera il token
36
+ let refreshToken;
37
+ let userId = null;
38
+ if (this.tokens.has(userIdOrToken)) {
39
+ userId = userIdOrToken;
40
+ const storedToken = this.tokens.get(userIdOrToken);
41
+ if (!storedToken) {
42
+ throw new Error('No token found');
43
+ }
44
+ // Controlla se è già revocato
45
+ if (this.revokedTokens.has(userId)) {
46
+ throw new Error('Accesso revocato. Riconnetti il tuo account Google.');
47
+ }
48
+ refreshToken = storedToken.refresh_token;
49
+ }
50
+ else {
51
+ refreshToken = userIdOrToken;
52
+ }
53
+ try {
54
+ const response = await fetch('https://oauth2.googleapis.com/token', {
55
+ method: 'POST',
56
+ headers: {
57
+ 'Content-Type': 'application/x-www-form-urlencoded',
58
+ },
59
+ body: new URLSearchParams({
60
+ refresh_token: refreshToken,
61
+ client_id: this.config.clientId,
62
+ client_secret: this.config.clientSecret,
63
+ grant_type: 'refresh_token',
64
+ }),
65
+ });
66
+ if (!response.ok) {
67
+ // Prima controlla errori HTTP specifici
68
+ if (response.status === 500) {
69
+ throw new Error('Errore del server Google');
70
+ }
71
+ // Prova a parsare il JSON solo se non è un errore 500
72
+ let data = {};
73
+ try {
74
+ data = await response.json();
75
+ }
76
+ catch {
77
+ // Se non è JSON valido, continua con i controlli di status
78
+ }
79
+ // Controlla se è un errore di revoca
80
+ if (data.error === 'invalid_grant') {
81
+ if (userId) {
82
+ this.revokedTokens.set(userId, new Date());
83
+ }
84
+ throw new Error('Accesso revocato. Riconnetti il tuo account Google.');
85
+ }
86
+ const isRevoked = response.status === 400 || response.status === 401;
87
+ if (isRevoked) {
88
+ throw new Error('Sessione scaduta. Effettua nuovamente il login.');
89
+ }
90
+ throw new Error('Token refresh failed');
91
+ }
92
+ const data = await response.json();
93
+ return data;
94
+ }
95
+ catch (error) {
96
+ if (error instanceof Error) {
97
+ if (error.message.includes('Accesso revocato')) {
98
+ throw error;
99
+ }
100
+ if (error.message.includes('Sessione scaduta')) {
101
+ throw error;
102
+ }
103
+ if (error.message.includes('Errore del server Google')) {
104
+ throw error;
105
+ }
106
+ if (error.message === 'Network timeout') {
107
+ throw new Error('Errore di rete');
108
+ }
109
+ }
110
+ throw new Error('Impossibile rinnovare la sessione. Riprova.');
111
+ }
112
+ }
113
+ async saveTokens(userIdOrToken, tokens) {
114
+ if (typeof userIdOrToken === 'string' && tokens) {
115
+ // Nuovo formato: saveTokens(userId, tokens)
116
+ const userId = userIdOrToken;
117
+ const storedToken = this.processToken(tokens);
118
+ this.tokens.set(userId, storedToken);
119
+ // Se salviamo nuovi token, rimuoviamo lo stato di revoca
120
+ if (this.revokedTokens.has(userId)) {
121
+ this.revokedTokens.delete(userId);
122
+ }
123
+ }
124
+ else {
125
+ // Vecchio formato: saveTokens(token)
126
+ this.currentToken = userIdOrToken;
127
+ }
128
+ }
129
+ async getValidAccessToken(userId) {
130
+ if (userId) {
131
+ // Controlla se è revocato
132
+ if (this.revokedTokens.has(userId)) {
133
+ return {
134
+ success: false,
135
+ error: 'Accesso revocato. Riconnetti il tuo account Google.',
136
+ token: null,
137
+ };
138
+ }
139
+ const token = this.tokens.get(userId);
140
+ if (!token) {
141
+ return {
142
+ success: false,
143
+ error: 'No token available',
144
+ token: null,
145
+ };
146
+ }
147
+ return {
148
+ success: true,
149
+ error: null,
150
+ token: token.access_token,
151
+ };
152
+ }
153
+ // Vecchio comportamento
154
+ if (!this.currentToken) {
155
+ throw new Error('No token available');
156
+ }
157
+ if (this.isTokenExpired(this.currentToken) || this.needsRefresh(this.currentToken)) {
158
+ const newTokens = await this.refreshAccessToken(this.currentToken.refresh_token);
159
+ const processedToken = this.processToken(newTokens);
160
+ // Mantieni il refresh token originale se Google non ne invia uno nuovo
161
+ if (!newTokens.refresh_token) {
162
+ processedToken.refresh_token = this.currentToken.refresh_token;
163
+ }
164
+ this.currentToken = processedToken;
165
+ }
166
+ return this.currentToken.access_token;
167
+ }
168
+ async validateAccessToken(_accessToken) {
169
+ // Simula validazione - ritorna revocato per token non autorizzati
170
+ return {
171
+ isValid: false,
172
+ isRevoked: true,
173
+ errorMessage: 'Accesso revocato. Riconnetti il tuo account Google.',
174
+ };
175
+ }
176
+ async getTokenStatus(userId) {
177
+ const isRevoked = this.revokedTokens.has(userId);
178
+ return {
179
+ isRevoked,
180
+ revokedAt: isRevoked ? this.revokedTokens.get(userId) : null,
181
+ };
182
+ }
183
+ async markAsRevoked(userId) {
184
+ this.revokedTokens.set(userId, new Date());
185
+ }
186
+ async isAccessRevoked(userId) {
187
+ return this.revokedTokens.has(userId);
188
+ }
189
+ }
190
+ //# sourceMappingURL=TokenManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenManager.js","sourceRoot":"","sources":["../../src/auth/TokenManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * CLI Commands
3
+ *
4
+ * Implementazione dei 7 comandi: login, add, list, status, run, export, delete
5
+ */
6
+ /**
7
+ * login — Collega l'account Google per accedere a Google Search Console
8
+ */
9
+ export declare function loginCommand(): Promise<void>;
10
+ /**
11
+ * add — Crea un nuovo test SEO con prompt interattivi
12
+ */
13
+ export declare function addCommand(): Promise<void>;
14
+ /**
15
+ * list — Mostra tabella con tutti i test
16
+ */
17
+ export declare function listCommand(): Promise<void>;
18
+ /**
19
+ * status <id> — Dettaglio di un singolo test con analisi e grafico
20
+ */
21
+ export declare function statusCommand(id: string): Promise<void>;
22
+ /**
23
+ * run — Sincronizza tutti i test attivi via orchestratore
24
+ */
25
+ export declare function runCommand(): Promise<void>;
26
+ /**
27
+ * export <id> — Esporta dati test su file Excel o CSV (con scelta interattiva)
28
+ */
29
+ export declare function exportCommand(id: string, options?: {
30
+ format?: string;
31
+ }): Promise<void>;
32
+ /**
33
+ * delete <id> — Elimina un test (con conferma interattiva) e registra nell'Audit Log
34
+ */
35
+ export declare function deleteCommand(id: string): Promise<void>;
36
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAsEH;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA4FlD;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CA8DhD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBjD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D7D;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAoEhD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E5F;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmD7D"}