اضافه شدن قابلیت حافظه S3 به پروژه
Verify / test (push) Failing after 19s

This commit is contained in:
2026-11-09 13:32:00 +03:30
والد ea9fb855b3
کامیت a4e8f0c6c0
9فایلهای تغییر یافته به همراه439 افزوده شده و 37 حذف شده
+48
مشاهده پرونده
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Copy and verify existing private local objects into the configured S3 namespace.
No local objects or database rows are deleted or changed. Stop uploads/workers
before copying and keep them stopped until both switch to the same S3 settings.
Run with PYTHONPATH=backend and the service .env exported in the environment.
"""
import argparse
import hashlib
from pathlib import Path
from botocore.exceptions import ClientError
from app.config import settings
from app.storage import KEY, storage
def migrate(source_dir):
config=settings()
if config.storage_backend!='s3':raise ValueError('Configure STORAGE_BACKEND=s3 and S3_BUCKET before migrating')
root=Path(source_dir).resolve()/'objects'
if not root.is_dir() or root.is_symlink():raise ValueError('Source directory must contain the original objects directory (not a symlink)')
service=storage();copied=existing=0
for path in sorted(root.rglob('*')):
if path.is_symlink():raise ValueError('Symlinks are not permitted in the source object tree')
if not path.is_file():continue
key=path.relative_to(root).as_posix()
if not KEY.fullmatch(key):raise ValueError('Unrecognized source object; migration stopped without deleting any files')
data=path.read_bytes();digest=hashlib.sha256(data).digest()
try:remote=service.get(key)
except ClientError as exc:
if exc.response.get('Error',{}).get('Code') not in ('NoSuchKey','404','NotFound'):raise
else:
if hashlib.sha256(remote).digest()!=digest:
raise ValueError(f'Destination object differs; refusing to overwrite {key}')
existing+=1;continue
content_type='application/json' if key.endswith('.json') else 'image/svg+xml' if key.endswith('.svg') else 'application/octet-stream'
service.put(key,data,content_type)
if hashlib.sha256(service.get(key)).digest()!=digest:raise ValueError('Destination verification failed')
copied+=1
return {'copied_and_verified':copied,'already_identical':existing}
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--source-dir',type=Path,default=None,help='Old STORAGE_DIR, e.g. ./data or /data; must contain objects/')
args=parser.parse_args()
print(migrate(args.source_dir or settings().storage_dir))
if __name__=='__main__':main()