191 lines
7.3 KiB
Python
191 lines
7.3 KiB
Python
from typing import Optional, Tuple, Dict
|
|
import os
|
|
import logging
|
|
from urllib.request import urlopen
|
|
|
|
try:
|
|
from minio import Minio # type: ignore
|
|
import urllib3 # type: ignore
|
|
except Exception:
|
|
Minio = None
|
|
urllib3 = None # type: ignore
|
|
|
|
def minio_head_bucket(client: object, bucket: str) -> bool:
|
|
try:
|
|
if hasattr(client, "bucket_exists"):
|
|
try:
|
|
return bool(client.bucket_exists(bucket)) # type: ignore
|
|
except Exception:
|
|
pass
|
|
try:
|
|
region = client._get_region(bucket) # type: ignore
|
|
except Exception:
|
|
region = "us-east-1"
|
|
client._url_open(method="HEAD", region=region, bucket_name=bucket) # type: ignore
|
|
return True
|
|
except Exception:
|
|
try:
|
|
names = [getattr(b, "name", None) for b in client.list_buckets()] # type: ignore
|
|
return bucket in set(n for n in names if n)
|
|
except Exception:
|
|
return False
|
|
|
|
def minio_create_bucket(client: object, bucket: str) -> bool:
|
|
try:
|
|
if hasattr(client, "bucket_exists"):
|
|
try:
|
|
if client.bucket_exists(bucket): # type: ignore
|
|
return True
|
|
except Exception:
|
|
pass
|
|
if hasattr(client, "make_bucket"):
|
|
try:
|
|
client.make_bucket(bucket) # type: ignore
|
|
return True
|
|
except Exception:
|
|
try:
|
|
region = client._get_region(bucket) # type: ignore
|
|
except Exception:
|
|
region = "us-east-1"
|
|
try:
|
|
client.make_bucket(bucket, location=region) # type: ignore
|
|
return True
|
|
except Exception:
|
|
pass
|
|
try:
|
|
try:
|
|
region = client._get_region(bucket) # type: ignore
|
|
except Exception:
|
|
region = "us-east-1"
|
|
client._url_open(method="PUT", region=region, bucket_name=bucket) # type: ignore
|
|
return True
|
|
except Exception as ce:
|
|
if "BucketAlreadyOwnedByYou" in str(ce) or "BucketAlreadyExists" in str(ce):
|
|
return True
|
|
raise
|
|
except Exception as e:
|
|
raise e
|
|
|
|
def minio_client(endpoint: str, access: str, secret: str, secure: bool):
|
|
if urllib3 is not None:
|
|
try:
|
|
http = urllib3.PoolManager(timeout=urllib3.Timeout(connect=3.0, read=20.0))
|
|
return Minio(endpoint=endpoint, access_key=access, secret_key=secret, secure=secure, http_client=http) # type: ignore
|
|
except Exception:
|
|
return Minio(endpoint=endpoint, access_key=access, secret_key=secret, secure=secure) # type: ignore
|
|
return Minio(endpoint=endpoint, access_key=access, secret_key=secret, secure=secure) # type: ignore
|
|
|
|
def minio_time_hint(endpoint: str, secure: bool) -> Optional[str]:
|
|
try:
|
|
scheme = "https" if secure else "http"
|
|
r = urlopen(f"{scheme}://{endpoint}", timeout=3)
|
|
srv_date = r.headers.get("Date")
|
|
if not srv_date:
|
|
return None
|
|
from email.utils import parsedate_to_datetime
|
|
from datetime import datetime, timezone
|
|
dt = parsedate_to_datetime(srv_date)
|
|
now = datetime.now(timezone.utc)
|
|
diff = abs((now - dt).total_seconds())
|
|
return f"服务器时间与本机相差约 {int(diff)} 秒"
|
|
except Exception:
|
|
return None
|
|
|
|
def join_prefix(prefix: str, rel: str) -> str:
|
|
pre = (prefix or "").strip("/")
|
|
r = rel.lstrip("/")
|
|
if pre and r.startswith(pre + "/"):
|
|
return r
|
|
return f"{pre}/{r}" if pre else r
|
|
|
|
def presigned_read(client: object, bucket: str, obj: str, expires_seconds: int) -> Optional[str]:
|
|
try:
|
|
from datetime import timedelta
|
|
exp = expires_seconds
|
|
try:
|
|
exp = int(exp)
|
|
except Exception:
|
|
pass
|
|
td = timedelta(seconds=exp)
|
|
try:
|
|
return client.get_presigned_url("GET", bucket, obj, expires=td) # type: ignore
|
|
except Exception:
|
|
return client.presigned_get_object(bucket, obj, expires=td) # type: ignore
|
|
except Exception:
|
|
return None
|
|
|
|
def minio_current(runtime_cfg: Dict[str, Dict[str, Optional[str]]]) -> Tuple[Optional[object], Optional[str], Optional[str], str]:
|
|
rc = runtime_cfg.get("minio", {})
|
|
endpoint_raw = rc.get("endpoint") or os.environ.get("MINIO_ENDPOINT")
|
|
access_raw = rc.get("access") or os.environ.get("MINIO_ACCESS_KEY")
|
|
secret_raw = rc.get("secret") or os.environ.get("MINIO_SECRET_KEY")
|
|
bucket_raw = rc.get("bucket") or os.environ.get("MINIO_BUCKET")
|
|
secure_flag = rc.get("secure") or os.environ.get("MINIO_SECURE", "false")
|
|
secure = str(secure_flag or "false").lower() in {"1","true","yes","on"}
|
|
public_raw = rc.get("public") or os.environ.get("MINIO_PUBLIC_ENDPOINT")
|
|
endpoint = (str(endpoint_raw).strip() if endpoint_raw else None)
|
|
try:
|
|
if isinstance(endpoint, str) and ":9001" in endpoint:
|
|
h = endpoint.split("/")[0]
|
|
if ":" in h:
|
|
parts = h.split(":")
|
|
endpoint = f"{parts[0]}:9000"
|
|
else:
|
|
endpoint = h
|
|
except Exception:
|
|
endpoint = endpoint
|
|
access = (str(access_raw).strip() if access_raw else None)
|
|
secret = (str(secret_raw).strip() if secret_raw else None)
|
|
bucket = (str(bucket_raw).strip() if bucket_raw else None)
|
|
public_base = (str(public_raw).strip() if public_raw else None)
|
|
try:
|
|
if isinstance(public_base, str) and (":9001" in public_base or "/browser" in public_base or "/minio" in public_base):
|
|
host = public_base.strip().split("/")[0]
|
|
scheme = "https" if secure else "http"
|
|
if ":" in host:
|
|
host = host.split("/")[0]
|
|
base_host = host.split(":")[0]
|
|
public_base = f"{scheme}://{base_host}:9000"
|
|
else:
|
|
public_base = f"{scheme}://{host}:9000"
|
|
except Exception:
|
|
public_base = public_base
|
|
if not public_base and endpoint:
|
|
public_base = f"https://{endpoint}" if secure else f"http://{endpoint}"
|
|
missing = []
|
|
if Minio is None:
|
|
missing.append("client")
|
|
if not endpoint:
|
|
missing.append("endpoint")
|
|
if not access:
|
|
missing.append("access")
|
|
if not secret:
|
|
missing.append("secret")
|
|
if not bucket:
|
|
missing.append("bucket")
|
|
if not public_base:
|
|
missing.append("public")
|
|
if missing:
|
|
try:
|
|
logging.error(f"minio config invalid: missing={missing}")
|
|
except Exception:
|
|
pass
|
|
return None, None, None, ""
|
|
client = minio_client(endpoint=endpoint, access=access, secret=secret, secure=secure)
|
|
try:
|
|
try:
|
|
client.list_buckets() # type: ignore
|
|
except Exception as e:
|
|
if secure and ("SSL" in str(e) or "HTTPSConnectionPool" in str(e) or "SSLError" in str(e)):
|
|
client = minio_client(endpoint=endpoint, access=access, secret=secret, secure=False)
|
|
except Exception:
|
|
pass
|
|
try:
|
|
exists = minio_head_bucket(client, bucket)
|
|
if not exists:
|
|
minio_create_bucket(client, bucket)
|
|
except Exception:
|
|
pass
|
|
prefix = rc.get("prefix") or os.environ.get("MINIO_PREFIX", "")
|
|
return client, bucket, public_base, prefix
|