Compare commits
No commits in common. "21ff8c0c4b13724cd0561718121659396a9b22fd" and "278bc6bd616b3fcab77d2f7f4b7f74d3b3a4e5e7" have entirely different histories.
21ff8c0c4b
...
278bc6bd61
@ -1,51 +0,0 @@
|
||||
journalctl -u ids-list-fetcher -n 50 --no-pager
|
||||
Jan 02 15:30:01 ids.alfacom.it ids-list-fetcher[9296]: Skipped (whitelisted): 0
|
||||
Jan 02 15:30:01 ids.alfacom.it ids-list-fetcher[9296]: ============================================================
|
||||
Jan 02 15:30:01 ids.alfacom.it systemd[1]: ids-list-fetcher.service: Deactivated successfully.
|
||||
Jan 02 15:30:01 ids.alfacom.it systemd[1]: Finished IDS Public Lists Fetcher Service.
|
||||
Jan 02 15:40:00 ids.alfacom.it systemd[1]: Starting IDS Public Lists Fetcher Service...
|
||||
Jan 02 15:40:00 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:00 ids.alfacom.it ids-list-fetcher[9493]: [2026-01-02 15:40:00] PUBLIC LISTS SYNC
|
||||
Jan 02 15:40:00 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:00 ids.alfacom.it ids-list-fetcher[9493]: Found 2 enabled lists
|
||||
Jan 02 15:40:00 ids.alfacom.it ids-list-fetcher[9493]: [15:40:00] Downloading Spamhaus from https://www.spamhaus.org/drop/drop_v4.json...
|
||||
Jan 02 15:40:00 ids.alfacom.it ids-list-fetcher[9493]: [15:40:00] Downloading AWS from https://ip-ranges.amazonaws.com/ip-ranges.json...
|
||||
Jan 02 15:40:00 ids.alfacom.it ids-list-fetcher[9493]: [15:40:00] Parsing AWS...
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: [15:40:01] Found 9548 IPs, syncing to database...
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: [15:40:01] ✓ AWS: +0 -0 ~9511
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: [15:40:01] Parsing Spamhaus...
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: [15:40:01] Found 1468 IPs, syncing to database...
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: [15:40:01] ✓ Spamhaus: +0 -0 ~1464
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: SYNC SUMMARY
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Success: 2/2
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Errors: 0/2
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Total IPs Added: 0
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Total IPs Removed: 0
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: RUNNING MERGE LOGIC
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ERROR:merge_logic:Failed to cleanup detections: operator does not exist: inet = text
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: LINE 9: d.source_ip::inet = wl.ip_inet
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ^
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ERROR:merge_logic:Failed to sync detections: operator does not exist: inet = text
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: LINE 29: bl.ip_inet = wl.ip_inet
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ^
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Traceback (most recent call last):
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: File "/opt/ids/python_ml/merge_logic.py", line 264, in sync_public_blacklist_detections
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: cur.execute("""
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: psycopg2.errors.UndefinedFunction: operator does not exist: inet = text
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: LINE 29: bl.ip_inet = wl.ip_inet
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ^
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Merge Logic Stats:
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Created detections: 0
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Cleaned invalid detections: 0
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: Skipped (whitelisted): 0
|
||||
Jan 02 15:40:01 ids.alfacom.it ids-list-fetcher[9493]: ============================================================
|
||||
Jan 02 15:40:01 ids.alfacom.it systemd[1]: ids-list-fetcher.service: Deactivated successfully.
|
||||
Jan 02 15:40:01 ids.alfacom.it systemd[1]: Finished IDS Public Lists Fetcher Service.
|
||||
@ -1,51 +0,0 @@
|
||||
journalctl -u ids-list-fetcher -n 50 --no-pager
|
||||
Jan 02 12:50:02 ids.alfacom.it ids-list-fetcher[5900]: ============================================================
|
||||
Jan 02 12:50:02 ids.alfacom.it systemd[1]: ids-list-fetcher.service: Deactivated successfully.
|
||||
Jan 02 12:50:02 ids.alfacom.it systemd[1]: Finished IDS Public Lists Fetcher Service.
|
||||
Jan 02 12:54:56 ids.alfacom.it systemd[1]: Starting IDS Public Lists Fetcher Service...
|
||||
Jan 02 12:54:56 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:56 ids.alfacom.it ids-list-fetcher[6290]: [2026-01-02 12:54:56] PUBLIC LISTS SYNC
|
||||
Jan 02 12:54:56 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:56 ids.alfacom.it ids-list-fetcher[6290]: Found 2 enabled lists
|
||||
Jan 02 12:54:56 ids.alfacom.it ids-list-fetcher[6290]: [12:54:56] Downloading Spamhaus from https://www.spamhaus.org/drop/drop_v4.json...
|
||||
Jan 02 12:54:56 ids.alfacom.it ids-list-fetcher[6290]: [12:54:56] Downloading AWS from https://ip-ranges.amazonaws.com/ip-ranges.json...
|
||||
Jan 02 12:54:56 ids.alfacom.it ids-list-fetcher[6290]: [12:54:56] Parsing AWS...
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: [12:54:57] Found 9548 IPs, syncing to database...
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: [12:54:57] ✓ AWS: +0 -0 ~9511
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: [12:54:57] Parsing Spamhaus...
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: [12:54:57] Found 1468 IPs, syncing to database...
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: [12:54:57] ✗ Spamhaus: ON CONFLICT DO UPDATE command cannot affect row a second time
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: HINT: Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: SYNC SUMMARY
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Success: 1/2
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Errors: 1/2
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Total IPs Added: 0
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Total IPs Removed: 0
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: RUNNING MERGE LOGIC
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ERROR:merge_logic:Failed to cleanup detections: operator does not exist: inet = text
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: LINE 9: d.source_ip::inet = wl.ip_inet
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ^
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ERROR:merge_logic:Failed to sync detections: operator does not exist: text <<= text
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: LINE 30: OR bl.ip_inet <<= wl.ip_inet
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ^
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Traceback (most recent call last):
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: File "/opt/ids/python_ml/merge_logic.py", line 264, in sync_public_blacklist_detections
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: cur.execute("""
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: psycopg2.errors.UndefinedFunction: operator does not exist: text <<= text
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: LINE 30: OR bl.ip_inet <<= wl.ip_inet
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ^
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Merge Logic Stats:
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Created detections: 0
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Cleaned invalid detections: 0
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: Skipped (whitelisted): 0
|
||||
Jan 02 12:54:57 ids.alfacom.it ids-list-fetcher[6290]: ============================================================
|
||||
Jan 02 12:54:57 ids.alfacom.it systemd[1]: ids-list-fetcher.service: Deactivated successfully.
|
||||
Jan 02 12:54:57 ids.alfacom.it systemd[1]: Finished IDS Public Lists Fetcher Service.
|
||||
@ -2,7 +2,7 @@
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
\restrict bpBxNz70Ka0m0tyhrhacCuMK1bx7vbLdZerc7LHt1LG4ZFdy4h6aJ0zgCj4XJhK
|
||||
\restrict 1YQEYYblTjhEF1oaDcpZ6W1vr63857XpsHsgqbm4Hu9qkogTgJA6Ph6OgNkIj16
|
||||
|
||||
-- Dumped from database version 16.11 (74c6bb6)
|
||||
-- Dumped by pg_dump version 16.10
|
||||
@ -387,5 +387,5 @@ ALTER TABLE ONLY public.public_blacklist_ips
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
\unrestrict bpBxNz70Ka0m0tyhrhacCuMK1bx7vbLdZerc7LHt1LG4ZFdy4h6aJ0zgCj4XJhK
|
||||
\unrestrict 1YQEYYblTjhEF1oaDcpZ6W1vr63857XpsHsgqbm4Hu9qkogTgJA6Ph6OgNkIj16
|
||||
|
||||
|
||||
@ -1,92 +0,0 @@
|
||||
-- Migration 008: Force INET/CIDR types (unconditional)
|
||||
-- Fixes issues where columns remained TEXT after conditional migration 007
|
||||
-- Date: 2026-01-02
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- FORCE DROP AND RECREATE ALL INET COLUMNS
|
||||
-- This is unconditional - always executes regardless of current state
|
||||
-- ============================================================================
|
||||
|
||||
-- Drop indexes first (if exist)
|
||||
DROP INDEX IF EXISTS public_blacklist_ip_inet_idx;
|
||||
DROP INDEX IF EXISTS public_blacklist_cidr_inet_idx;
|
||||
DROP INDEX IF EXISTS whitelist_ip_inet_idx;
|
||||
|
||||
-- ============================================================================
|
||||
-- FIX public_blacklist_ips TABLE
|
||||
-- ============================================================================
|
||||
|
||||
-- Drop columns unconditionally
|
||||
ALTER TABLE public_blacklist_ips DROP COLUMN IF EXISTS ip_inet;
|
||||
ALTER TABLE public_blacklist_ips DROP COLUMN IF EXISTS cidr_inet;
|
||||
|
||||
-- Recreate with correct INET/CIDR types
|
||||
ALTER TABLE public_blacklist_ips ADD COLUMN ip_inet inet;
|
||||
ALTER TABLE public_blacklist_ips ADD COLUMN cidr_inet cidr;
|
||||
|
||||
-- Populate from existing text data
|
||||
UPDATE public_blacklist_ips
|
||||
SET
|
||||
ip_inet = CASE
|
||||
WHEN ip_address ~ '/' THEN ip_address::inet
|
||||
ELSE ip_address::inet
|
||||
END,
|
||||
cidr_inet = CASE
|
||||
WHEN cidr_range IS NOT NULL AND cidr_range != '' THEN cidr_range::cidr
|
||||
WHEN ip_address ~ '/' THEN ip_address::cidr
|
||||
ELSE (ip_address || '/32')::cidr
|
||||
END
|
||||
WHERE ip_inet IS NULL;
|
||||
|
||||
-- Create GiST indexes for fast INET/CIDR containment operators
|
||||
CREATE INDEX public_blacklist_ip_inet_idx ON public_blacklist_ips USING gist(ip_inet inet_ops);
|
||||
CREATE INDEX public_blacklist_cidr_inet_idx ON public_blacklist_ips USING gist(cidr_inet inet_ops);
|
||||
|
||||
-- ============================================================================
|
||||
-- FIX whitelist TABLE
|
||||
-- ============================================================================
|
||||
|
||||
-- Drop column unconditionally
|
||||
ALTER TABLE whitelist DROP COLUMN IF EXISTS ip_inet;
|
||||
|
||||
-- Recreate with correct INET type
|
||||
ALTER TABLE whitelist ADD COLUMN ip_inet inet;
|
||||
|
||||
-- Populate from existing text data
|
||||
UPDATE whitelist
|
||||
SET ip_inet = CASE
|
||||
WHEN ip_address ~ '/' THEN ip_address::inet
|
||||
ELSE ip_address::inet
|
||||
END
|
||||
WHERE ip_inet IS NULL;
|
||||
|
||||
-- Create index for whitelist
|
||||
CREATE INDEX whitelist_ip_inet_idx ON whitelist USING gist(ip_inet inet_ops);
|
||||
|
||||
-- ============================================================================
|
||||
-- UPDATE SCHEMA VERSION
|
||||
-- ============================================================================
|
||||
|
||||
UPDATE schema_version SET version = 8, applied_at = NOW() WHERE id = 1;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================================
|
||||
-- VERIFICATION
|
||||
-- ============================================================================
|
||||
|
||||
SELECT 'Migration 008 completed successfully' as status;
|
||||
SELECT version, applied_at FROM schema_version WHERE id = 1;
|
||||
|
||||
-- Verify column types
|
||||
SELECT
|
||||
table_name,
|
||||
column_name,
|
||||
data_type
|
||||
FROM information_schema.columns
|
||||
WHERE
|
||||
(table_name = 'public_blacklist_ips' AND column_name IN ('ip_inet', 'cidr_inet'))
|
||||
OR (table_name = 'whitelist' AND column_name = 'ip_inet')
|
||||
ORDER BY table_name, column_name;
|
||||
@ -21,15 +21,13 @@ class ListParser:
|
||||
def normalize_cidr(ip_str: str) -> tuple[str, Optional[str]]:
|
||||
"""
|
||||
Normalize IP/CIDR to (ip_address, cidr_range)
|
||||
For CIDR ranges, use the full CIDR notation as ip_address to ensure uniqueness
|
||||
Example: '1.2.3.0/24' -> ('1.2.3.0/24', '1.2.3.0/24')
|
||||
Example: '1.2.3.0/24' -> ('1.2.3.0', '1.2.3.0/24')
|
||||
'1.2.3.4' -> ('1.2.3.4', None)
|
||||
"""
|
||||
try:
|
||||
network = ipaddress.ip_network(ip_str, strict=False)
|
||||
if '/' in ip_str:
|
||||
normalized_cidr = str(network)
|
||||
return (normalized_cidr, normalized_cidr)
|
||||
return (str(network.network_address), str(network))
|
||||
else:
|
||||
return (ip_str, None)
|
||||
except ValueError:
|
||||
|
||||
@ -30,7 +30,7 @@ The IDS employs a React-based frontend for real-time monitoring, detection visua
|
||||
- **Automatic Cleanup**: An hourly systemd timer (`cleanup_detections.py`) removes old detections (48h) and auto-unblocks IPs (2h).
|
||||
- **Service Monitoring & Management**: A dashboard provides real-time status (ML Backend, Database, Syslog Parser). API endpoints, secured with API key authentication and Systemd integration, allow for service management (start/stop/restart) of Python services.
|
||||
- **IP Geolocation**: Integration with `ip-api.com` enriches detection data with geographical and AS information, utilizing intelligent caching.
|
||||
- **Database Management**: PostgreSQL is used for all persistent data. An intelligent database versioning system ensures efficient SQL migrations (v8 with forced INET/CIDR column types for network range matching). Migration 008 unconditionally recreates INET columns to fix type mismatches. Dual-mode database drivers (`@neondatabase/serverless` for Replit, `pg` for AlmaLinux) ensure environment compatibility.
|
||||
- **Database Management**: PostgreSQL is used for all persistent data. An intelligent database versioning system ensures efficient SQL migrations (v7 with INET/CIDR columns for network range matching). Dual-mode database drivers (`@neondatabase/serverless` for Replit, `pg` for AlmaLinux) ensure environment compatibility.
|
||||
- **Microservices**: Clear separation of concerns between the Python ML backend and the Node.js API backend.
|
||||
- **UI/UX**: Utilizes ShadCN UI for a modern component library and `react-hook-form` with Zod for robust form validation. Analytics dashboards provide visualizations of normal and attack traffic, including real-time and historical data.
|
||||
|
||||
|
||||
@ -70,12 +70,10 @@ export const detections = pgTable("detections", {
|
||||
}));
|
||||
|
||||
// Whitelist per IP fidati
|
||||
// NOTE: ip_inet is INET type in production (managed by SQL migrations)
|
||||
// Drizzle lacks native INET support, so we use text() here
|
||||
export const whitelist = pgTable("whitelist", {
|
||||
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
ipAddress: text("ip_address").notNull().unique(),
|
||||
ipInet: text("ip_inet"), // Actually INET in production - see migration 008
|
||||
ipInet: text("ip_inet"),
|
||||
comment: text("comment"),
|
||||
reason: text("reason"),
|
||||
createdBy: text("created_by"),
|
||||
@ -158,14 +156,12 @@ export const publicLists = pgTable("public_lists", {
|
||||
}));
|
||||
|
||||
// Public blacklist IPs from external sources
|
||||
// NOTE: ip_inet/cidr_inet are INET/CIDR types in production (managed by SQL migrations)
|
||||
// Drizzle lacks native INET/CIDR support, so we use text() here
|
||||
export const publicBlacklistIps = pgTable("public_blacklist_ips", {
|
||||
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
ipAddress: text("ip_address").notNull(),
|
||||
cidrRange: text("cidr_range"),
|
||||
ipInet: text("ip_inet"), // Actually INET in production - see migration 008
|
||||
cidrInet: text("cidr_inet"), // Actually CIDR in production - see migration 008
|
||||
ipInet: text("ip_inet"),
|
||||
cidrInet: text("cidr_inet"),
|
||||
listId: varchar("list_id").notNull().references(() => publicLists.id, { onDelete: 'cascade' }),
|
||||
firstSeen: timestamp("first_seen").defaultNow().notNull(),
|
||||
lastSeen: timestamp("last_seen").defaultNow().notNull(),
|
||||
|
||||
16
version.json
16
version.json
@ -1,13 +1,7 @@
|
||||
{
|
||||
"version": "1.0.97",
|
||||
"lastUpdate": "2026-01-02T14:50:15.450Z",
|
||||
"version": "1.0.96",
|
||||
"lastUpdate": "2026-01-02T11:49:34.922Z",
|
||||
"changelog": [
|
||||
{
|
||||
"version": "1.0.97",
|
||||
"date": "2026-01-02",
|
||||
"type": "patch",
|
||||
"description": "Deployment automatico v1.0.97"
|
||||
},
|
||||
{
|
||||
"version": "1.0.96",
|
||||
"date": "2026-01-02",
|
||||
@ -301,6 +295,12 @@
|
||||
"date": "2025-11-24",
|
||||
"type": "patch",
|
||||
"description": "Deployment automatico v1.0.48"
|
||||
},
|
||||
{
|
||||
"version": "1.0.47",
|
||||
"date": "2025-11-24",
|
||||
"type": "patch",
|
||||
"description": "Deployment automatico v1.0.47"
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user