@kaikybrofc/omnizap-system 2.2.0 → 2.2.2
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.
- package/.env.example +8 -0
- package/README.md +23 -0
- package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +286 -0
- package/package.json +1 -1
- package/scripts/github-release-notify.mjs +13 -1
- package/scripts/release.sh +191 -15
- package/RELEASE-v2.1.2.md +0 -83
package/.env.example
CHANGED
|
@@ -539,6 +539,10 @@ RELEASE_GIT_BRANCH=
|
|
|
539
539
|
RELEASE_GIT_PRE_COMMIT_MESSAGE=chore(release): auto-commit before release
|
|
540
540
|
RELEASE_GIT_COMMIT_VERSION=1
|
|
541
541
|
RELEASE_GIT_VERSION_COMMIT_PREFIX=chore(release): v
|
|
542
|
+
# Geração automática de tag da versão no git
|
|
543
|
+
RELEASE_GIT_TAG_CREATE=1
|
|
544
|
+
RELEASE_GIT_TAG_PUSH=1
|
|
545
|
+
RELEASE_GIT_TAG_ANNOTATED=1
|
|
542
546
|
|
|
543
547
|
# Cria/atualiza GitHub Release (aba Releases) automaticamente no npm run release
|
|
544
548
|
RELEASE_GITHUB_RELEASE=1
|
|
@@ -549,6 +553,10 @@ RELEASE_GITHUB_TOKEN=
|
|
|
549
553
|
RELEASE_GITHUB_TAG_PREFIX=v
|
|
550
554
|
RELEASE_GITHUB_NAME_PREFIX=v
|
|
551
555
|
RELEASE_GITHUB_GENERATE_NOTES=1
|
|
556
|
+
# Inclui seção markdown com arquivos alterados no corpo da release
|
|
557
|
+
RELEASE_GITHUB_RELEASE_INCLUDE_CHANGED_FILES=1
|
|
558
|
+
# Limite de arquivos listados no markdown da release
|
|
559
|
+
RELEASE_GITHUB_RELEASE_MAX_FILES=300
|
|
552
560
|
# Opcional: 1 para prerelease, 0 para release normal, vazio = auto (detecta '-' na versao)
|
|
553
561
|
RELEASE_GITHUB_PRERELEASE=
|
|
554
562
|
RELEASE_GITHUB_DRAFT=0
|
package/README.md
CHANGED
|
@@ -26,6 +26,24 @@ O **OmniZap System** é uma plataforma de automação para WhatsApp usando **Nod
|
|
|
26
26
|
- Termos de uso: https://omnizap.shop/termos-de-uso/
|
|
27
27
|
- Licença: https://omnizap.shop/licenca/
|
|
28
28
|
|
|
29
|
+
## Snapshot dinâmico para README/NPM
|
|
30
|
+
|
|
31
|
+
Use as rotas abaixo para consumir o bloco já renderizado com dados do sistema:
|
|
32
|
+
|
|
33
|
+
- JSON: `GET /api/sticker-packs/readme-summary`
|
|
34
|
+
- Markdown: `GET /api/sticker-packs/readme-markdown`
|
|
35
|
+
|
|
36
|
+
Conteúdo incluído no snapshot:
|
|
37
|
+
|
|
38
|
+
- total de usuários (`lid_map`)
|
|
39
|
+
- total de grupos
|
|
40
|
+
- total de packs e stickers
|
|
41
|
+
- total de mensagens registradas
|
|
42
|
+
- tipos de mensagem mais usados
|
|
43
|
+
- lista de comandos disponíveis no menu
|
|
44
|
+
|
|
45
|
+
Atualização em cache: **30 minutos** por padrão (`README_SUMMARY_CACHE_SECONDS=1800`).
|
|
46
|
+
|
|
29
47
|
## Recursos principais
|
|
30
48
|
|
|
31
49
|
- Gerenciamento de grupos (admin, boas-vindas, despedida, anti-link, captcha).
|
|
@@ -239,6 +257,9 @@ Variáveis do fluxo de release (git):
|
|
|
239
257
|
- `RELEASE_GIT_PRE_COMMIT_MESSAGE` (default: `chore(release): auto-commit before release`)
|
|
240
258
|
- `RELEASE_GIT_COMMIT_VERSION` (default: `1`) - commita alteração da versão após sucesso
|
|
241
259
|
- `RELEASE_GIT_VERSION_COMMIT_PREFIX` (default: `chore(release): v`)
|
|
260
|
+
- `RELEASE_GIT_TAG_CREATE` (default: `1`) - cria tag `vX.Y.Z` no release
|
|
261
|
+
- `RELEASE_GIT_TAG_PUSH` (default: `1`) - envia a tag para o remoto
|
|
262
|
+
- `RELEASE_GIT_TAG_ANNOTATED` (default: `1`) - usa tag anotada (`git tag -a`)
|
|
242
263
|
- `RELEASE_GITHUB_RELEASE` (default: `1`) - cria/atualiza GitHub Release na aba Releases
|
|
243
264
|
- `RELEASE_REQUIRE_GITHUB_RELEASE` (default: `1`) - falha se GitHub Release estiver desativado
|
|
244
265
|
- `RELEASE_GITHUB_REPO` (opcional; ex.: `kaikybrofc/omnizap-system`)
|
|
@@ -246,6 +267,8 @@ Variáveis do fluxo de release (git):
|
|
|
246
267
|
- `RELEASE_GITHUB_TAG_PREFIX` (default: `v`) - prefixo da tag (`v2.1.9`)
|
|
247
268
|
- `RELEASE_GITHUB_NAME_PREFIX` (default: `v`) - prefixo do nome exibido na release
|
|
248
269
|
- `RELEASE_GITHUB_GENERATE_NOTES` (default: `1`) - usa notas automáticas do GitHub
|
|
270
|
+
- `RELEASE_GITHUB_RELEASE_INCLUDE_CHANGED_FILES` (default: `1`) - adiciona markdown com arquivos alterados no corpo da release
|
|
271
|
+
- `RELEASE_GITHUB_RELEASE_MAX_FILES` (default: `300`) - limite de arquivos listados no markdown
|
|
249
272
|
- `RELEASE_GITHUB_PRERELEASE` (default: auto) - vazio detecta `-` na versão como prerelease
|
|
250
273
|
- `RELEASE_GITHUB_DRAFT` (default: `0`)
|
|
251
274
|
- `RELEASE_GITHUB_TARGET` (opcional; vazio usa `HEAD`)
|
|
@@ -63,6 +63,16 @@ import {
|
|
|
63
63
|
buildViewerTagAffinity,
|
|
64
64
|
computePackSignals,
|
|
65
65
|
} from './stickerPackMarketplaceService.js';
|
|
66
|
+
import {
|
|
67
|
+
buildAdminMenu,
|
|
68
|
+
buildAiMenu,
|
|
69
|
+
buildAnimeMenu,
|
|
70
|
+
buildMediaMenu,
|
|
71
|
+
buildMenuCaption,
|
|
72
|
+
buildQuoteMenu,
|
|
73
|
+
buildStatsMenu,
|
|
74
|
+
buildStickerMenu,
|
|
75
|
+
} from '../menuModule/common.js';
|
|
66
76
|
import { getMarketplaceDriftSnapshot } from './stickerMarketplaceDriftService.js';
|
|
67
77
|
import { getStickerStorageConfig, readStickerAssetBuffer, saveStickerAssetFromBuffer } from './stickerStorageService.js';
|
|
68
78
|
import { convertToWebp } from '../stickerModule/convertToWebp.js';
|
|
@@ -193,6 +203,9 @@ const MARKETPLACE_GLOBAL_STATS_API_PATH = '/api/marketplace/stats';
|
|
|
193
203
|
const MARKETPLACE_GLOBAL_STATS_CACHE_SECONDS = clampInt(process.env.MARKETPLACE_GLOBAL_STATS_CACHE_SECONDS, 45, 30, 60);
|
|
194
204
|
const HOME_MARKETPLACE_STATS_CACHE_SECONDS = clampInt(process.env.HOME_MARKETPLACE_STATS_CACHE_SECONDS, 45, 10, 300);
|
|
195
205
|
const SYSTEM_SUMMARY_CACHE_SECONDS = clampInt(process.env.SYSTEM_SUMMARY_CACHE_SECONDS, 20, 5, 120);
|
|
206
|
+
const README_SUMMARY_CACHE_SECONDS = clampInt(process.env.README_SUMMARY_CACHE_SECONDS, 60 * 30, 60, 60 * 60 * 6);
|
|
207
|
+
const README_MESSAGE_TYPE_SAMPLE_LIMIT = clampInt(process.env.README_MESSAGE_TYPE_SAMPLE_LIMIT, 25000, 500, 250000);
|
|
208
|
+
const README_COMMAND_PREFIX = String(process.env.README_COMMAND_PREFIX || PACK_COMMAND_PREFIX).trim() || PACK_COMMAND_PREFIX;
|
|
196
209
|
const SITE_CANONICAL_HOST = String(process.env.SITE_CANONICAL_HOST || 'omnizap.shop').trim().toLowerCase() || 'omnizap.shop';
|
|
197
210
|
const SITE_CANONICAL_SCHEME = String(process.env.SITE_CANONICAL_SCHEME || 'https').trim().toLowerCase() === 'http'
|
|
198
211
|
? 'http'
|
|
@@ -283,6 +296,11 @@ const SYSTEM_SUMMARY_CACHE = {
|
|
|
283
296
|
value: null,
|
|
284
297
|
pending: null,
|
|
285
298
|
};
|
|
299
|
+
const README_SUMMARY_CACHE = {
|
|
300
|
+
expiresAt: 0,
|
|
301
|
+
value: null,
|
|
302
|
+
pending: null,
|
|
303
|
+
};
|
|
286
304
|
const SITEMAP_CACHE = {
|
|
287
305
|
expiresAt: 0,
|
|
288
306
|
xml: '',
|
|
@@ -3700,6 +3718,9 @@ const invalidateStickerCatalogDerivedCaches = () => {
|
|
|
3700
3718
|
SYSTEM_SUMMARY_CACHE.expiresAt = 0;
|
|
3701
3719
|
SYSTEM_SUMMARY_CACHE.value = null;
|
|
3702
3720
|
SYSTEM_SUMMARY_CACHE.pending = null;
|
|
3721
|
+
README_SUMMARY_CACHE.expiresAt = 0;
|
|
3722
|
+
README_SUMMARY_CACHE.value = null;
|
|
3723
|
+
README_SUMMARY_CACHE.pending = null;
|
|
3703
3724
|
};
|
|
3704
3725
|
|
|
3705
3726
|
const sendManagedMutationStatus = (req, res, status, extra = {}, statusCode = 200) => {
|
|
@@ -5736,6 +5757,253 @@ const parseMessageTypeFromRaw = (rawMessage) => {
|
|
|
5736
5757
|
}
|
|
5737
5758
|
};
|
|
5738
5759
|
|
|
5760
|
+
const formatPtBrInteger = (value) => Number(value || 0).toLocaleString('pt-BR');
|
|
5761
|
+
|
|
5762
|
+
const extractCommandsFromMenuLine = (line, commandPrefix) => {
|
|
5763
|
+
const normalizedLine = String(line || '').trim();
|
|
5764
|
+
if (!normalizedLine.startsWith('→')) return [];
|
|
5765
|
+
|
|
5766
|
+
const commandParts = normalizedLine
|
|
5767
|
+
.replace(/^→\s*/, '')
|
|
5768
|
+
.split('|')
|
|
5769
|
+
.map((part) => part.trim())
|
|
5770
|
+
.filter((part) => part.startsWith(commandPrefix));
|
|
5771
|
+
|
|
5772
|
+
return commandParts
|
|
5773
|
+
.map((part) => {
|
|
5774
|
+
const normalized = part.replace(/\s{2,}.*/, '').trim();
|
|
5775
|
+
const withoutPrefix = normalized.slice(commandPrefix.length).trim();
|
|
5776
|
+
if (!withoutPrefix) return '';
|
|
5777
|
+
|
|
5778
|
+
const tokens = withoutPrefix.split(/\s+/).filter(Boolean);
|
|
5779
|
+
const selectedTokens = [];
|
|
5780
|
+
for (const token of tokens) {
|
|
5781
|
+
if (/^[<[(]/.test(token)) break;
|
|
5782
|
+
selectedTokens.push(token);
|
|
5783
|
+
}
|
|
5784
|
+
if (!selectedTokens.length) return '';
|
|
5785
|
+
return `${commandPrefix}${selectedTokens.join(' ')}`;
|
|
5786
|
+
})
|
|
5787
|
+
.filter(Boolean);
|
|
5788
|
+
};
|
|
5789
|
+
|
|
5790
|
+
const collectAvailableMenuCommands = (commandPrefix = README_COMMAND_PREFIX) => {
|
|
5791
|
+
const sections = [
|
|
5792
|
+
buildMenuCaption('OmniZap', commandPrefix),
|
|
5793
|
+
buildStickerMenu(commandPrefix),
|
|
5794
|
+
buildMediaMenu(commandPrefix),
|
|
5795
|
+
buildQuoteMenu(commandPrefix),
|
|
5796
|
+
buildAnimeMenu(commandPrefix),
|
|
5797
|
+
buildAiMenu(commandPrefix),
|
|
5798
|
+
buildStatsMenu(commandPrefix),
|
|
5799
|
+
buildAdminMenu(commandPrefix),
|
|
5800
|
+
];
|
|
5801
|
+
|
|
5802
|
+
const commands = new Set();
|
|
5803
|
+
for (const section of sections) {
|
|
5804
|
+
for (const line of String(section || '').split('\n')) {
|
|
5805
|
+
const extracted = extractCommandsFromMenuLine(line, commandPrefix);
|
|
5806
|
+
for (const command of extracted) {
|
|
5807
|
+
commands.add(command);
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
5810
|
+
}
|
|
5811
|
+
|
|
5812
|
+
return Array.from(commands).sort((left, right) => left.localeCompare(right, 'pt-BR'));
|
|
5813
|
+
};
|
|
5814
|
+
|
|
5815
|
+
const renderReadmeSnapshotMarkdown = ({ generatedAt, totals, topMessageTypes, commands }) => {
|
|
5816
|
+
const typeLines = topMessageTypes.length
|
|
5817
|
+
? topMessageTypes.map((entry, index) => `${index + 1}. \`${entry.type}\` - **${formatPtBrInteger(entry.total)}**`)
|
|
5818
|
+
: ['1. `outros` - **0**'];
|
|
5819
|
+
|
|
5820
|
+
const commandLines = commands.length
|
|
5821
|
+
? commands.map((command) => `- \`${command}\``)
|
|
5822
|
+
: ['- Nenhum comando identificado no menu atual.'];
|
|
5823
|
+
|
|
5824
|
+
return [
|
|
5825
|
+
'## OmniZap System Snapshot',
|
|
5826
|
+
'',
|
|
5827
|
+
`Atualizado em: **${generatedAt}**`,
|
|
5828
|
+
`Janela de atualização: **${README_SUMMARY_CACHE_SECONDS} segundos**`,
|
|
5829
|
+
'',
|
|
5830
|
+
'### Totais do sistema',
|
|
5831
|
+
`- Usuários (lid_map): **${formatPtBrInteger(totals.total_users)}**`,
|
|
5832
|
+
`- Grupos: **${formatPtBrInteger(totals.total_groups)}**`,
|
|
5833
|
+
`- Packs: **${formatPtBrInteger(totals.total_packs)}**`,
|
|
5834
|
+
`- Stickers: **${formatPtBrInteger(totals.total_stickers)}**`,
|
|
5835
|
+
`- Mensagens registradas: **${formatPtBrInteger(totals.total_messages)}**`,
|
|
5836
|
+
'',
|
|
5837
|
+
`### Tipos de mensagem mais usados (amostra de ${formatPtBrInteger(totals.message_types_sample_size)} mensagens)`,
|
|
5838
|
+
...typeLines,
|
|
5839
|
+
'',
|
|
5840
|
+
'### Comandos disponíveis',
|
|
5841
|
+
...commandLines,
|
|
5842
|
+
'',
|
|
5843
|
+
].join('\n');
|
|
5844
|
+
};
|
|
5845
|
+
|
|
5846
|
+
const buildReadmeSummarySnapshot = async () => {
|
|
5847
|
+
const [
|
|
5848
|
+
lidMapTotalsRows,
|
|
5849
|
+
chatsTotalsRows,
|
|
5850
|
+
groupsMetadataTotalsRows,
|
|
5851
|
+
packTotalsRows,
|
|
5852
|
+
stickerTotalsRows,
|
|
5853
|
+
messageTotalsRows,
|
|
5854
|
+
messageTypeRows,
|
|
5855
|
+
] = await Promise.all([
|
|
5856
|
+
executeQuery(`SELECT COUNT(*) AS total_users FROM ${TABLES.LID_MAP}`),
|
|
5857
|
+
executeQuery(
|
|
5858
|
+
`SELECT
|
|
5859
|
+
COUNT(*) AS total_chats,
|
|
5860
|
+
SUM(CASE WHEN id LIKE '%@g.us' THEN 1 ELSE 0 END) AS total_groups
|
|
5861
|
+
FROM ${TABLES.CHATS}`,
|
|
5862
|
+
),
|
|
5863
|
+
executeQuery(`SELECT COUNT(*) AS total_groups FROM ${TABLES.GROUPS_METADATA}`),
|
|
5864
|
+
executeQuery(`SELECT COUNT(*) AS total_packs FROM ${TABLES.STICKER_PACK} WHERE deleted_at IS NULL`),
|
|
5865
|
+
executeQuery(`SELECT COUNT(*) AS total_stickers FROM ${TABLES.STICKER_ASSET}`),
|
|
5866
|
+
executeQuery(`SELECT COUNT(*) AS total_messages FROM ${TABLES.MESSAGES}`),
|
|
5867
|
+
executeQuery(
|
|
5868
|
+
`SELECT raw_message
|
|
5869
|
+
FROM ${TABLES.MESSAGES}
|
|
5870
|
+
WHERE raw_message IS NOT NULL
|
|
5871
|
+
ORDER BY id DESC
|
|
5872
|
+
LIMIT ${README_MESSAGE_TYPE_SAMPLE_LIMIT}`,
|
|
5873
|
+
),
|
|
5874
|
+
]);
|
|
5875
|
+
|
|
5876
|
+
const lidMapTotals = lidMapTotalsRows?.[0] || {};
|
|
5877
|
+
const chatsTotals = chatsTotalsRows?.[0] || {};
|
|
5878
|
+
const groupsMetadataTotals = groupsMetadataTotalsRows?.[0] || {};
|
|
5879
|
+
const packTotals = packTotalsRows?.[0] || {};
|
|
5880
|
+
const stickerTotals = stickerTotalsRows?.[0] || {};
|
|
5881
|
+
const messageTotals = messageTotalsRows?.[0] || {};
|
|
5882
|
+
|
|
5883
|
+
const totalGroupsFromChats = Number(chatsTotals?.total_groups || 0);
|
|
5884
|
+
const totalGroupsFromMetadata = Number(groupsMetadataTotals?.total_groups || 0);
|
|
5885
|
+
const totalGroups = Math.max(totalGroupsFromChats, totalGroupsFromMetadata);
|
|
5886
|
+
const totalMessages = Number(messageTotals?.total_messages || 0);
|
|
5887
|
+
|
|
5888
|
+
const typeCounts = new Map();
|
|
5889
|
+
const sampledMessages = Array.isArray(messageTypeRows) ? messageTypeRows.length : 0;
|
|
5890
|
+
for (const row of messageTypeRows || []) {
|
|
5891
|
+
const type = parseMessageTypeFromRaw(row?.raw_message);
|
|
5892
|
+
typeCounts.set(type, (typeCounts.get(type) || 0) + 1);
|
|
5893
|
+
}
|
|
5894
|
+
const topMessageTypes = Array.from(typeCounts.entries())
|
|
5895
|
+
.map(([type, total]) => ({ type, total: Number(total || 0) }))
|
|
5896
|
+
.sort((left, right) => Number(right.total || 0) - Number(left.total || 0))
|
|
5897
|
+
.slice(0, 8);
|
|
5898
|
+
|
|
5899
|
+
const commands = collectAvailableMenuCommands(README_COMMAND_PREFIX);
|
|
5900
|
+
const generatedAt = new Date().toISOString();
|
|
5901
|
+
|
|
5902
|
+
const totals = {
|
|
5903
|
+
total_users: Number(lidMapTotals?.total_users || 0),
|
|
5904
|
+
total_groups: totalGroups,
|
|
5905
|
+
total_groups_from_chats: totalGroupsFromChats,
|
|
5906
|
+
total_groups_from_metadata: totalGroupsFromMetadata,
|
|
5907
|
+
total_chats: Number(chatsTotals?.total_chats || 0),
|
|
5908
|
+
total_packs: Number(packTotals?.total_packs || 0),
|
|
5909
|
+
total_stickers: Number(stickerTotals?.total_stickers || 0),
|
|
5910
|
+
total_messages: totalMessages,
|
|
5911
|
+
message_types_sample_size: sampledMessages,
|
|
5912
|
+
message_types_total_coverage_percent: totalMessages > 0 ? Number(((sampledMessages / totalMessages) * 100).toFixed(2)) : 0,
|
|
5913
|
+
};
|
|
5914
|
+
|
|
5915
|
+
const markdown = renderReadmeSnapshotMarkdown({
|
|
5916
|
+
generatedAt,
|
|
5917
|
+
totals,
|
|
5918
|
+
topMessageTypes,
|
|
5919
|
+
commands,
|
|
5920
|
+
});
|
|
5921
|
+
|
|
5922
|
+
return {
|
|
5923
|
+
data: {
|
|
5924
|
+
generated_at: generatedAt,
|
|
5925
|
+
cache_seconds: README_SUMMARY_CACHE_SECONDS,
|
|
5926
|
+
command_prefix: README_COMMAND_PREFIX,
|
|
5927
|
+
totals,
|
|
5928
|
+
top_message_types: topMessageTypes,
|
|
5929
|
+
commands,
|
|
5930
|
+
markdown,
|
|
5931
|
+
},
|
|
5932
|
+
};
|
|
5933
|
+
};
|
|
5934
|
+
|
|
5935
|
+
const getReadmeSummaryCached = async () => {
|
|
5936
|
+
const now = Date.now();
|
|
5937
|
+
const hasValue = Boolean(README_SUMMARY_CACHE.value);
|
|
5938
|
+
|
|
5939
|
+
if (hasValue && now < README_SUMMARY_CACHE.expiresAt) {
|
|
5940
|
+
return README_SUMMARY_CACHE.value;
|
|
5941
|
+
}
|
|
5942
|
+
|
|
5943
|
+
if (!README_SUMMARY_CACHE.pending) {
|
|
5944
|
+
README_SUMMARY_CACHE.pending = withTimeout(buildReadmeSummarySnapshot(), 7000)
|
|
5945
|
+
.then((payload) => {
|
|
5946
|
+
README_SUMMARY_CACHE.value = payload;
|
|
5947
|
+
README_SUMMARY_CACHE.expiresAt = Date.now() + README_SUMMARY_CACHE_SECONDS * 1000;
|
|
5948
|
+
return payload;
|
|
5949
|
+
})
|
|
5950
|
+
.finally(() => {
|
|
5951
|
+
README_SUMMARY_CACHE.pending = null;
|
|
5952
|
+
});
|
|
5953
|
+
}
|
|
5954
|
+
|
|
5955
|
+
if (hasValue) return README_SUMMARY_CACHE.value;
|
|
5956
|
+
return README_SUMMARY_CACHE.pending;
|
|
5957
|
+
};
|
|
5958
|
+
|
|
5959
|
+
const handleReadmeSummaryRequest = async (req, res) => {
|
|
5960
|
+
try {
|
|
5961
|
+
const payload = await getReadmeSummaryCached();
|
|
5962
|
+
sendJson(req, res, 200, payload);
|
|
5963
|
+
} catch (error) {
|
|
5964
|
+
logger.warn('Falha ao montar resumo markdown para README.', {
|
|
5965
|
+
action: 'readme_summary_error',
|
|
5966
|
+
error: error?.message,
|
|
5967
|
+
});
|
|
5968
|
+
if (README_SUMMARY_CACHE.value) {
|
|
5969
|
+
sendJson(req, res, 200, {
|
|
5970
|
+
...README_SUMMARY_CACHE.value,
|
|
5971
|
+
meta: {
|
|
5972
|
+
stale: true,
|
|
5973
|
+
error: error?.message || 'fallback_cache',
|
|
5974
|
+
},
|
|
5975
|
+
});
|
|
5976
|
+
return;
|
|
5977
|
+
}
|
|
5978
|
+
sendJson(req, res, 503, { error: 'Resumo markdown indisponível no momento.' });
|
|
5979
|
+
}
|
|
5980
|
+
};
|
|
5981
|
+
|
|
5982
|
+
const handleReadmeMarkdownRequest = async (req, res) => {
|
|
5983
|
+
try {
|
|
5984
|
+
const payload = await getReadmeSummaryCached();
|
|
5985
|
+
const markdown = String(payload?.data?.markdown || '').trim();
|
|
5986
|
+
res.setHeader('X-Robots-Tag', 'noindex, nofollow');
|
|
5987
|
+
res.setHeader('Cache-Control', `public, max-age=${Math.min(README_SUMMARY_CACHE_SECONDS, 300)}`);
|
|
5988
|
+
res.setHeader('X-Cache-Seconds', String(README_SUMMARY_CACHE_SECONDS));
|
|
5989
|
+
sendText(req, res, 200, markdown ? `${markdown}\n` : '', 'text/markdown; charset=utf-8');
|
|
5990
|
+
} catch (error) {
|
|
5991
|
+
logger.warn('Falha ao renderizar markdown para README.', {
|
|
5992
|
+
action: 'readme_markdown_error',
|
|
5993
|
+
error: error?.message,
|
|
5994
|
+
});
|
|
5995
|
+
if (README_SUMMARY_CACHE.value) {
|
|
5996
|
+
const markdown = String(README_SUMMARY_CACHE.value?.data?.markdown || '').trim();
|
|
5997
|
+
res.setHeader('X-Robots-Tag', 'noindex, nofollow');
|
|
5998
|
+
res.setHeader('Cache-Control', `public, max-age=${Math.min(README_SUMMARY_CACHE_SECONDS, 300)}`);
|
|
5999
|
+
res.setHeader('X-Cache-Seconds', String(README_SUMMARY_CACHE_SECONDS));
|
|
6000
|
+
sendText(req, res, 200, markdown ? `${markdown}\n` : '', 'text/markdown; charset=utf-8');
|
|
6001
|
+
return;
|
|
6002
|
+
}
|
|
6003
|
+
sendText(req, res, 503, 'Resumo markdown indisponivel no momento.\n', 'text/plain; charset=utf-8');
|
|
6004
|
+
}
|
|
6005
|
+
};
|
|
6006
|
+
|
|
5739
6007
|
const resolveBotUserCandidates = (activeSocket) => {
|
|
5740
6008
|
const candidates = new Set();
|
|
5741
6009
|
const botJidFromSocket = resolveBotJid(activeSocket?.user?.id);
|
|
@@ -7155,6 +7423,24 @@ const handleCatalogApiRequest = async (req, res, pathname, url) => {
|
|
|
7155
7423
|
return true;
|
|
7156
7424
|
}
|
|
7157
7425
|
|
|
7426
|
+
if (segments.length === 1 && segments[0] === 'readme-summary') {
|
|
7427
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
7428
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
7429
|
+
return true;
|
|
7430
|
+
}
|
|
7431
|
+
await handleReadmeSummaryRequest(req, res);
|
|
7432
|
+
return true;
|
|
7433
|
+
}
|
|
7434
|
+
|
|
7435
|
+
if (segments.length === 1 && segments[0] === 'readme-markdown') {
|
|
7436
|
+
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
7437
|
+
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
|
7438
|
+
return true;
|
|
7439
|
+
}
|
|
7440
|
+
await handleReadmeMarkdownRequest(req, res);
|
|
7441
|
+
return true;
|
|
7442
|
+
}
|
|
7443
|
+
|
|
7158
7444
|
if (segments.length === 1 && segments[0] === 'support') {
|
|
7159
7445
|
if (!['GET', 'HEAD'].includes(req.method || '')) {
|
|
7160
7446
|
sendJson(req, res, 405, { error: 'Metodo nao permitido.' });
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
|
+
import fs from 'node:fs';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
import process from 'node:process';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
@@ -80,11 +81,22 @@ if (!repoOwner || !repoName) {
|
|
|
80
81
|
const tag = getArg('--tag');
|
|
81
82
|
const target = getArg('--target');
|
|
82
83
|
const name = getArg('--name', tag);
|
|
83
|
-
const
|
|
84
|
+
const bodyArg = getArg('--body', '');
|
|
85
|
+
const bodyFile = getArg('--body-file', '');
|
|
84
86
|
const generateNotes = toBool(getArg('--generate-notes', 'true'), true);
|
|
85
87
|
const prerelease = toBool(getArg('--prerelease', 'false'), false);
|
|
86
88
|
const draft = toBool(getArg('--draft', 'false'), false);
|
|
87
89
|
|
|
90
|
+
let body = bodyArg;
|
|
91
|
+
if (!body && bodyFile) {
|
|
92
|
+
try {
|
|
93
|
+
body = fs.readFileSync(bodyFile, 'utf8');
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error(`Falha ao ler --body-file (${bodyFile}): ${error?.message || error}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
88
100
|
if (!tag) {
|
|
89
101
|
console.error('Parâmetro obrigatório ausente: --tag');
|
|
90
102
|
process.exit(1);
|
package/scripts/release.sh
CHANGED
|
@@ -13,6 +13,9 @@ RELEASE_GIT_BRANCH="${RELEASE_GIT_BRANCH:-}"
|
|
|
13
13
|
RELEASE_GIT_PRE_COMMIT_MESSAGE="${RELEASE_GIT_PRE_COMMIT_MESSAGE:-chore(release): auto-commit before release}"
|
|
14
14
|
RELEASE_GIT_COMMIT_VERSION="${RELEASE_GIT_COMMIT_VERSION:-1}"
|
|
15
15
|
RELEASE_GIT_VERSION_COMMIT_PREFIX="${RELEASE_GIT_VERSION_COMMIT_PREFIX:-chore(release): v}"
|
|
16
|
+
RELEASE_GIT_TAG_CREATE="${RELEASE_GIT_TAG_CREATE:-1}"
|
|
17
|
+
RELEASE_GIT_TAG_PUSH="${RELEASE_GIT_TAG_PUSH:-1}"
|
|
18
|
+
RELEASE_GIT_TAG_ANNOTATED="${RELEASE_GIT_TAG_ANNOTATED:-1}"
|
|
16
19
|
RELEASE_GITHUB_RELEASE="${RELEASE_GITHUB_RELEASE:-1}"
|
|
17
20
|
RELEASE_REQUIRE_GITHUB_RELEASE="${RELEASE_REQUIRE_GITHUB_RELEASE:-1}"
|
|
18
21
|
RELEASE_GITHUB_TAG_PREFIX="${RELEASE_GITHUB_TAG_PREFIX:-v}"
|
|
@@ -21,6 +24,8 @@ RELEASE_GITHUB_GENERATE_NOTES="${RELEASE_GITHUB_GENERATE_NOTES:-1}"
|
|
|
21
24
|
RELEASE_GITHUB_PRERELEASE="${RELEASE_GITHUB_PRERELEASE:-}"
|
|
22
25
|
RELEASE_GITHUB_DRAFT="${RELEASE_GITHUB_DRAFT:-0}"
|
|
23
26
|
RELEASE_GITHUB_TARGET="${RELEASE_GITHUB_TARGET:-}"
|
|
27
|
+
RELEASE_GITHUB_RELEASE_INCLUDE_CHANGED_FILES="${RELEASE_GITHUB_RELEASE_INCLUDE_CHANGED_FILES:-1}"
|
|
28
|
+
RELEASE_GITHUB_RELEASE_MAX_FILES="${RELEASE_GITHUB_RELEASE_MAX_FILES:-300}"
|
|
24
29
|
RELEASE_REQUIRE_DUAL_PUBLISH="${RELEASE_REQUIRE_DUAL_PUBLISH:-1}"
|
|
25
30
|
RELEASE_VERIFY_UNIFIED_VERSION="${RELEASE_VERIFY_UNIFIED_VERSION:-1}"
|
|
26
31
|
RELEASE_VERIFY_PRIMARY_REGISTRY="${RELEASE_VERIFY_PRIMARY_REGISTRY:-${DEPLOY_PACKAGE_REGISTRY:-https://npm.pkg.github.com}}"
|
|
@@ -28,6 +33,7 @@ RELEASE_VERIFY_SECONDARY_REGISTRY="${RELEASE_VERIFY_SECONDARY_REGISTRY:-${DEPLOY
|
|
|
28
33
|
RELEASE_VERIFY_PRIMARY_TOKEN_KEYS="${RELEASE_VERIFY_PRIMARY_TOKEN_KEYS:-DEPLOY_PACKAGE_TOKEN,DEPLOY_GITHUB_TOKEN,GITHUB_TOKEN,GH_TOKEN,NPM_TOKEN,NODE_AUTH_TOKEN}"
|
|
29
34
|
RELEASE_VERIFY_SECONDARY_TOKEN_KEYS="${RELEASE_VERIFY_SECONDARY_TOKEN_KEYS:-DEPLOY_PACKAGE_SECONDARY_TOKEN,NPM_TOKEN,NODE_AUTH_TOKEN}"
|
|
30
35
|
TMP_NPMRC_FILES=()
|
|
36
|
+
TMP_MISC_FILES=()
|
|
31
37
|
|
|
32
38
|
case "$RELEASE_TYPE" in
|
|
33
39
|
patch|minor|major|prepatch|preminor|premajor|prerelease)
|
|
@@ -43,14 +49,19 @@ log() {
|
|
|
43
49
|
printf '[release] %s\n' "$*"
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
cleanup_tmp_files() {
|
|
47
53
|
for npmrc_tmp in "${TMP_NPMRC_FILES[@]:-}"; do
|
|
48
54
|
if [ -n "$npmrc_tmp" ] && [ -f "$npmrc_tmp" ]; then
|
|
49
55
|
rm -f "$npmrc_tmp"
|
|
50
56
|
fi
|
|
51
57
|
done
|
|
58
|
+
for tmp_file in "${TMP_MISC_FILES[@]:-}"; do
|
|
59
|
+
if [ -n "$tmp_file" ] && [ -f "$tmp_file" ]; then
|
|
60
|
+
rm -f "$tmp_file"
|
|
61
|
+
fi
|
|
62
|
+
done
|
|
52
63
|
}
|
|
53
|
-
trap
|
|
64
|
+
trap cleanup_tmp_files EXIT
|
|
54
65
|
|
|
55
66
|
require_cmd() {
|
|
56
67
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
@@ -217,6 +228,119 @@ compute_target_version() {
|
|
|
217
228
|
printf ''
|
|
218
229
|
}
|
|
219
230
|
|
|
231
|
+
resolve_previous_release_tag() {
|
|
232
|
+
local current_tag="$1"
|
|
233
|
+
(
|
|
234
|
+
cd "$PROJECT_ROOT" &&
|
|
235
|
+
git tag --list "${RELEASE_GITHUB_TAG_PREFIX}[0-9]*" --sort=-version:refname |
|
|
236
|
+
grep -Fvx "$current_tag" |
|
|
237
|
+
head -n 1
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
build_release_body_file() {
|
|
242
|
+
local current_tag="$1"
|
|
243
|
+
local target_ref="$2"
|
|
244
|
+
local body_file=""
|
|
245
|
+
body_file="$(mktemp /tmp/omnizap-release-body.XXXXXX.md)"
|
|
246
|
+
TMP_MISC_FILES+=("$body_file")
|
|
247
|
+
|
|
248
|
+
local previous_tag=""
|
|
249
|
+
previous_tag="$(resolve_previous_release_tag "$current_tag")"
|
|
250
|
+
|
|
251
|
+
local max_files=300
|
|
252
|
+
if [[ "$RELEASE_GITHUB_RELEASE_MAX_FILES" =~ ^[0-9]+$ ]] && [ "$RELEASE_GITHUB_RELEASE_MAX_FILES" -gt 0 ]; then
|
|
253
|
+
max_files="$RELEASE_GITHUB_RELEASE_MAX_FILES"
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
local -a changed_files=()
|
|
257
|
+
if [ -n "$previous_tag" ]; then
|
|
258
|
+
mapfile -t changed_files < <(
|
|
259
|
+
cd "$PROJECT_ROOT" &&
|
|
260
|
+
git diff --name-only --diff-filter=ACMRTUXB "${previous_tag}..${target_ref}" |
|
|
261
|
+
sed '/^$/d'
|
|
262
|
+
)
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
{
|
|
266
|
+
printf '## Arquivos alterados\n\n'
|
|
267
|
+
if [ -n "$previous_tag" ]; then
|
|
268
|
+
printf 'Comparação: `%s...%s`\n\n' "$previous_tag" "$current_tag"
|
|
269
|
+
else
|
|
270
|
+
printf 'Release inicial (sem tag anterior para comparação).\n\n'
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
if [ "${#changed_files[@]}" -eq 0 ]; then
|
|
274
|
+
printf -- '- Nenhum arquivo alterado detectado.\n'
|
|
275
|
+
else
|
|
276
|
+
local total="${#changed_files[@]}"
|
|
277
|
+
local limit="$total"
|
|
278
|
+
if [ "$total" -gt "$max_files" ]; then
|
|
279
|
+
limit="$max_files"
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
local i=0
|
|
283
|
+
while [ "$i" -lt "$limit" ]; do
|
|
284
|
+
printf -- '- `%s`\n' "${changed_files[$i]}"
|
|
285
|
+
i=$((i + 1))
|
|
286
|
+
done
|
|
287
|
+
|
|
288
|
+
if [ "$total" -gt "$max_files" ]; then
|
|
289
|
+
printf '\n_...e mais %s arquivo(s)._\n' "$((total - max_files))"
|
|
290
|
+
fi
|
|
291
|
+
fi
|
|
292
|
+
} > "$body_file"
|
|
293
|
+
|
|
294
|
+
printf '%s' "$body_file"
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
ensure_release_tag() {
|
|
298
|
+
local tag_name="$1"
|
|
299
|
+
local target_ref="$2"
|
|
300
|
+
|
|
301
|
+
local local_target_sha=""
|
|
302
|
+
local_target_sha="$(cd "$PROJECT_ROOT" && git rev-parse "${target_ref}^{}")"
|
|
303
|
+
|
|
304
|
+
if (cd "$PROJECT_ROOT" && git rev-parse --verify "refs/tags/${tag_name}" >/dev/null 2>&1); then
|
|
305
|
+
local local_tag_sha=""
|
|
306
|
+
local_tag_sha="$(cd "$PROJECT_ROOT" && git rev-parse "${tag_name}^{}")"
|
|
307
|
+
if [ "$local_tag_sha" != "$local_target_sha" ]; then
|
|
308
|
+
printf '[release] Tag %s já existe e aponta para outro commit (%s).\n' "$tag_name" "$local_tag_sha" >&2
|
|
309
|
+
exit 1
|
|
310
|
+
fi
|
|
311
|
+
log "Tag ${tag_name} já existe localmente."
|
|
312
|
+
else
|
|
313
|
+
if [ "$RELEASE_GIT_TAG_CREATE" != "1" ]; then
|
|
314
|
+
printf '[release] Tag %s não existe e RELEASE_GIT_TAG_CREATE=0.\n' "$tag_name" >&2
|
|
315
|
+
exit 1
|
|
316
|
+
fi
|
|
317
|
+
log "Criando tag ${tag_name}"
|
|
318
|
+
if [ "$RELEASE_GIT_TAG_ANNOTATED" = "1" ]; then
|
|
319
|
+
(cd "$PROJECT_ROOT" && git tag -a "$tag_name" -m "Release ${tag_name}" "$target_ref")
|
|
320
|
+
else
|
|
321
|
+
(cd "$PROJECT_ROOT" && git tag "$tag_name" "$target_ref")
|
|
322
|
+
fi
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
if [ "$RELEASE_GIT_TAG_PUSH" = "1" ]; then
|
|
326
|
+
local remote_sha=""
|
|
327
|
+
remote_sha="$(cd "$PROJECT_ROOT" && git ls-remote --tags "$RELEASE_GIT_REMOTE" "refs/tags/${tag_name}^{}" | awk 'NR==1{print $1}')"
|
|
328
|
+
if [ -z "$remote_sha" ]; then
|
|
329
|
+
remote_sha="$(cd "$PROJECT_ROOT" && git ls-remote --tags "$RELEASE_GIT_REMOTE" "refs/tags/${tag_name}" | awk 'NR==1{print $1}')"
|
|
330
|
+
fi
|
|
331
|
+
|
|
332
|
+
if [ -z "$remote_sha" ]; then
|
|
333
|
+
log "Enviando tag ${tag_name} para ${RELEASE_GIT_REMOTE}"
|
|
334
|
+
(cd "$PROJECT_ROOT" && git push "$RELEASE_GIT_REMOTE" "refs/tags/${tag_name}")
|
|
335
|
+
elif [ "$remote_sha" != "$local_target_sha" ]; then
|
|
336
|
+
printf '[release] Tag remota %s já existe e aponta para outro commit (%s).\n' "$tag_name" "$remote_sha" >&2
|
|
337
|
+
exit 1
|
|
338
|
+
else
|
|
339
|
+
log "Tag ${tag_name} já existe no remoto ${RELEASE_GIT_REMOTE}."
|
|
340
|
+
fi
|
|
341
|
+
fi
|
|
342
|
+
}
|
|
343
|
+
|
|
220
344
|
commit_and_push_if_dirty() {
|
|
221
345
|
local commit_message="$1"
|
|
222
346
|
|
|
@@ -320,6 +444,14 @@ if [ "$RELEASE_GIT_COMMIT_VERSION" = "1" ]; then
|
|
|
320
444
|
fi
|
|
321
445
|
|
|
322
446
|
release_tag="${RELEASE_GITHUB_TAG_PREFIX}${new_version}"
|
|
447
|
+
release_target_ref="$(cd "$PROJECT_ROOT" && git rev-parse HEAD)"
|
|
448
|
+
|
|
449
|
+
if [ -n "$(cd "$PROJECT_ROOT" && git status --porcelain --untracked-files=no)" ]; then
|
|
450
|
+
printf '[release] Working tree com alterações rastreadas antes de criar tag/release. Ajuste RELEASE_GIT_COMMIT_VERSION ou commite manualmente.\n' >&2
|
|
451
|
+
exit 1
|
|
452
|
+
fi
|
|
453
|
+
|
|
454
|
+
ensure_release_tag "$release_tag" "$release_target_ref"
|
|
323
455
|
|
|
324
456
|
if [ "$RELEASE_GITHUB_RELEASE" = "1" ]; then
|
|
325
457
|
if [ "$RELEASE_GIT_AUTO_PUSH" != "1" ]; then
|
|
@@ -330,7 +462,7 @@ if [ "$RELEASE_GITHUB_RELEASE" = "1" ]; then
|
|
|
330
462
|
local_name="${RELEASE_GITHUB_NAME_PREFIX}${new_version}"
|
|
331
463
|
local_target="$RELEASE_GITHUB_TARGET"
|
|
332
464
|
if [ -z "$local_target" ]; then
|
|
333
|
-
local_target="$
|
|
465
|
+
local_target="$release_target_ref"
|
|
334
466
|
fi
|
|
335
467
|
|
|
336
468
|
local_prerelease="$RELEASE_GITHUB_PRERELEASE"
|
|
@@ -342,23 +474,40 @@ if [ "$RELEASE_GITHUB_RELEASE" = "1" ]; then
|
|
|
342
474
|
fi
|
|
343
475
|
fi
|
|
344
476
|
|
|
345
|
-
|
|
477
|
+
generate_notes_bool=""
|
|
346
478
|
generate_notes_bool="$(to_bool "$RELEASE_GITHUB_GENERATE_NOTES")"
|
|
347
|
-
|
|
479
|
+
prerelease_bool=""
|
|
348
480
|
prerelease_bool="$(to_bool "$local_prerelease")"
|
|
349
|
-
|
|
481
|
+
draft_bool=""
|
|
350
482
|
draft_bool="$(to_bool "$RELEASE_GITHUB_DRAFT")"
|
|
483
|
+
release_body_file=""
|
|
484
|
+
if [ "$RELEASE_GITHUB_RELEASE_INCLUDE_CHANGED_FILES" = "1" ]; then
|
|
485
|
+
release_body_file="$(build_release_body_file "$release_tag" "$release_target_ref")"
|
|
486
|
+
fi
|
|
351
487
|
|
|
352
488
|
log "Criando/atualizando GitHub Release ($release_tag)"
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
489
|
+
if [ -n "$release_body_file" ]; then
|
|
490
|
+
release_output="$(
|
|
491
|
+
cd "$PROJECT_ROOT" && node ./scripts/github-release-notify.mjs upsert \
|
|
492
|
+
--tag "$release_tag" \
|
|
493
|
+
--target "$local_target" \
|
|
494
|
+
--name "$local_name" \
|
|
495
|
+
--body-file "$release_body_file" \
|
|
496
|
+
--generate-notes "$generate_notes_bool" \
|
|
497
|
+
--prerelease "$prerelease_bool" \
|
|
498
|
+
--draft "$draft_bool"
|
|
499
|
+
)"
|
|
500
|
+
else
|
|
501
|
+
release_output="$(
|
|
502
|
+
cd "$PROJECT_ROOT" && node ./scripts/github-release-notify.mjs upsert \
|
|
503
|
+
--tag "$release_tag" \
|
|
504
|
+
--target "$local_target" \
|
|
505
|
+
--name "$local_name" \
|
|
506
|
+
--generate-notes "$generate_notes_bool" \
|
|
507
|
+
--prerelease "$prerelease_bool" \
|
|
508
|
+
--draft "$draft_bool"
|
|
509
|
+
)"
|
|
510
|
+
fi
|
|
362
511
|
log "GitHub Release atualizado: $release_output"
|
|
363
512
|
fi
|
|
364
513
|
|
|
@@ -376,6 +525,33 @@ if [ "$RELEASE_VERIFY_UNIFIED_VERSION" = "1" ]; then
|
|
|
376
525
|
fi
|
|
377
526
|
log "Verificado localmente: versão=$local_version_now"
|
|
378
527
|
|
|
528
|
+
tag_commit_now="$(cd "$PROJECT_ROOT" && git rev-parse "${release_tag}^{}" 2>/dev/null || true)"
|
|
529
|
+
if [ -z "$tag_commit_now" ]; then
|
|
530
|
+
printf '[release] Tag local ausente: %s\n' "$release_tag" >&2
|
|
531
|
+
exit 1
|
|
532
|
+
fi
|
|
533
|
+
if [ "$tag_commit_now" != "$release_target_ref" ]; then
|
|
534
|
+
printf '[release] Tag local %s aponta para commit divergente (%s).\n' "$release_tag" "$tag_commit_now" >&2
|
|
535
|
+
exit 1
|
|
536
|
+
fi
|
|
537
|
+
log "Verificada tag local: ${release_tag} -> ${tag_commit_now}"
|
|
538
|
+
|
|
539
|
+
if [ "$RELEASE_GIT_TAG_PUSH" = "1" ]; then
|
|
540
|
+
remote_tag_sha="$(cd "$PROJECT_ROOT" && git ls-remote --tags "$RELEASE_GIT_REMOTE" "refs/tags/${release_tag}^{}" | awk 'NR==1{print $1}')"
|
|
541
|
+
if [ -z "$remote_tag_sha" ]; then
|
|
542
|
+
remote_tag_sha="$(cd "$PROJECT_ROOT" && git ls-remote --tags "$RELEASE_GIT_REMOTE" "refs/tags/${release_tag}" | awk 'NR==1{print $1}')"
|
|
543
|
+
fi
|
|
544
|
+
if [ -z "$remote_tag_sha" ]; then
|
|
545
|
+
printf '[release] Tag remota ausente: %s em %s\n' "$release_tag" "$RELEASE_GIT_REMOTE" >&2
|
|
546
|
+
exit 1
|
|
547
|
+
fi
|
|
548
|
+
if [ "$remote_tag_sha" != "$release_target_ref" ]; then
|
|
549
|
+
printf '[release] Tag remota %s divergente (%s).\n' "$release_tag" "$remote_tag_sha" >&2
|
|
550
|
+
exit 1
|
|
551
|
+
fi
|
|
552
|
+
log "Verificada tag remota: ${release_tag} -> ${remote_tag_sha}"
|
|
553
|
+
fi
|
|
554
|
+
|
|
379
555
|
verify_registry_version "$pkg_name" "$new_version" "$RELEASE_VERIFY_PRIMARY_REGISTRY" "$RELEASE_VERIFY_PRIMARY_TOKEN_KEYS" "1"
|
|
380
556
|
verify_registry_version "$pkg_name" "$new_version" "$RELEASE_VERIFY_SECONDARY_REGISTRY" "$RELEASE_VERIFY_SECONDARY_TOKEN_KEYS" "0"
|
|
381
557
|
|
package/RELEASE-v2.1.2.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# v2.1.3
|
|
2
|
-
|
|
3
|
-
Atualiza a versão para `2.1.3` e consolida o ciclo de entregas desde a `v2.1.2`.
|
|
4
|
-
|
|
5
|
-
Período do ciclo: `2026-02-26` a `2026-02-27`
|
|
6
|
-
Comparação base: `v2.1.2..HEAD`
|
|
7
|
-
Escopo: **39 commits** e **71 arquivos alterados**
|
|
8
|
-
|
|
9
|
-
## Destaques
|
|
10
|
-
|
|
11
|
-
- Plataforma web de stickers expandida de ponta a ponta: criação de packs no browser, upload de mídia (imagem/vídeo), fluxo de publicação e dashboard de criador.
|
|
12
|
-
- Marketplace e descoberta evoluídos: categorias, sorting, ranking de criadores, métricas globais, cards/UX mobile e melhorias de navegação.
|
|
13
|
-
- SEO e indexação: `sitemap`, ajustes de páginas SEO por pack e controles de exposição NSFW no catálogo.
|
|
14
|
-
- Classificação inteligente e curadoria automática reforçadas: pipeline CLIP/MobileCLIP/OpenCLIP, fila de reprocessamento, clustering semântico e otimizações do auto-pack por tags.
|
|
15
|
-
- Admin panel consolidado com moderação: gestão de bans e role de moderador, com persistência de sessão Google Web.
|
|
16
|
-
- Backend mais robusto: limpeza segura de órfãos, proteção de mutações, idempotência em uploads/publicação e novos caches para stats/sumários.
|
|
17
|
-
- Runtime e resiliência: reconexão de socket aprimorada, tratamento de rejeições transitórias e ajustes de agendamento em background.
|
|
18
|
-
- Banco e deploy: novas migrations para classificação/engajamento/workers/admin, e ajuste no nome do banco com sufixo por ambiente.
|
|
19
|
-
|
|
20
|
-
## Banco de dados e migrations
|
|
21
|
-
|
|
22
|
-
Este ciclo adiciona migrations estruturais importantes:
|
|
23
|
-
|
|
24
|
-
- `20260226_0011_sticker_asset_classification.sql`
|
|
25
|
-
- `20260226_0012_sticker_pack_engagement.sql`
|
|
26
|
-
- `20260226_0013_sticker_marketplace_intelligence.sql`
|
|
27
|
-
- `20260226_0014_sticker_pack_publish_flow.sql`
|
|
28
|
-
- `20260226_0014_sticker_worker_queues.sql`
|
|
29
|
-
- `20260226_0015_sticker_auto_pack_curation_integrity.sql`
|
|
30
|
-
- `20260226_0016_sticker_web_google_auth_persistence.sql`
|
|
31
|
-
- `20260226_0017_sticker_web_admin_ban.sql`
|
|
32
|
-
- `20260226_0018_sticker_web_admin_moderator.sql`
|
|
33
|
-
- `20260227_0019_sticker_classification_v2_signals.sql`
|
|
34
|
-
- `20260227_0020_semantic_theme_clusters.sql`
|
|
35
|
-
|
|
36
|
-
## Commits incluídos (v2.1.2..HEAD)
|
|
37
|
-
|
|
38
|
-
- `b46a33f` Improve background scheduling, add cached stats and UI/status enhancements
|
|
39
|
-
- `c43decb` Enable semantic clustering and improve auto-pack scheduler
|
|
40
|
-
- `91f2950` Enhance socket reconnection and prioritize complete sticker packs
|
|
41
|
-
- `64f8e73` Improve classification and auto-pack optimization
|
|
42
|
-
- `d11d78b` Appends environment-based suffix to DB name
|
|
43
|
-
- `b8ede5f` Add advanced auto-pack optimization and CLIP classification pipeline
|
|
44
|
-
- `32e169f` Add SEO, sitemap and NSFW gating to sticker catalog
|
|
45
|
-
- `7271a9b` Adds web links for sticker packs and sets auto-packs to unlisted
|
|
46
|
-
- `7cd1151` Ignore transient rejections and force process exit
|
|
47
|
-
- `5072a0e` Add admin moderator role and revamp admin panel UI
|
|
48
|
-
- `84c08dd` Add admin panel and ban management
|
|
49
|
-
- `27b387b` Persist Google web sessions and add client-side Google auth cache
|
|
50
|
-
- `6c07feb` Improve mobile category-chip touch and scroll behavior
|
|
51
|
-
- `c3ca0ea` Improve auto-pack-by-tags curation and pack metadata
|
|
52
|
-
- `3f6c258` Fix mobile discover tabs behavior
|
|
53
|
-
- `831c279` Adds marketplace global stats API and panel
|
|
54
|
-
- `08fdafe` Add catalog sorting, creators ranking and pack UI improvements
|
|
55
|
-
- `082ba82` Add safe pack management, orphan asset cleanup, and UX/network robustness
|
|
56
|
-
- `d928227` Improve marketplace discovery UX
|
|
57
|
-
- `97fe5f4` Bumps stickers app asset version
|
|
58
|
-
- `d2d1843` Adds sticker upload API and improves creator dashboard UX
|
|
59
|
-
- `be5506b` Adds creator pack management endpoints and dashboard UI
|
|
60
|
-
- `589f671` Add creator profile with Google sign-in
|
|
61
|
-
- `122c1e8` Switches CLIP classifier to MobileCLIP/OpenCLIP
|
|
62
|
-
- `7385d30` Allow spaces in pack names and add tag typeahead
|
|
63
|
-
- `2c5589c` Stops trimming input on field change
|
|
64
|
-
- `067b6f9` Add web pack publish flow with Google auth and idempotent uploads
|
|
65
|
-
- `6953768` Allow image/video uploads with WebP conversion
|
|
66
|
-
- `b33c1b8` Add web pack creation and sticker upload UI/API
|
|
67
|
-
- `2b23441` Add reprocess queue, cohesion scoring and ranking
|
|
68
|
-
- `6342d2b` Add marketplace stats endpoint and homepage preview
|
|
69
|
-
- `55cf8b7` Batch-scans and deduplicates catalog listings
|
|
70
|
-
- `7819fe6` Adds automatic sticker pack curation by tags
|
|
71
|
-
- `8ed61bc` Improve API docs and dynamic sidebar/UI for stickers
|
|
72
|
-
- `d0fd86d` Adds support contact API and enhances sticker catalog UI
|
|
73
|
-
- `c5cb106` Improves pack cards and mobile header UX
|
|
74
|
-
- `3da0375` Adds categories filtering and pack engagement tracking
|
|
75
|
-
- `d9bb604` Adds CLIP-based sticker classification
|
|
76
|
-
- `fffc205` Enhances catalog and API docs UI
|
|
77
|
-
|
|
78
|
-
## Notas de atualização
|
|
79
|
-
|
|
80
|
-
- `package.json` atualizado para `2.1.3`.
|
|
81
|
-
- `README.md` atualizado para refletir a versão atual.
|
|
82
|
-
- Recomendado reiniciar o processo de produção após deploy (`pm2 restart ... --update-env`).
|
|
83
|
-
- Atenção para comportamento de nome de banco por ambiente (`DB_NAME` com sufixo `_dev`/`_prod` quando aplicável).
|