@kaikybrofc/omnizap-system 2.1.8

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 (166) hide show
  1. package/.env.example +534 -0
  2. package/LICENSE +21 -0
  3. package/README.md +431 -0
  4. package/RELEASE-v2.1.2.md +83 -0
  5. package/app/config/adminIdentity.js +87 -0
  6. package/app/config/baileysConfig.js +693 -0
  7. package/app/config/groupUtils.js +388 -0
  8. package/app/connection/socketController.js +992 -0
  9. package/app/controllers/messageController.js +354 -0
  10. package/app/modules/adminModule/groupCommandHandlers.js +1294 -0
  11. package/app/modules/adminModule/groupEventHandlers.js +355 -0
  12. package/app/modules/aiModule/catCommand.js +1006 -0
  13. package/app/modules/broadcastModule/noticeCommand.js +416 -0
  14. package/app/modules/gameModule/diceCommand.js +67 -0
  15. package/app/modules/menuModule/common.js +311 -0
  16. package/app/modules/menuModule/menus.js +59 -0
  17. package/app/modules/playModule/playCommand.js +1615 -0
  18. package/app/modules/quoteModule/quoteCommand.js +851 -0
  19. package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +786 -0
  20. package/app/modules/rpgPokemonModule/rpgBattleService.js +2082 -0
  21. package/app/modules/rpgPokemonModule/rpgBattleService.test.js +760 -0
  22. package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
  23. package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +172 -0
  24. package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
  25. package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
  26. package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
  27. package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
  28. package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1859 -0
  29. package/app/modules/rpgPokemonModule/rpgPokemonService.js +6738 -0
  30. package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
  31. package/app/modules/statsModule/globalRankingCommand.js +65 -0
  32. package/app/modules/statsModule/noMessageCommand.js +288 -0
  33. package/app/modules/statsModule/rankingCommand.js +60 -0
  34. package/app/modules/statsModule/rankingCommon.js +889 -0
  35. package/app/modules/stickerModule/addStickerMetadata.js +239 -0
  36. package/app/modules/stickerModule/convertToWebp.js +390 -0
  37. package/app/modules/stickerModule/stickerCommand.js +454 -0
  38. package/app/modules/stickerModule/stickerConvertCommand.js +156 -0
  39. package/app/modules/stickerModule/stickerTextCommand.js +657 -0
  40. package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
  41. package/app/modules/stickerPackModule/autoPackCollectorService.js +284 -0
  42. package/app/modules/stickerPackModule/semanticReclassificationEngine.js +466 -0
  43. package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +88 -0
  44. package/app/modules/stickerPackModule/semanticThemeClusterService.js +571 -0
  45. package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +449 -0
  46. package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
  47. package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +180 -0
  48. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +4078 -0
  49. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +598 -0
  50. package/app/modules/stickerPackModule/stickerClassificationService.js +588 -0
  51. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +102 -0
  52. package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +7506 -0
  53. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1095 -0
  54. package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +108 -0
  55. package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
  56. package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +110 -0
  57. package/app/modules/stickerPackModule/stickerPackItemRepository.js +440 -0
  58. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +337 -0
  59. package/app/modules/stickerPackModule/stickerPackMessageService.js +296 -0
  60. package/app/modules/stickerPackModule/stickerPackRepository.js +442 -0
  61. package/app/modules/stickerPackModule/stickerPackService.js +788 -0
  62. package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +51 -0
  63. package/app/modules/stickerPackModule/stickerPackUtils.js +97 -0
  64. package/app/modules/stickerPackModule/stickerStorageService.js +507 -0
  65. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +233 -0
  66. package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +205 -0
  67. package/app/modules/systemMetricsModule/pingCommand.js +421 -0
  68. package/app/modules/tiktokModule/tiktokCommand.js +798 -0
  69. package/app/modules/userModule/userCommand.js +1217 -0
  70. package/app/modules/waifuPicsModule/waifuPicsCommand.js +177 -0
  71. package/app/observability/metrics.js +734 -0
  72. package/app/services/captchaService.js +492 -0
  73. package/app/services/dbWriteQueue.js +572 -0
  74. package/app/services/groupMetadataService.js +279 -0
  75. package/app/services/lidMapService.js +663 -0
  76. package/app/services/messagePersistenceService.js +56 -0
  77. package/app/services/newsBroadcastService.js +351 -0
  78. package/app/services/pokeApiService.js +398 -0
  79. package/app/services/queueUtils.js +57 -0
  80. package/app/services/socketState.js +7 -0
  81. package/app/store/aiPromptStore.js +38 -0
  82. package/app/store/groupConfigStore.js +58 -0
  83. package/app/store/premiumUserStore.js +36 -0
  84. package/app/utils/antiLink/antiLinkModule.js +804 -0
  85. package/app/utils/http/getImageBufferModule.js +18 -0
  86. package/app/utils/json/jsonSanitizer.js +113 -0
  87. package/app/utils/json/jsonSanitizer.test.js +40 -0
  88. package/app/utils/logger/loggerModule.js +262 -0
  89. package/app/utils/systemMetrics/systemMetricsModule.js +91 -0
  90. package/database/index.js +2052 -0
  91. package/database/init.js +516 -0
  92. package/database/migrations/20260203_0001_sticker_packs.sql +54 -0
  93. package/database/migrations/20260210_0003_rpg_pokemon.sql +58 -0
  94. package/database/migrations/20260210_0004_rpg_shiny_biome.sql +9 -0
  95. package/database/migrations/20260210_0005_rpg_missions.sql +14 -0
  96. package/database/migrations/20260210_0006_rpg_world_pokedex_traits.sql +27 -0
  97. package/database/migrations/20260210_0007_rpg_raid_pvp.sql +56 -0
  98. package/database/migrations/20260210_0008_rpg_social_system.sql +195 -0
  99. package/database/migrations/20260211_0009_rpg_social_xp.sql +36 -0
  100. package/database/migrations/20260222_0010_remove_message_xp.sql +2 -0
  101. package/database/migrations/20260226_0011_sticker_asset_classification.sql +17 -0
  102. package/database/migrations/20260226_0012_sticker_pack_engagement.sql +16 -0
  103. package/database/migrations/20260226_0013_sticker_marketplace_intelligence.sql +19 -0
  104. package/database/migrations/20260226_0014_sticker_pack_publish_flow.sql +30 -0
  105. package/database/migrations/20260226_0014_sticker_worker_queues.sql +42 -0
  106. package/database/migrations/20260226_0015_sticker_auto_pack_curation_integrity.sql +18 -0
  107. package/database/migrations/20260226_0016_sticker_web_google_auth_persistence.sql +34 -0
  108. package/database/migrations/20260226_0017_sticker_web_admin_ban.sql +22 -0
  109. package/database/migrations/20260226_0018_sticker_web_admin_moderator.sql +18 -0
  110. package/database/migrations/20260227_0019_sticker_classification_v2_signals.sql +12 -0
  111. package/database/migrations/20260227_0020_semantic_theme_clusters.sql +35 -0
  112. package/docker-compose.yml +103 -0
  113. package/ecosystem.prod.config.cjs +35 -0
  114. package/eslint.config.js +61 -0
  115. package/index.js +437 -0
  116. package/ml/clip_classifier/Dockerfile +16 -0
  117. package/ml/clip_classifier/README.md +120 -0
  118. package/ml/clip_classifier/adaptive_scoring.py +40 -0
  119. package/ml/clip_classifier/classifier.py +654 -0
  120. package/ml/clip_classifier/embedding_store.py +481 -0
  121. package/ml/clip_classifier/env_loader.py +15 -0
  122. package/ml/clip_classifier/llm_label_expander.py +144 -0
  123. package/ml/clip_classifier/main.py +213 -0
  124. package/ml/clip_classifier/requirements.txt +10 -0
  125. package/ml/clip_classifier/similarity_engine.py +74 -0
  126. package/observability/alert-rules.yml +60 -0
  127. package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
  128. package/observability/grafana/dashboards/omnizap-overview.json +170 -0
  129. package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
  130. package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
  131. package/observability/loki-config.yml +38 -0
  132. package/observability/mysql-exporter.cnf +5 -0
  133. package/observability/mysql-setup.sql +46 -0
  134. package/observability/prometheus.yml +32 -0
  135. package/observability/promtail-config.yml +84 -0
  136. package/package.json +109 -0
  137. package/public/api-docs/index.html +144 -0
  138. package/public/css/github-project-panel.css +297 -0
  139. package/public/css/stickers-admin.css +1272 -0
  140. package/public/css/styles.css +671 -0
  141. package/public/index.html +1311 -0
  142. package/public/js/apps/apiDocsApp.js +310 -0
  143. package/public/js/apps/createPackApp.js +2069 -0
  144. package/public/js/apps/homeApp.js +396 -0
  145. package/public/js/apps/stickersAdminApp.js +1744 -0
  146. package/public/js/apps/stickersApp.js +4830 -0
  147. package/public/js/catalog.js +1019 -0
  148. package/public/js/github-panel/components/CommitList.js +34 -0
  149. package/public/js/github-panel/components/ErrorState.js +16 -0
  150. package/public/js/github-panel/components/GithubProjectPanel.js +106 -0
  151. package/public/js/github-panel/components/ReleaseList.js +38 -0
  152. package/public/js/github-panel/components/SkeletonPanel.js +22 -0
  153. package/public/js/github-panel/components/StatCard.js +15 -0
  154. package/public/js/github-panel/index.js +15 -0
  155. package/public/js/github-panel/useGithubRepoData.js +154 -0
  156. package/public/js/github-panel/vendor/react.js +11 -0
  157. package/public/js/runtime/react-runtime.js +19 -0
  158. package/public/licenca/index.html +106 -0
  159. package/public/stickers/admin/index.html +23 -0
  160. package/public/stickers/create/index.html +47 -0
  161. package/public/stickers/index.html +48 -0
  162. package/public/termos-de-uso/index.html +125 -0
  163. package/scripts/cache-bust.mjs +107 -0
  164. package/scripts/deploy.sh +458 -0
  165. package/scripts/github-deploy-notify.mjs +174 -0
  166. package/scripts/release.sh +129 -0
@@ -0,0 +1,125 @@
1
+ <!doctype html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Termos de Uso | OmniZap System</title>
7
+ <meta name="description" content="Termos de Uso da plataforma OmniZap System." />
8
+ <meta name="robots" content="index, follow" />
9
+ <link rel="canonical" href="https://omnizap.shop/termos-de-uso/" />
10
+ <link rel="icon" type="image/jpeg" href="https://iili.io/FC3FABe.jpg" />
11
+ <link rel="apple-touch-icon" href="https://iili.io/FC3FABe.jpg" />
12
+ <style>
13
+ :root {
14
+ --bg: #0b1020;
15
+ --bg-2: #121a2f;
16
+ --line: #2a3b60;
17
+ --text: #e6edf7;
18
+ --muted: #9fb0cc;
19
+ --card: #141e36cc;
20
+ }
21
+ * { box-sizing: border-box; }
22
+ body {
23
+ margin: 0;
24
+ font-family: "Manrope", system-ui, -apple-system, sans-serif;
25
+ color: var(--text);
26
+ background:
27
+ radial-gradient(58rem 24rem at -8% -12%, #22c55e26, transparent 60%),
28
+ radial-gradient(58rem 24rem at 112% -10%, #38bdf822, transparent 58%),
29
+ linear-gradient(165deg, var(--bg), var(--bg-2));
30
+ }
31
+ .wrap { width: min(960px, 92vw); margin: 0 auto; padding: 24px 0 42px; }
32
+ .top {
33
+ display: flex;
34
+ gap: 10px;
35
+ flex-wrap: wrap;
36
+ margin-bottom: 16px;
37
+ }
38
+ .top a {
39
+ color: var(--text);
40
+ text-decoration: none;
41
+ border: 1px solid var(--line);
42
+ border-radius: 10px;
43
+ padding: 8px 12px;
44
+ background: #111a2d;
45
+ }
46
+ .card {
47
+ border: 1px solid var(--line);
48
+ border-radius: 14px;
49
+ background: var(--card);
50
+ padding: 18px;
51
+ margin-bottom: 12px;
52
+ }
53
+ h1, h2 { margin: 0 0 10px; }
54
+ h1 { font-size: clamp(28px, 4.2vw, 38px); }
55
+ h2 { font-size: 20px; margin-top: 8px; }
56
+ p, li { color: var(--muted); line-height: 1.6; }
57
+ a { color: #8ec5ff; }
58
+ ul { margin: 0; padding-left: 18px; }
59
+ .updated { color: #bcd1ef; font-size: 14px; }
60
+ </style>
61
+ </head>
62
+ <body>
63
+ <main class="wrap">
64
+ <nav class="top" aria-label="Navegação">
65
+ <a href="/">Início</a>
66
+ <a href="/stickers/">Stickers</a>
67
+ <a href="/api-docs/">API</a>
68
+ <a href="/licenca/">Licença</a>
69
+ </nav>
70
+
71
+ <section class="card">
72
+ <h1>Termos de Uso</h1>
73
+ <p class="updated">Última atualização: 26/02/2026</p>
74
+ <p>Ao acessar e utilizar o OmniZap System, você concorda com estes termos.</p>
75
+ </section>
76
+
77
+ <section class="card">
78
+ <h2>1. Uso da plataforma</h2>
79
+ <ul>
80
+ <li>Você pode usar o sistema para automação e integração com WhatsApp conforme a legislação aplicável.</li>
81
+ <li>É proibido uso para spam, fraude, abuso, engenharia social ou atividades ilícitas.</li>
82
+ <li>Você é responsável pelo conteúdo e pelos comandos enviados por sua operação.</li>
83
+ <li>Ao adicionar o bot em grupos, você reconhece que as mensagens e interações do grupo podem ser processadas para funcionamento dos comandos e recursos do sistema.</li>
84
+ </ul>
85
+ </section>
86
+
87
+ <section class="card">
88
+ <h2>2. API e disponibilidade</h2>
89
+ <ul>
90
+ <li>A API pública pode sofrer limites, manutenção e mudanças de versão sem aviso prévio.</li>
91
+ <li>Não há garantia de disponibilidade contínua ou desempenho mínimo específico.</li>
92
+ </ul>
93
+ </section>
94
+
95
+ <section class="card">
96
+ <h2>3. Conteúdo e stickers</h2>
97
+ <ul>
98
+ <li>Você deve possuir direitos sobre os arquivos enviados e compartilhados.</li>
99
+ <li>Conteúdos que violem direitos autorais ou políticas aplicáveis podem ser removidos.</li>
100
+ </ul>
101
+ </section>
102
+
103
+ <section class="card">
104
+ <h2>4. Dados e privacidade</h2>
105
+ <ul>
106
+ <li>Para operar comandos, estatísticas e organização de packs, o sistema pode coletar e processar dados técnicos e operacionais (como identificadores, mensagens usadas em comandos, metadados de mídia e registros de uso).</li>
107
+ <li>Esses dados são utilizados para funcionamento, segurança, manutenção e melhoria do serviço.</li>
108
+ <li>Ao usar o bot, você concorda com esse tratamento de informações para as finalidades acima.</li>
109
+ </ul>
110
+ <p>Se você deseja solicitar remoção de dados, entre em contato com o administrador:</p>
111
+ <p><a href="https://wa.me/559591122954?text=Ol%C3%A1%2C%20gostaria%20de%20solicitar%20a%20remo%C3%A7%C3%A3o%20dos%20meus%20dados%20do%20OmniZap%20System." target="_blank" rel="noreferrer noopener">Falar com o administrador no WhatsApp</a></p>
112
+ </section>
113
+
114
+ <section class="card">
115
+ <h2>5. Limitação de responsabilidade</h2>
116
+ <p>O OmniZap System é fornecido como está. Não nos responsabilizamos por perdas indiretas, interrupções, bloqueios de conta, danos operacionais ou uso inadequado do sistema por terceiros.</p>
117
+ </section>
118
+
119
+ <section class="card">
120
+ <h2>6. Alterações destes termos</h2>
121
+ <p>Podemos atualizar este documento periodicamente. O uso contínuo da plataforma após alterações representa concordância com os termos vigentes.</p>
122
+ </section>
123
+ </main>
124
+ </body>
125
+ </html>
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs/promises';
4
+ import path from 'node:path';
5
+
6
+ const usage = () => {
7
+ console.error('Uso: node scripts/cache-bust.mjs --dir <diretorio> --version <build_id>');
8
+ };
9
+
10
+ const parseArgs = (argv) => {
11
+ const options = {
12
+ dir: '',
13
+ version: '',
14
+ };
15
+
16
+ for (let index = 0; index < argv.length; index += 1) {
17
+ const token = argv[index];
18
+ if (token === '--dir') {
19
+ options.dir = String(argv[index + 1] || '');
20
+ index += 1;
21
+ continue;
22
+ }
23
+ if (token === '--version') {
24
+ options.version = String(argv[index + 1] || '');
25
+ index += 1;
26
+ continue;
27
+ }
28
+ }
29
+
30
+ return options;
31
+ };
32
+
33
+ const listHtmlFiles = async (rootDir) => {
34
+ const output = [];
35
+ const stack = [rootDir];
36
+
37
+ while (stack.length > 0) {
38
+ const current = stack.pop();
39
+ const entries = await fs.readdir(current, { withFileTypes: true });
40
+
41
+ for (const entry of entries) {
42
+ const absolutePath = path.join(current, entry.name);
43
+ if (entry.isDirectory()) {
44
+ stack.push(absolutePath);
45
+ continue;
46
+ }
47
+ if (entry.isFile() && absolutePath.toLowerCase().endsWith('.html')) {
48
+ output.push(absolutePath);
49
+ }
50
+ }
51
+ }
52
+
53
+ return output;
54
+ };
55
+
56
+ const withVersion = (assetPath, version) => {
57
+ const [withoutHash, hash = ''] = assetPath.split('#', 2);
58
+ const [pathname, query = ''] = withoutHash.split('?', 2);
59
+ const params = new URLSearchParams(query);
60
+ params.set('v', version);
61
+ const queryString = params.toString();
62
+ return `${pathname}?${queryString}${hash ? `#${hash}` : ''}`;
63
+ };
64
+
65
+ const applyCacheBustToHtml = (html, version) => {
66
+ const pattern = /((?:src|href)=["'])(\/(?:js|css)\/[^"']+)(["'])/gi;
67
+ let referencesUpdated = 0;
68
+
69
+ const output = html.replace(pattern, (fullMatch, prefix, assetPath, suffix) => {
70
+ const nextPath = withVersion(assetPath, version);
71
+ if (nextPath !== assetPath) referencesUpdated += 1;
72
+ return `${prefix}${nextPath}${suffix}`;
73
+ });
74
+
75
+ return { output, referencesUpdated };
76
+ };
77
+
78
+ const main = async () => {
79
+ const options = parseArgs(process.argv.slice(2));
80
+
81
+ if (!options.dir || !options.version) {
82
+ usage();
83
+ process.exit(1);
84
+ }
85
+
86
+ const targetDir = path.resolve(options.dir);
87
+ const htmlFiles = await listHtmlFiles(targetDir);
88
+ let filesUpdated = 0;
89
+ let referencesUpdated = 0;
90
+
91
+ for (const filePath of htmlFiles) {
92
+ const current = await fs.readFile(filePath, 'utf8');
93
+ const { output, referencesUpdated: fileRefs } = applyCacheBustToHtml(current, options.version);
94
+ if (output !== current) {
95
+ await fs.writeFile(filePath, output, 'utf8');
96
+ filesUpdated += 1;
97
+ referencesUpdated += fileRefs;
98
+ }
99
+ }
100
+
101
+ console.log(`[cache-bust] version=${options.version} files=${filesUpdated} refs=${referencesUpdated}`);
102
+ };
103
+
104
+ main().catch((error) => {
105
+ console.error(`[cache-bust] erro: ${error?.message || error}`);
106
+ process.exit(1);
107
+ });
@@ -0,0 +1,458 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ SOURCE_DIR="${DEPLOY_SOURCE_DIR:-$PROJECT_ROOT/public}"
6
+ DEPLOY_DIR="${DEPLOY_TARGET_DIR:-/var/www/omnizap}"
7
+ BACKUP_ENABLED="${DEPLOY_CREATE_BACKUP:-1}"
8
+ BACKUP_DIR="${DEPLOY_BACKUP_DIR:-$DEPLOY_DIR/.backup}"
9
+ NGINX_SERVICE="${DEPLOY_NGINX_SERVICE:-nginx}"
10
+ RESTART_PM2="${DEPLOY_RESTART_PM2:-1}"
11
+ PM2_APP_NAME="${DEPLOY_PM2_APP_NAME:-omnizap-system-production}"
12
+ BUILD_ID="${DEPLOY_BUILD_ID:-$(date -u +%Y%m%d%H%M%S)}"
13
+ VERIFY_URL="${DEPLOY_VERIFY_URL:-https://omnizap.shop/}"
14
+ DRY_RUN="${DEPLOY_DRY_RUN:-0}"
15
+ GITHUB_NOTIFY="${DEPLOY_GITHUB_NOTIFY:-1}"
16
+ GITHUB_ENVIRONMENT="${DEPLOY_GITHUB_ENVIRONMENT:-production}"
17
+ GITHUB_DEPLOYMENT_ID=""
18
+ PACKAGE_STEP="${DEPLOY_PACKAGE_STEP:-0}"
19
+ PACKAGE_INSTALL="${DEPLOY_PACKAGE_INSTALL:-1}"
20
+ PACKAGE_TEST="${DEPLOY_PACKAGE_TEST:-0}"
21
+ PACKAGE_PACK="${DEPLOY_PACKAGE_PACK:-0}"
22
+ PACKAGE_ARTIFACTS_DIR="${DEPLOY_PACKAGE_ARTIFACTS_DIR:-$PROJECT_ROOT/.artifacts}"
23
+ PACKAGE_PUBLISH="${DEPLOY_PACKAGE_PUBLISH:-0}"
24
+ PACKAGE_PUBLISH_SKIP_IF_EXISTS="${DEPLOY_PACKAGE_PUBLISH_SKIP_IF_EXISTS:-1}"
25
+ PACKAGE_REGISTRY="${DEPLOY_PACKAGE_REGISTRY:-https://npm.pkg.github.com}"
26
+ PACKAGE_TAG="${DEPLOY_PACKAGE_TAG:-latest}"
27
+ PACKAGE_TOKEN="${DEPLOY_PACKAGE_TOKEN:-}"
28
+ PACKAGE_OTP="${DEPLOY_PACKAGE_OTP:-}"
29
+ PACKAGE_PUBLISH_SECONDARY="${DEPLOY_PACKAGE_PUBLISH_SECONDARY:-0}"
30
+ PACKAGE_SECONDARY_REGISTRY="${DEPLOY_PACKAGE_SECONDARY_REGISTRY:-https://registry.npmjs.org}"
31
+ PACKAGE_SECONDARY_TAG="${DEPLOY_PACKAGE_SECONDARY_TAG:-latest}"
32
+ PACKAGE_SECONDARY_TOKEN="${DEPLOY_PACKAGE_SECONDARY_TOKEN:-}"
33
+ PACKAGE_SECONDARY_OTP="${DEPLOY_PACKAGE_SECONDARY_OTP:-}"
34
+ PACKAGE_SECONDARY_ACCESS="${DEPLOY_PACKAGE_SECONDARY_ACCESS:-public}"
35
+ PACKAGE_SECONDARY_PUBLISH_SKIP_IF_EXISTS="${DEPLOY_PACKAGE_SECONDARY_PUBLISH_SKIP_IF_EXISTS:-$PACKAGE_PUBLISH_SKIP_IF_EXISTS}"
36
+ PACKAGE_SECONDARY_TOKEN_KEYS="${DEPLOY_PACKAGE_SECONDARY_TOKEN_KEYS:-}"
37
+ NPMRC_TMP_FILES=()
38
+
39
+ log() {
40
+ printf '[deploy] %s\n' "$*"
41
+ }
42
+
43
+ resolve_github_repo() {
44
+ local explicit_repo="${DEPLOY_GITHUB_REPO:-${GITHUB_REPOSITORY:-}}"
45
+ if [ -n "$explicit_repo" ] && printf '%s' "$explicit_repo" | grep -q '/'; then
46
+ printf '%s' "$explicit_repo"
47
+ return 0
48
+ fi
49
+
50
+ local remote_url=""
51
+ remote_url="$(cd "$PROJECT_ROOT" && git config --get remote.origin.url 2>/dev/null || true)"
52
+ if [ -z "$remote_url" ]; then
53
+ return 0
54
+ fi
55
+
56
+ local repo=""
57
+ repo="$(printf '%s' "$remote_url" | sed -nE 's#.*github\.com[:/]([^/]+/[^/.]+)(\.git)?$#\1#p' | head -n 1)"
58
+ if [ -n "$repo" ]; then
59
+ printf '%s' "$repo"
60
+ fi
61
+ }
62
+
63
+ resolve_github_owner() {
64
+ local repo=""
65
+ repo="$(resolve_github_repo)"
66
+ if [ -z "$repo" ]; then
67
+ return 0
68
+ fi
69
+ printf '%s' "$repo" | cut -d'/' -f1
70
+ }
71
+
72
+ require_cmd() {
73
+ if ! command -v "$1" >/dev/null 2>&1; then
74
+ printf '[deploy] comando ausente: %s\n' "$1" >&2
75
+ exit 1
76
+ fi
77
+ }
78
+
79
+ resolve_token_from_dotenv() {
80
+ local token_keys="$1"
81
+ if [ -z "$token_keys" ]; then
82
+ return 0
83
+ fi
84
+
85
+ (
86
+ cd "$PROJECT_ROOT" && TOKEN_KEYS="$token_keys" node --input-type=module -e "
87
+ import dotenv from 'dotenv';
88
+ dotenv.config({ path: '.env' });
89
+ const keys = String(process.env.TOKEN_KEYS || '')
90
+ .split(',')
91
+ .map((item) => item.trim())
92
+ .filter(Boolean);
93
+ for (const key of keys) {
94
+ const value = process.env[key];
95
+ if (value && String(value).trim()) {
96
+ process.stdout.write(String(value).trim());
97
+ process.exit(0);
98
+ }
99
+ }
100
+ " 2>/dev/null || true
101
+ )
102
+ }
103
+
104
+ default_token_keys_for_registry() {
105
+ local registry="$1"
106
+ if printf '%s' "$registry" | grep -q 'npm.pkg.github.com'; then
107
+ printf 'DEPLOY_PACKAGE_TOKEN,DEPLOY_GITHUB_TOKEN,GITHUB_TOKEN,GH_TOKEN,NPM_TOKEN,NODE_AUTH_TOKEN'
108
+ else
109
+ printf 'DEPLOY_PACKAGE_SECONDARY_TOKEN,NPM_TOKEN,NODE_AUTH_TOKEN'
110
+ fi
111
+ }
112
+
113
+ create_npmrc_for_registry() {
114
+ local registry="$1"
115
+ local token="$2"
116
+ local scope_owner="$3"
117
+ local registry_host=""
118
+ registry_host="$(printf '%s' "$registry" | sed -E 's#^https?://##; s#/*$##')"
119
+
120
+ local npmrc_tmp=""
121
+ npmrc_tmp="$(mktemp /tmp/omnizap-npmrc.XXXXXX)"
122
+ {
123
+ printf 'registry=%s\n' "$registry"
124
+ if [ -n "$scope_owner" ]; then
125
+ printf '@%s:registry=%s\n' "$scope_owner" "$registry"
126
+ fi
127
+ printf '//%s/:_authToken=%s\n' "$registry_host" "$token"
128
+ printf '//%s:_authToken=%s\n' "$registry_host" "$token"
129
+ } > "$npmrc_tmp"
130
+ chmod 600 "$npmrc_tmp"
131
+ NPMRC_TMP_FILES+=("$npmrc_tmp")
132
+ printf '%s' "$npmrc_tmp"
133
+ }
134
+
135
+ publish_package_to_registry() {
136
+ local pkg_name="$1"
137
+ local pkg_version="$2"
138
+ local registry="$3"
139
+ local tag="$4"
140
+ local explicit_token="$5"
141
+ local skip_if_exists="$6"
142
+ local access="$7"
143
+ local token_keys_override="$8"
144
+ local otp="$9"
145
+
146
+ local token="$explicit_token"
147
+ if [ -z "$token" ]; then
148
+ local token_keys="$token_keys_override"
149
+ if [ -z "$token_keys" ]; then
150
+ token_keys="$(default_token_keys_for_registry "$registry")"
151
+ fi
152
+ token="$(resolve_token_from_dotenv "$token_keys")"
153
+ fi
154
+
155
+ if [ -z "$token" ]; then
156
+ printf '[deploy] Publish habilitado para %s, mas nenhum token foi encontrado.\n' "$registry" >&2
157
+ exit 1
158
+ fi
159
+
160
+ if printf '%s' "$registry" | grep -q 'registry.npmjs.org'; then
161
+ if printf '%s' "$token" | grep -Eq '^(ghp_|gho_|ghu_|ghs_|ghr_|github_pat_)'; then
162
+ printf '[deploy] Token incompatível para npmjs.org (parece token do GitHub).\n' >&2
163
+ printf '[deploy] Configure DEPLOY_PACKAGE_SECONDARY_TOKEN ou NPM_TOKEN com token do npmjs.\n' >&2
164
+ exit 1
165
+ fi
166
+ fi
167
+
168
+ local scope_owner=""
169
+ scope_owner="$(printf '%s' "$pkg_name" | sed -nE 's#^@([^/]+)/.*#\1#p')"
170
+ local pkg_base_name=""
171
+ pkg_base_name="$(printf '%s' "$pkg_name" | sed -E 's#^@[^/]+/##')"
172
+
173
+ if printf '%s' "$registry" | grep -q 'npm.pkg.github.com'; then
174
+ if ! printf '%s' "$pkg_name" | grep -q '^@'; then
175
+ printf '[deploy] Para GitHub Packages o nome do pacote deve ser escopado (ex: @owner/repo).\n' >&2
176
+ exit 1
177
+ fi
178
+
179
+ local expected_owner="${DEPLOY_PACKAGE_SCOPE_OWNER:-}"
180
+ if [ -z "$expected_owner" ]; then
181
+ expected_owner="$(resolve_github_owner)"
182
+ fi
183
+
184
+ if [ -n "$expected_owner" ] && [ -n "$scope_owner" ] && [ "$scope_owner" != "$expected_owner" ]; then
185
+ printf '[deploy] Scope do pacote (%s) difere do owner GitHub esperado (%s).\n' "$scope_owner" "$expected_owner" >&2
186
+ printf '[deploy] Ajuste com: npm pkg set name=\"@%s/%s\"\n' "$expected_owner" "$pkg_base_name" >&2
187
+ printf '[deploy] Ou defina DEPLOY_PACKAGE_SCOPE_OWNER para publicar em outro owner.\n' >&2
188
+ exit 1
189
+ fi
190
+ fi
191
+
192
+ local npmrc_tmp=""
193
+ npmrc_tmp="$(create_npmrc_for_registry "$registry" "$token" "$scope_owner")"
194
+
195
+ if ! (
196
+ cd "$PROJECT_ROOT" &&
197
+ npm_config_userconfig="$npmrc_tmp" npm whoami --registry "$registry" --userconfig "$npmrc_tmp" >/dev/null 2>&1
198
+ ); then
199
+ printf '[deploy] Falha de autenticação no registry %s. Verifique token/permissões.\n' "$registry" >&2
200
+ exit 1
201
+ fi
202
+
203
+ if [ "$skip_if_exists" = "1" ]; then
204
+ if (
205
+ cd "$PROJECT_ROOT" &&
206
+ npm_config_userconfig="$npmrc_tmp" npm view "${pkg_name}@${pkg_version}" --registry "$registry" --userconfig "$npmrc_tmp" >/dev/null 2>&1
207
+ ); then
208
+ log "Pacote ${pkg_name}@${pkg_version} já publicado em $registry (skip)."
209
+ return 0
210
+ fi
211
+ fi
212
+
213
+ log "Publicando ${pkg_name}@${pkg_version} em $registry (tag=$tag)"
214
+ local publish_cmd=(npm publish --registry "$registry" --tag "$tag" --userconfig "$npmrc_tmp")
215
+ if [ -n "$access" ] && printf '%s' "$registry" | grep -q 'registry.npmjs.org' && printf '%s' "$pkg_name" | grep -q '^@'; then
216
+ publish_cmd+=(--access "$access")
217
+ fi
218
+ if [ -n "$otp" ]; then
219
+ publish_cmd+=(--otp "$otp")
220
+ fi
221
+ (
222
+ cd "$PROJECT_ROOT" &&
223
+ npm_config_userconfig="$npmrc_tmp" "${publish_cmd[@]}"
224
+ )
225
+ log "Publish concluído para ${pkg_name}@${pkg_version} em $registry."
226
+ }
227
+
228
+ as_root() {
229
+ if [ "$(id -u)" -eq 0 ]; then
230
+ "$@"
231
+ return
232
+ fi
233
+
234
+ if command -v sudo >/dev/null 2>&1; then
235
+ sudo "$@"
236
+ return
237
+ fi
238
+
239
+ printf '[deploy] precisa de root/sudo para executar: %s\n' "$*" >&2
240
+ exit 1
241
+ }
242
+
243
+ require_cmd node
244
+ require_cmd rsync
245
+ require_cmd nginx
246
+ require_cmd systemctl
247
+
248
+ if [ ! -d "$SOURCE_DIR" ]; then
249
+ printf '[deploy] pasta de origem não encontrada: %s\n' "$SOURCE_DIR" >&2
250
+ exit 1
251
+ fi
252
+
253
+ STAGING_DIR="$(mktemp -d /tmp/omnizap-deploy.XXXXXX)"
254
+
255
+ github_deploy_start() {
256
+ if [ "$DRY_RUN" = "1" ] || [ "$GITHUB_NOTIFY" != "1" ]; then
257
+ return 0
258
+ fi
259
+
260
+ local deployment_id=""
261
+ deployment_id="$(
262
+ node "$PROJECT_ROOT/scripts/github-deploy-notify.mjs" start \
263
+ --build-id "$BUILD_ID" \
264
+ --environment "$GITHUB_ENVIRONMENT" \
265
+ --environment-url "$VERIFY_URL" \
266
+ --log-url "$VERIFY_URL" 2>/dev/null || true
267
+ )"
268
+ deployment_id="$(printf '%s' "$deployment_id" | tr -d '[:space:]')"
269
+
270
+ if [ -n "$deployment_id" ]; then
271
+ GITHUB_DEPLOYMENT_ID="$deployment_id"
272
+ log "GitHub deployment iniciado: id=$GITHUB_DEPLOYMENT_ID"
273
+ else
274
+ log "GitHub deployment não iniciado (token/repo ausente ou API indisponível)."
275
+ fi
276
+ }
277
+
278
+ github_deploy_status() {
279
+ local state="$1"
280
+ if [ -z "$GITHUB_DEPLOYMENT_ID" ] || [ "$GITHUB_NOTIFY" != "1" ]; then
281
+ return 0
282
+ fi
283
+
284
+ if node "$PROJECT_ROOT/scripts/github-deploy-notify.mjs" status \
285
+ --deployment-id "$GITHUB_DEPLOYMENT_ID" \
286
+ --state "$state" \
287
+ --build-id "$BUILD_ID" \
288
+ --environment "$GITHUB_ENVIRONMENT" \
289
+ --environment-url "$VERIFY_URL" \
290
+ --log-url "$VERIFY_URL" >/dev/null 2>&1; then
291
+ log "GitHub deployment atualizado: id=$GITHUB_DEPLOYMENT_ID state=$state"
292
+ else
293
+ log "Aviso: falha ao atualizar status do deployment no GitHub (state=$state)."
294
+ fi
295
+ }
296
+
297
+ run_package_stage() {
298
+ if [ "$DRY_RUN" = "1" ] || [ "$PACKAGE_STEP" != "1" ]; then
299
+ return 0
300
+ fi
301
+
302
+ require_cmd npm
303
+
304
+ local pkg_version="n/d"
305
+ pkg_version="$(cd "$PROJECT_ROOT" && npm pkg get version 2>/dev/null | tr -d '"[:space:]' || true)"
306
+ log "Etapa package iniciada (versão=$pkg_version)."
307
+
308
+ if [ "$PACKAGE_INSTALL" = "1" ]; then
309
+ if [ -f "$PROJECT_ROOT/package-lock.json" ]; then
310
+ log "Instalando dependências com npm ci --omit=dev"
311
+ (cd "$PROJECT_ROOT" && npm ci --omit=dev)
312
+ else
313
+ log "Instalando dependências com npm install --omit=dev"
314
+ (cd "$PROJECT_ROOT" && npm install --omit=dev)
315
+ fi
316
+ fi
317
+
318
+ if [ "$PACKAGE_TEST" = "1" ]; then
319
+ log "Executando testes de package (npm test)"
320
+ (cd "$PROJECT_ROOT" && npm test)
321
+ fi
322
+
323
+ if [ "$PACKAGE_PACK" = "1" ]; then
324
+ log "Gerando artefato npm pack"
325
+ mkdir -p "$PACKAGE_ARTIFACTS_DIR"
326
+ local pack_name=""
327
+ pack_name="$(cd "$PROJECT_ROOT" && npm pack --silent)"
328
+ if [ -n "$pack_name" ] && [ -f "$PROJECT_ROOT/$pack_name" ]; then
329
+ mv "$PROJECT_ROOT/$pack_name" "$PACKAGE_ARTIFACTS_DIR/$pack_name"
330
+ log "Artefato salvo em $PACKAGE_ARTIFACTS_DIR/$pack_name"
331
+ fi
332
+ fi
333
+
334
+ if [ "$PACKAGE_PUBLISH" = "1" ] || [ "$PACKAGE_PUBLISH_SECONDARY" = "1" ]; then
335
+ local pkg_name=""
336
+ pkg_name="$(cd "$PROJECT_ROOT" && npm pkg get name 2>/dev/null | tr -d '"[:space:]' || true)"
337
+ if [ -z "$pkg_name" ] || [ -z "$pkg_version" ] || [ "$pkg_version" = "n/d" ]; then
338
+ printf '[deploy] não foi possível ler nome/versão do package para publish.\n' >&2
339
+ exit 1
340
+ fi
341
+
342
+ if [ "$PACKAGE_PUBLISH" = "1" ]; then
343
+ publish_package_to_registry \
344
+ "$pkg_name" \
345
+ "$pkg_version" \
346
+ "$PACKAGE_REGISTRY" \
347
+ "$PACKAGE_TAG" \
348
+ "$PACKAGE_TOKEN" \
349
+ "$PACKAGE_PUBLISH_SKIP_IF_EXISTS" \
350
+ "" \
351
+ "" \
352
+ "$PACKAGE_OTP"
353
+ fi
354
+
355
+ if [ "$PACKAGE_PUBLISH_SECONDARY" = "1" ]; then
356
+ if [ "$PACKAGE_PUBLISH" = "1" ] && [ "$PACKAGE_SECONDARY_REGISTRY" = "$PACKAGE_REGISTRY" ] && [ "$PACKAGE_SECONDARY_TAG" = "$PACKAGE_TAG" ]; then
357
+ log "Registry/tag secundário igual ao primário. Publish secundário ignorado."
358
+ else
359
+ publish_package_to_registry \
360
+ "$pkg_name" \
361
+ "$pkg_version" \
362
+ "$PACKAGE_SECONDARY_REGISTRY" \
363
+ "$PACKAGE_SECONDARY_TAG" \
364
+ "$PACKAGE_SECONDARY_TOKEN" \
365
+ "$PACKAGE_SECONDARY_PUBLISH_SKIP_IF_EXISTS" \
366
+ "$PACKAGE_SECONDARY_ACCESS" \
367
+ "$PACKAGE_SECONDARY_TOKEN_KEYS" \
368
+ "$PACKAGE_SECONDARY_OTP"
369
+ fi
370
+ fi
371
+ fi
372
+
373
+ log "Etapa package concluída."
374
+ }
375
+
376
+ finalize() {
377
+ local exit_code=$?
378
+ if [ "$exit_code" -eq 0 ]; then
379
+ github_deploy_status "success"
380
+ else
381
+ github_deploy_status "failure"
382
+ fi
383
+ for npmrc_tmp in "${NPMRC_TMP_FILES[@]:-}"; do
384
+ if [ -n "$npmrc_tmp" ] && [ -f "$npmrc_tmp" ]; then
385
+ rm -f "$npmrc_tmp"
386
+ fi
387
+ done
388
+ rm -rf "$STAGING_DIR"
389
+ }
390
+ trap finalize EXIT
391
+
392
+ log "build_id=$BUILD_ID"
393
+ log "Preparando staging em $STAGING_DIR"
394
+ rsync -a --delete "$SOURCE_DIR"/ "$STAGING_DIR"/
395
+ node "$PROJECT_ROOT/scripts/cache-bust.mjs" --dir "$STAGING_DIR" --version "$BUILD_ID"
396
+
397
+ if [ "$BACKUP_ENABLED" = "1" ] && [ "$DRY_RUN" != "1" ] && [ -d "$DEPLOY_DIR" ]; then
398
+ BACKUP_STAMP="$(date -u +%Y%m%d-%H%M%S)"
399
+ BACKUP_PATH="$BACKUP_DIR/$BACKUP_STAMP"
400
+ log "Criando backup em $BACKUP_PATH"
401
+ as_root mkdir -p "$BACKUP_PATH"
402
+ as_root rsync -a --delete --exclude '.backup/' "$DEPLOY_DIR"/ "$BACKUP_PATH"/
403
+ fi
404
+
405
+ if [ ! -d "$DEPLOY_DIR" ]; then
406
+ log "Criando diretório de deploy $DEPLOY_DIR"
407
+ as_root mkdir -p "$DEPLOY_DIR"
408
+ fi
409
+
410
+ if [ "$DRY_RUN" = "1" ]; then
411
+ log "DRY_RUN=1 ativo. Simulando sync para $DEPLOY_DIR"
412
+ as_root rsync -avhn --delete --exclude '.backup/' "$STAGING_DIR"/ "$DEPLOY_DIR"/
413
+ log "Dry-run finalizado."
414
+ exit 0
415
+ fi
416
+
417
+ github_deploy_start
418
+ run_package_stage
419
+
420
+ log "Sincronizando arquivos para $DEPLOY_DIR"
421
+ as_root rsync -a --delete --exclude '.backup/' "$STAGING_DIR"/ "$DEPLOY_DIR"/
422
+
423
+ log "Validando configuração do nginx"
424
+ as_root nginx -t
425
+
426
+ log "Recarregando serviço $NGINX_SERVICE"
427
+ as_root systemctl reload "$NGINX_SERVICE"
428
+ as_root systemctl is-active --quiet "$NGINX_SERVICE"
429
+
430
+ if [ "$RESTART_PM2" = "1" ] && command -v pm2 >/dev/null 2>&1; then
431
+ if as_root pm2 describe "$PM2_APP_NAME" >/dev/null 2>&1; then
432
+ log "Reiniciando PM2 app $PM2_APP_NAME"
433
+ as_root pm2 restart "$PM2_APP_NAME" --update-env >/dev/null
434
+ else
435
+ log "PM2 app '$PM2_APP_NAME' não encontrada. Restart ignorado."
436
+ fi
437
+ fi
438
+
439
+ if [ -f "$DEPLOY_DIR/index.html" ]; then
440
+ if command -v rg >/dev/null 2>&1; then
441
+ DEPLOYED_REF="$(rg -o '/js/apps/homeApp.js\\?v=[^"]+' "$DEPLOY_DIR/index.html" -m 1 || true)"
442
+ else
443
+ DEPLOYED_REF="$(grep -oE '/js/apps/homeApp\\.js\\?v=[^"]+' "$DEPLOY_DIR/index.html" | head -n 1 || true)"
444
+ fi
445
+ if [ -n "$DEPLOYED_REF" ]; then
446
+ log "Cache-bust aplicado: $DEPLOYED_REF"
447
+ fi
448
+ fi
449
+
450
+ if command -v curl >/dev/null 2>&1; then
451
+ if curl -kfsS "$VERIFY_URL" >/dev/null; then
452
+ log "Health check OK em $VERIFY_URL"
453
+ else
454
+ log "Health check falhou em $VERIFY_URL (deploy concluído, verifique manualmente)."
455
+ fi
456
+ fi
457
+
458
+ log "Deploy concluído com sucesso."