[image-service] updated image upload backup, rollback, commit files
This commit is contained in:
parent
fe75c5b9ed
commit
64e9decf97
2 changed files with 135 additions and 37 deletions
|
|
@ -8,6 +8,8 @@ services:
|
||||||
- "8125:8125"
|
- "8125:8125"
|
||||||
environment:
|
environment:
|
||||||
- PYTHONUNBUFFERED=1
|
- PYTHONUNBUFFERED=1
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
volumes:
|
volumes:
|
||||||
- shared-repo:/usr/src/app
|
- shared-repo:/usr/src/app
|
||||||
# - ~/repo/taobin_project:/taobin_project
|
# - ~/repo/taobin_project:/taobin_project
|
||||||
|
|
|
||||||
170
main.py
170
main.py
|
|
@ -12,7 +12,10 @@ app = FastAPI()
|
||||||
BASE_DIR = Path("/usr/src/app/taobin_project")
|
BASE_DIR = Path("/usr/src/app/taobin_project")
|
||||||
# BASE_DIR = Path("/taobin_project")
|
# BASE_DIR = Path("/taobin_project")
|
||||||
|
|
||||||
|
SERVICE_NAME = os.getenv("SERVICE_NAME")
|
||||||
|
|
||||||
GIT_REPO_SERVER_URL = os.getenv("GIT_REPO_SERVER_URL")
|
GIT_REPO_SERVER_URL = os.getenv("GIT_REPO_SERVER_URL")
|
||||||
|
FRONTEND_NOTIFY_URL = os.getenv("FRONTEND_NOTIFY_URL")
|
||||||
|
|
||||||
ALLOWED_FOLDERS = {"page_drink",
|
ALLOWED_FOLDERS = {"page_drink",
|
||||||
"page_drink_disable",
|
"page_drink_disable",
|
||||||
|
|
@ -104,7 +107,7 @@ async def commit_files_to_git(
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
git_server_url,
|
git_server_url + "/commit",
|
||||||
data=commit_data,
|
data=commit_data,
|
||||||
files=multipart_files,
|
files=multipart_files,
|
||||||
timeout=30.0
|
timeout=30.0
|
||||||
|
|
@ -112,6 +115,62 @@ async def commit_files_to_git(
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
|
def backup_existing_file(dest: Path) -> Optional[Path]:
|
||||||
|
if dest.exists():
|
||||||
|
backup_path = dest.with_suffix(dest.suffix + f".bak.{uuid.uuid4().hex[:8]}")
|
||||||
|
shutil.copy2(dest, backup_path)
|
||||||
|
print(f"[BACKUP] {dest.name} → {backup_path.name}")
|
||||||
|
return backup_path
|
||||||
|
return None
|
||||||
|
|
||||||
|
def rollback_files(saved_files: list[dict], backups: dict[str, Optional[Path]], folder: str, country: Optional[str] = None):
|
||||||
|
for item in saved_files:
|
||||||
|
filename = item["filename"]
|
||||||
|
dest = get_image_dir(folder, country) / filename
|
||||||
|
backup = backups.get(filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if backup and backup.exists():
|
||||||
|
if dest.exists():
|
||||||
|
dest.unlink()
|
||||||
|
shutil.move(str(backup), str(dest))
|
||||||
|
print(f"[ROLLBACK] Restored: {filename}")
|
||||||
|
else:
|
||||||
|
if dest.exists():
|
||||||
|
dest.unlink()
|
||||||
|
print(f"[ROLLBACK] Deleted new file: {filename}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ROLLBACK ERROR] {filename}: {e}")
|
||||||
|
|
||||||
|
def cleanup_backups(backups: dict[str, Optional[Path]]):
|
||||||
|
for filename, backup in backups.items():
|
||||||
|
if backup and backup.exists():
|
||||||
|
try:
|
||||||
|
backup.unlink()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[CLEANUP ERROR] {backup}: {e}")
|
||||||
|
|
||||||
|
async def notify_frontend(uid: str, msg: str):
|
||||||
|
if not FRONTEND_NOTIFY_URL:
|
||||||
|
print(f"[NOTIFY SKIP] FRONTEND_NOTIFY_URL not set")
|
||||||
|
return
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"type": "notify",
|
||||||
|
"payload": {
|
||||||
|
"from": SERVICE_NAME,
|
||||||
|
"level": "error",
|
||||||
|
"to": uid,
|
||||||
|
"msg": msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
await client.post(FRONTEND_NOTIFY_URL, json=payload, timeout=10.0)
|
||||||
|
print(f"[NOTIFY SEND SUCCESS] {payload}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[NOTIFY ERROR] Failed to notify frontend: {e}")
|
||||||
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────
|
# ─────────────────────────────────────────
|
||||||
# UPLOAD
|
# UPLOAD
|
||||||
|
|
@ -132,7 +191,7 @@ async def upload_images(
|
||||||
"country": "tha"
|
"country": "tha"
|
||||||
}
|
}
|
||||||
|
|
||||||
if not (country):
|
if not (required_user_fields.get("country")):
|
||||||
raise HTTPException(status_code=400, detail="Invalid country")
|
raise HTTPException(status_code=400, detail="Invalid country")
|
||||||
|
|
||||||
for field, value in required_user_fields.items():
|
for field, value in required_user_fields.items():
|
||||||
|
|
@ -144,25 +203,43 @@ async def upload_images(
|
||||||
|
|
||||||
validate_folder(folder)
|
validate_folder(folder)
|
||||||
saved = []
|
saved = []
|
||||||
for file in files:
|
backups: dict[str, Optional[Path]] = {}
|
||||||
ext = validate_ext(file.filename)
|
|
||||||
filename = Path(file.filename).name
|
|
||||||
dest = get_image_dir(folder) / filename
|
|
||||||
with open(dest, "wb") as f:
|
|
||||||
shutil.copyfileobj(file.file, f)
|
|
||||||
saved.append({
|
|
||||||
"filename": filename,
|
|
||||||
"url": f"/image/{folder}/{filename}"
|
|
||||||
})
|
|
||||||
|
|
||||||
# Commit to Git
|
try:
|
||||||
git_response = await commit_files_to_git(
|
for file in files:
|
||||||
files=files,
|
ext = validate_ext(file.filename)
|
||||||
folder=folder,
|
filename = Path(file.filename).name
|
||||||
display_name=required_user_fields.get("displayname"),
|
dest = get_image_dir(folder) / filename
|
||||||
email=required_user_fields.get("email"),
|
|
||||||
country=required_user_fields.get("country")
|
backups[filename] = backup_existing_file(dest)
|
||||||
)
|
|
||||||
|
with open(dest, "wb") as f:
|
||||||
|
shutil.copyfileobj(file.file, f)
|
||||||
|
saved.append({
|
||||||
|
"filename": filename,
|
||||||
|
"url": f"/image/{folder}/{filename}"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
rollback_files(saved, backups, folder)
|
||||||
|
error_msg = f"Save image failed: {str(e)}"
|
||||||
|
await notify_frontend(uid=uid, msg=error_msg)
|
||||||
|
raise HTTPException(status_code=500, detail=error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
git_response = await commit_files_to_git(
|
||||||
|
files=files,
|
||||||
|
folder=folder,
|
||||||
|
display_name=displayname,
|
||||||
|
email=email,
|
||||||
|
country=required_user_fields.get("country")
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
rollback_files(saved, backups, folder)
|
||||||
|
error_msg = f"Git commit failed: {str(e)}"
|
||||||
|
await notify_frontend(uid=uid, msg=error_msg)
|
||||||
|
raise HTTPException(status_code=502, detail=f"{error_msg}, files rolled back")
|
||||||
|
|
||||||
|
cleanup_backups(backups)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"uploaded": saved,
|
"uploaded": saved,
|
||||||
|
|
@ -199,24 +276,43 @@ async def upload_inter_images(
|
||||||
|
|
||||||
validate_folder(folder)
|
validate_folder(folder)
|
||||||
saved = []
|
saved = []
|
||||||
for file in files:
|
backups: dict[str, Optional[Path]] = {}
|
||||||
ext = validate_ext(file.filename)
|
|
||||||
filename = Path(file.filename).name
|
|
||||||
dest = get_image_dir(folder, country) / filename
|
|
||||||
with open(dest, "wb") as f:
|
|
||||||
shutil.copyfileobj(file.file, f)
|
|
||||||
saved.append({
|
|
||||||
"filename": filename,
|
|
||||||
"url": f"/inter/{country}/image/{folder}/{filename}"
|
|
||||||
})
|
|
||||||
|
|
||||||
git_response = await commit_files_to_git(
|
try:
|
||||||
files=files,
|
for file in files:
|
||||||
folder=folder,
|
ext = validate_ext(file.filename)
|
||||||
display_name=required_user_fields.get("displayname"),
|
filename = Path(file.filename).name
|
||||||
email=required_user_fields.get("email"),
|
dest = get_image_dir(folder, country) / filename
|
||||||
country=required_user_fields.get("country")
|
|
||||||
)
|
backups[filename] = backup_existing_file(dest)
|
||||||
|
|
||||||
|
with open(dest, "wb") as f:
|
||||||
|
shutil.copyfileobj(file.file, f)
|
||||||
|
saved.append({
|
||||||
|
"filename": filename,
|
||||||
|
"url": f"/inter/{country}/image/{folder}/{filename}"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
rollback_files(saved, backups, folder, country)
|
||||||
|
error_msg = f"Save image failed: {str(e)}"
|
||||||
|
await notify_frontend(uid=uid, msg=error_msg)
|
||||||
|
raise HTTPException(status_code=500, detail=error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
git_response = await commit_files_to_git(
|
||||||
|
files=files,
|
||||||
|
folder=folder,
|
||||||
|
display_name=displayname,
|
||||||
|
email=email,
|
||||||
|
country=country
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
rollback_files(saved, backups, folder, country)
|
||||||
|
error_msg = f"Git commit failed: {str(e)}"
|
||||||
|
await notify_frontend(uid=uid, msg=error_msg)
|
||||||
|
raise HTTPException(status_code=502, detail=f"{error_msg}, files rolled back")
|
||||||
|
|
||||||
|
cleanup_backups(backups)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"uploaded": saved,
|
"uploaded": saved,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue