Art4Hotel Hub: código + documentación extensiva

ERP a la medida (Python stdlib + SQLite + vanilla JS SPA).
Incluye server.py, index.html, utilidades y documentación:
README, MODELO_DATOS, API, INSTALACION, CONTEXTO, NEGOCIO,
WEB, ONBOARDING, VALOR_SISTEMA, CLAUDE.

Secretos y datos (art4hotel.db, secret.key, ACCESOS.html,
uploads/, backups/) excluidos vía .gitignore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
consultoria-as
2026-06-09 00:10:07 -07:00
commit c2ae140078
16 changed files with 12675 additions and 0 deletions

90
backup.py Normal file
View File

@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""
Art4Hotel Hub — Backup diario
Crea snapshot del DB + uploads, conserva últimos N días.
Corre cada noche via cron.
"""
import sqlite3, shutil, datetime, tarfile, sys
from pathlib import Path
BASE = Path("/mnt/iclaude/art4hotel-hub")
DB = BASE / "art4hotel.db"
UPLOADS = BASE / "uploads"
BACKUPS = BASE / "backups"
KEEP_DAYS = 30
def log(msg):
ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"[{ts}] {msg}", flush=True)
def backup_db(out):
"""Use SQLite online backup (safe with WAL, doesn't lock writers)."""
src = sqlite3.connect(str(DB))
dst = sqlite3.connect(str(out))
with dst:
src.backup(dst)
src.close()
dst.close()
def backup_uploads(out_tar):
if not UPLOADS.exists():
return 0
n = 0
with tarfile.open(str(out_tar), "w:gz") as tar:
for p in UPLOADS.rglob("*"):
if p.is_file():
tar.add(str(p), arcname=str(p.relative_to(UPLOADS.parent)))
n += 1
return n
def prune_old():
cutoff = datetime.datetime.now() - datetime.timedelta(days=KEEP_DAYS)
removed = 0
for d in BACKUPS.iterdir():
if not d.is_dir():
continue
try:
date_part = d.name.split("_")[0]
d_date = datetime.datetime.strptime(date_part, "%Y-%m-%d")
if d_date < cutoff:
shutil.rmtree(d)
removed += 1
except Exception:
pass
return removed
def main():
BACKUPS.mkdir(exist_ok=True)
stamp = datetime.datetime.now().strftime("%Y-%m-%d_%H%M")
out_dir = BACKUPS / stamp
out_dir.mkdir(exist_ok=True)
log(f"Backup → {out_dir}")
try:
backup_db(out_dir / "art4hotel.db")
log(f" DB OK ({(out_dir/'art4hotel.db').stat().st_size//1024} KB)")
except Exception as e:
log(f" DB ERROR: {e}")
sys.exit(1)
try:
n = backup_uploads(out_dir / "uploads.tar.gz")
if (out_dir / "uploads.tar.gz").exists():
log(f" uploads OK ({n} archivos, {(out_dir/'uploads.tar.gz').stat().st_size//1024} KB)")
except Exception as e:
log(f" uploads ERROR: {e}")
removed = prune_old()
if removed:
log(f" Eliminados {removed} backup(s) > {KEEP_DAYS} días")
log("Listo.")
if __name__ == "__main__":
main()