feat: add stopWords to delivery invitation RPC response
This commit is contained in:
parent
50f504c2ff
commit
dda8f8fe1f
|
|
@ -0,0 +1,126 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""SuperSam daily backup — export public tables to CSV, tar.gz, upload to S3."""
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
from botocore.config import Config
|
||||||
|
|
||||||
|
# S3 config
|
||||||
|
S3_ENDPOINT = "https://s3.ru1.storage.beget.cloud"
|
||||||
|
S3_KEY = "YG4MQNKAPNL65200MBUY"
|
||||||
|
S3_SECRET = "8mXkFM2VRQ3pN1Nx4mhmJ2jrZoB5YTPUa4CaZh43"
|
||||||
|
S3_BUCKET = "02f162ff4a18-supersam-s3"
|
||||||
|
|
||||||
|
# DB config
|
||||||
|
DB_HOST = "supabase-db"
|
||||||
|
DB_USER = "supabase_admin"
|
||||||
|
DB_NAME = "postgres"
|
||||||
|
|
||||||
|
def get_tables():
|
||||||
|
"""Get list of public tables with data."""
|
||||||
|
result = subprocess.run(
|
||||||
|
["docker", "exec", "-i", DB_HOST, "psql", "-U", DB_USER, "-d", DB_NAME,
|
||||||
|
"-t", "-A", "-c",
|
||||||
|
"SELECT tablename FROM pg_tables WHERE schemaname='public' ORDER BY tablename;"],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"ERROR getting tables: {result.stderr}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
tables = [t.strip() for t in result.stdout.strip().split("\n") if t.strip()]
|
||||||
|
return tables
|
||||||
|
|
||||||
|
def export_table_csv(table_name, out_dir):
|
||||||
|
"""Export a single table to CSV via psql \\copy."""
|
||||||
|
csv_path = os.path.join(out_dir, f"{table_name}.csv")
|
||||||
|
# Use psql \copy (client-side) to avoid needing superuser for COPY
|
||||||
|
cmd = [
|
||||||
|
"docker", "exec", "-i", DB_HOST,
|
||||||
|
"psql", "-U", DB_USER, "-d", DB_NAME,
|
||||||
|
"-c", f"\\copy (SELECT * FROM public.{table_name}) TO '/tmp/{table_name}.csv' WITH CSV HEADER;"
|
||||||
|
]
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"WARN: {table_name} export failed: {result.stderr}", file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Copy from container to host
|
||||||
|
cmd2 = ["docker", "cp", f"{DB_HOST}:/tmp/{table_name}.csv", csv_path]
|
||||||
|
result2 = subprocess.run(cmd2, capture_output=True, text=True)
|
||||||
|
if result2.returncode != 0:
|
||||||
|
print(f"WARN: {table_name} docker cp failed: {result2.stderr}", file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Clean up temp file in container
|
||||||
|
subprocess.run(["docker", "exec", "-i", DB_HOST, "rm", "-f", f"/tmp/{table_name}.csv"],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
|
||||||
|
# Check if file has data (more than just header)
|
||||||
|
with open(csv_path, "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
if len(lines) <= 1:
|
||||||
|
print(f" {table_name}: empty (skipping)")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f" {table_name}: {len(lines)-1} rows")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def upload_to_s3(file_path, key):
|
||||||
|
"""Upload file to S3."""
|
||||||
|
s3 = boto3.client(
|
||||||
|
"s3",
|
||||||
|
endpoint_url=S3_ENDPOINT,
|
||||||
|
aws_access_key_id=S3_KEY,
|
||||||
|
aws_secret_access_key=S3_SECRET,
|
||||||
|
config=Config(signature_version="s3v4"),
|
||||||
|
region_name="ru-1"
|
||||||
|
)
|
||||||
|
s3.upload_file(file_path, S3_BUCKET, key)
|
||||||
|
print(f" Uploaded: s3://{S3_BUCKET}/{key}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
date_str = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
archive_name = f"supersam-backup-{date_str}.tar.gz"
|
||||||
|
s3_key = f"backups/{archive_name}"
|
||||||
|
|
||||||
|
print(f"=== SuperSam Backup {date_str} ===")
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
tables = get_tables()
|
||||||
|
print(f"Found {len(tables)} tables")
|
||||||
|
|
||||||
|
exported = []
|
||||||
|
for table in tables:
|
||||||
|
if export_table_csv(table, tmpdir):
|
||||||
|
exported.append(table)
|
||||||
|
|
||||||
|
if not exported:
|
||||||
|
print("No tables with data — nothing to backup")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Create tar.gz
|
||||||
|
archive_path = os.path.join(tmpdir, archive_name)
|
||||||
|
with tarfile.open(archive_path, "w:gz") as tar:
|
||||||
|
for table in exported:
|
||||||
|
csv_path = os.path.join(tmpdir, f"{table}.csv")
|
||||||
|
tar.add(csv_path, arcname=f"{table}.csv")
|
||||||
|
|
||||||
|
# Get size
|
||||||
|
size_mb = os.path.getsize(archive_path) / (1024 * 1024)
|
||||||
|
print(f"Archive: {archive_name} ({size_mb:.2f} MB)")
|
||||||
|
|
||||||
|
# Upload
|
||||||
|
upload_to_s3(archive_path, s3_key)
|
||||||
|
|
||||||
|
print(f"=== Backup complete ===")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue