api fixes. CHAT container NEEDS ATTENTION
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
32
scripts/fix_auth_response_model.sh
Executable file
32
scripts/fix_auth_response_model.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
set -euo pipefail
|
||||
FILE="services/auth/src/app/schemas/user.py"
|
||||
[ -f "$FILE" ] || { echo "Not found: $FILE"; exit 1; }
|
||||
|
||||
python3 - "$FILE" <<'PY'
|
||||
from pathlib import Path
|
||||
import re, sys
|
||||
p = Path(sys.argv[1]); s = p.read_text()
|
||||
|
||||
if "from uuid import UUID" not in s:
|
||||
s = "from uuid import UUID\n" + s
|
||||
|
||||
s = re.sub(r'(\bid\s*:\s*)str', r'\1UUID', s)
|
||||
s = re.sub(r'(\buser_id\s*:\s*)str', r'\1UUID', s)
|
||||
|
||||
# Включаем from_attributes (pydantic v2) или orm_mode (v1) для моделей ответа
|
||||
if "model_config" not in s and "orm_mode" not in s:
|
||||
s = re.sub(
|
||||
r"class\s+\w+Out\s*\((?:.|\n)*?\):",
|
||||
lambda m: m.group(0) +
|
||||
"\n try:\n from pydantic import ConfigDict\n model_config = ConfigDict(from_attributes=True)\n"
|
||||
" except Exception:\n class Config:\n orm_mode = True\n",
|
||||
s
|
||||
)
|
||||
|
||||
p.write_text(s)
|
||||
print("[auth] Patched:", p)
|
||||
PY
|
||||
|
||||
echo "[auth] Rebuild & restart…"
|
||||
docker compose build auth
|
||||
docker compose restart auth
|
||||
117
scripts/fix_chat_uuid_schemas.sh
Executable file
117
scripts/fix_chat_uuid_schemas.sh
Executable file
@@ -0,0 +1,117 @@
|
||||
set -euo pipefail
|
||||
|
||||
svc_dir="services/chat/src/app/schemas"
|
||||
test -d "$svc_dir" || { echo "Not found: $svc_dir"; exit 1; }
|
||||
|
||||
python3 - <<'PY'
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
root = Path("services/chat/src/app/schemas")
|
||||
|
||||
def ensure_future_and_uuid(text: str) -> str:
|
||||
lines = text.splitlines(keepends=True)
|
||||
changed = False
|
||||
|
||||
# Удалим дубли future-импорта
|
||||
fut_pat = re.compile(r'^\s*from\s+__future__\s+import\s+annotations\s*(#.*)?$\n?', re.M)
|
||||
if fut_pat.search(text):
|
||||
lines = [ln for ln in lines if not fut_pat.match(ln)]
|
||||
changed = True
|
||||
|
||||
# Вставим future после докстринга (если он есть)
|
||||
i = 0
|
||||
while i < len(lines) and (lines[i].strip()=="" or lines[i].lstrip().startswith("#!") or re.match(r'^\s*#.*coding[:=]', lines[i])):
|
||||
i += 1
|
||||
insert_at = i
|
||||
if i < len(lines) and re.match(r'^\s*[rubfRUBF]*[\'"]{3}', lines[i]): # докстринг
|
||||
q = '"""' if '"""' in lines[i] else "'''"
|
||||
j = i
|
||||
while j < len(lines):
|
||||
if q in lines[j] and j != i:
|
||||
j += 1
|
||||
break
|
||||
j += 1
|
||||
insert_at = min(j, len(lines))
|
||||
|
||||
if not any("from __future__ import annotations" in ln for ln in lines):
|
||||
lines.insert(insert_at, "from __future__ import annotations\n")
|
||||
changed = True
|
||||
|
||||
txt = "".join(lines)
|
||||
if "from uuid import UUID" not in txt:
|
||||
# вставим сразу после future
|
||||
# найдём позицию future
|
||||
pos = next((k for k,ln in enumerate(lines) if "from __future__ import annotations" in ln), 0)
|
||||
lines.insert(pos+1, "from uuid import UUID\n")
|
||||
changed = True
|
||||
|
||||
return "".join(lines), changed
|
||||
|
||||
def set_config(text: str) -> str:
|
||||
# Pydantic v2: добавим ConfigDict и model_config, если их нет
|
||||
if "ConfigDict" not in text:
|
||||
text = re.sub(r'from pydantic import ([^\n]+)',
|
||||
lambda m: m.group(0).replace(m.group(1), (m.group(1) + ", ConfigDict").replace(", ConfigDict,"," , ConfigDict,")),
|
||||
text, count=1) if "from pydantic import" in text else ("from pydantic import BaseModel, ConfigDict\n" + text)
|
||||
|
||||
# В каждую модель, наследующую BaseModel, которая заканчивается на Read/Out, добавим from_attributes
|
||||
def inject_cfg(block: str) -> str:
|
||||
# если уже есть model_config/orm_mode — не трогаем
|
||||
if "model_config" in block or "class Config" in block:
|
||||
return block
|
||||
# аккуратно добавим model_config сверху класса
|
||||
header, body = block.split(":", 1)
|
||||
return header + ":\n model_config = ConfigDict(from_attributes=True)\n" + body
|
||||
|
||||
# Разобьём по классам
|
||||
parts = re.split(r'(\nclass\s+[A-Za-z0-9_]+\s*\([^\)]*BaseModel[^\)]*\)\s*:)', text)
|
||||
if len(parts) > 1:
|
||||
out = [parts[0]]
|
||||
for i in range(1, len(parts), 2):
|
||||
head = parts[i]
|
||||
body = parts[i+1]
|
||||
m = re.search(r'class\s+([A-Za-z0-9_]+)\s*\(', head)
|
||||
name = m.group(1) if m else ""
|
||||
if name.endswith(("Read","Out")):
|
||||
out.append(inject_cfg(head + body))
|
||||
else:
|
||||
out.append(head + body)
|
||||
text = "".join(out)
|
||||
return text
|
||||
|
||||
def fix_uuid_fields(text: str) -> str:
|
||||
# Преобразуем только в классах Read/Out
|
||||
def repl_in_class(cls_text: str) -> str:
|
||||
# заменяем аннотации полей
|
||||
repl = {
|
||||
r'(^\s*)(id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(pair_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(room_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(sender_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(user_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
}
|
||||
for pat, sub in repl.items():
|
||||
cls_text = re.sub(pat, sub, cls_text, flags=re.M)
|
||||
return cls_text
|
||||
|
||||
patt = re.compile(r'(\nclass\s+[A-Za-z0-9_]+(Read|Out)\s*\([^\)]*BaseModel[^\)]*\)\s*:\n(?:\s+.*\n)+)', re.M)
|
||||
return patt.sub(lambda m: repl_in_class(m.group(1)), text)
|
||||
|
||||
changed_any = False
|
||||
for p in root.glob("**/*.py"):
|
||||
txt = p.read_text()
|
||||
new, c1 = ensure_future_and_uuid(txt)
|
||||
new = set_config(new)
|
||||
new2 = fix_uuid_fields(new)
|
||||
if new2 != txt:
|
||||
p.write_text(new2)
|
||||
print("fixed:", p)
|
||||
changed_any = True
|
||||
|
||||
print("DONE" if changed_any else "NO-CHANGES")
|
||||
PY
|
||||
|
||||
echo "[docker] rebuild & restart chat…"
|
||||
docker compose build --no-cache chat >/dev/null
|
||||
docker compose restart chat
|
||||
93
scripts/fix_future_imports.sh
Executable file
93
scripts/fix_future_imports.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
set -euo pipefail
|
||||
|
||||
python3 - <<'PY'
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
def fix_file(p: Path) -> bool:
|
||||
s = p.read_text()
|
||||
lines = s.splitlines(keepends=True)
|
||||
changed = False
|
||||
|
||||
# собрать все строки future-импортов и убрать их
|
||||
fut_pat = re.compile(r'^\s*from\s+__future__\s+import\s+annotations\s*(#.*)?$\n?', re.M)
|
||||
if fut_pat.search(s):
|
||||
new_lines = []
|
||||
for ln in lines:
|
||||
if not fut_pat.match(ln):
|
||||
new_lines.append(ln)
|
||||
lines = new_lines
|
||||
changed = True
|
||||
|
||||
# определить позицию вставки future (сразу после верхнего докстринга, если он есть)
|
||||
i = 0
|
||||
# пропускаем пустые строки и комментарии/кодировки/шейбанг
|
||||
while i < len(lines) and (
|
||||
lines[i].strip() == "" or
|
||||
lines[i].lstrip().startswith("#!") or
|
||||
re.match(r'^\s*#.*coding[:=]', lines[i])
|
||||
):
|
||||
i += 1
|
||||
|
||||
insert_at = i
|
||||
# если на вершине модульный докстринг ("""...""" или '''...''')
|
||||
if i < len(lines) and re.match(r'^\s*[rubfRUBF]*[\'"]{3}', lines[i]):
|
||||
quote = '"""' if '"""' in lines[i] else "'''"
|
||||
j = i
|
||||
# ищем закрывающую кавычку
|
||||
while j < len(lines):
|
||||
if quote in lines[j] and j != i:
|
||||
j += 1
|
||||
break
|
||||
j += 1
|
||||
insert_at = min(j, len(lines))
|
||||
|
||||
# вставляем future-импорт
|
||||
future_line = "from __future__ import annotations\n"
|
||||
if not any(l.strip().startswith("from __future__ import annotations") for l in lines):
|
||||
lines.insert(insert_at, future_line)
|
||||
changed = True
|
||||
|
||||
# обеспечить корректное положение "from uuid import UUID":
|
||||
txt = "".join(lines)
|
||||
uuid_pat = re.compile(r'^\s*from\s+uuid\s+import\s+UUID\s*(#.*)?$\n?', re.M)
|
||||
has_uuid = uuid_pat.search(txt) is not None
|
||||
if has_uuid:
|
||||
# удалим все вхождения, потом вставим одно — сразу после future
|
||||
new_lines = []
|
||||
removed = False
|
||||
for ln in lines:
|
||||
if uuid_pat.match(ln):
|
||||
removed = True
|
||||
continue
|
||||
new_lines.append(ln)
|
||||
lines = new_lines
|
||||
if removed:
|
||||
lines.insert(insert_at + 1, "from uuid import UUID\n")
|
||||
changed = True
|
||||
else:
|
||||
# импорт отсутствует — добавлять имеет смысл только если в файле встречается "UUID" как тип
|
||||
if re.search(r'\bUUID\b', "".join(lines)):
|
||||
lines.insert(insert_at + 1, "from uuid import UUID\n")
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
p.write_text("".join(lines))
|
||||
return changed
|
||||
|
||||
changed_any = False
|
||||
for sub in ("services/auth/src/app/schemas", "services/match/src/app/schemas", "services/profiles/src/app/schemas"):
|
||||
d = Path(sub)
|
||||
if not d.exists():
|
||||
continue
|
||||
for p in d.rglob("*.py"):
|
||||
if fix_file(p):
|
||||
print("fixed:", p)
|
||||
changed_any = True
|
||||
|
||||
print("DONE" if changed_any else "NO-CHANGES")
|
||||
PY
|
||||
|
||||
echo "[docker] rebuild & restart auth/match/profiles…"
|
||||
docker compose build auth match profiles >/dev/null
|
||||
docker compose restart auth match profiles
|
||||
41
scripts/fix_match_response_models.sh
Executable file
41
scripts/fix_match_response_models.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
set -euo pipefail
|
||||
DIR="services/match/src/app/schemas"
|
||||
[ -d "$DIR" ] || { echo "Not found: $DIR"; exit 1; }
|
||||
|
||||
python3 - "$DIR" <<'PY'
|
||||
from pathlib import Path
|
||||
import re, sys
|
||||
d = Path(sys.argv[1])
|
||||
for p in d.glob("*.py"):
|
||||
s = p.read_text(); orig = s
|
||||
if "class " not in s:
|
||||
continue
|
||||
|
||||
if "from uuid import UUID" not in s:
|
||||
s = "from uuid import UUID\n" + s
|
||||
|
||||
# самые частые поля
|
||||
s = re.sub(r'(\bid\s*:\s*)str', r'\1UUID', s)
|
||||
s = re.sub(r'(\buser_id_a\s*:\s*)str', r'\1UUID', s)
|
||||
s = re.sub(r'(\buser_id_b\s*:\s*)str', r'\1UUID', s)
|
||||
s = re.sub(r'(\buser_id\s*:\s*)str', r'\1UUID', s)
|
||||
|
||||
# включаем from_attributes / orm_mode для Out/Response моделей
|
||||
def patch_block(m):
|
||||
block = m.group(0)
|
||||
if "model_config" in block or "orm_mode" in block:
|
||||
return block
|
||||
return (block +
|
||||
"\n try:\n from pydantic import ConfigDict\n model_config = ConfigDict(from_attributes=True)\n"
|
||||
" except Exception:\n class Config:\n orm_mode = True\n")
|
||||
|
||||
s = re.sub(r"class\s+\w+(Out|Response)\s*\([^\)]*\)\s*:(?:\n\s+.+)+", patch_block, s)
|
||||
|
||||
if s != orig:
|
||||
p.write_text(s)
|
||||
print("[match] Patched:", p)
|
||||
PY
|
||||
|
||||
echo "[match] Rebuild & restart…"
|
||||
docker compose build match
|
||||
docker compose restart match
|
||||
141
scripts/patch.sh
141
scripts/patch.sh
@@ -1,31 +1,120 @@
|
||||
# scripts/patch_gateway_auth_header.sh
|
||||
cat > scripts/patch_gateway_auth_header.sh <<'BASH'
|
||||
#!/usr/bin/env bash
|
||||
cat > scripts/fix_chat_uuid_schemas.sh <<'SH'
|
||||
set -euo pipefail
|
||||
|
||||
CFG="infra/gateway/nginx.conf"
|
||||
[ -f "$CFG" ] || { echo "Not found: $CFG"; exit 1; }
|
||||
svc_dir="services/chat/src/app/schemas"
|
||||
test -d "$svc_dir" || { echo "Not found: $svc_dir"; exit 1; }
|
||||
|
||||
# Грубая, но надёжная вставка proxy_set_header Authorization во все блоки location к сервисам
|
||||
awk '
|
||||
/location[[:space:]]+\/(auth|profiles|match|chat|payments)\//,/\}/ {
|
||||
print
|
||||
if ($0 ~ /proxy_pass/ && !seen_auth) {
|
||||
print " proxy_set_header Authorization $http_authorization;"
|
||||
print " proxy_set_header X-Forwarded-Proto $scheme;"
|
||||
print " proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"
|
||||
print " proxy_set_header Host $host;"
|
||||
seen_auth=1
|
||||
}
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
/\}/ { seen_auth=0 }
|
||||
' "$CFG" > "$CFG.tmp" && mv "$CFG.tmp" "$CFG"
|
||||
python3 - <<'PY'
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
root = Path("services/chat/src/app/schemas")
|
||||
|
||||
def ensure_future_and_uuid(text: str) -> str:
|
||||
lines = text.splitlines(keepends=True)
|
||||
changed = False
|
||||
|
||||
# Удалим дубли future-импорта
|
||||
fut_pat = re.compile(r'^\s*from\s+__future__\s+import\s+annotations\s*(#.*)?$\n?', re.M)
|
||||
if fut_pat.search(text):
|
||||
lines = [ln for ln in lines if not fut_pat.match(ln)]
|
||||
changed = True
|
||||
|
||||
# Вставим future после докстринга (если он есть)
|
||||
i = 0
|
||||
while i < len(lines) and (lines[i].strip()=="" or lines[i].lstrip().startswith("#!") or re.match(r'^\s*#.*coding[:=]', lines[i])):
|
||||
i += 1
|
||||
insert_at = i
|
||||
if i < len(lines) and re.match(r'^\s*[rubfRUBF]*[\'"]{3}', lines[i]): # докстринг
|
||||
q = '"""' if '"""' in lines[i] else "'''"
|
||||
j = i
|
||||
while j < len(lines):
|
||||
if q in lines[j] and j != i:
|
||||
j += 1
|
||||
break
|
||||
j += 1
|
||||
insert_at = min(j, len(lines))
|
||||
|
||||
if not any("from __future__ import annotations" in ln for ln in lines):
|
||||
lines.insert(insert_at, "from __future__ import annotations\n")
|
||||
changed = True
|
||||
|
||||
txt = "".join(lines)
|
||||
if "from uuid import UUID" not in txt:
|
||||
# вставим сразу после future
|
||||
# найдём позицию future
|
||||
pos = next((k for k,ln in enumerate(lines) if "from __future__ import annotations" in ln), 0)
|
||||
lines.insert(pos+1, "from uuid import UUID\n")
|
||||
changed = True
|
||||
|
||||
return "".join(lines), changed
|
||||
|
||||
def set_config(text: str) -> str:
|
||||
# Pydantic v2: добавим ConfigDict и model_config, если их нет
|
||||
if "ConfigDict" not in text:
|
||||
text = re.sub(r'from pydantic import ([^\n]+)',
|
||||
lambda m: m.group(0).replace(m.group(1), (m.group(1) + ", ConfigDict").replace(", ConfigDict,"," , ConfigDict,")),
|
||||
text, count=1) if "from pydantic import" in text else ("from pydantic import BaseModel, ConfigDict\n" + text)
|
||||
|
||||
# В каждую модель, наследующую BaseModel, которая заканчивается на Read/Out, добавим from_attributes
|
||||
def inject_cfg(block: str) -> str:
|
||||
# если уже есть model_config/orm_mode — не трогаем
|
||||
if "model_config" in block or "class Config" in block:
|
||||
return block
|
||||
# аккуратно добавим model_config сверху класса
|
||||
header, body = block.split(":", 1)
|
||||
return header + ":\n model_config = ConfigDict(from_attributes=True)\n" + body
|
||||
|
||||
# Разобьём по классам
|
||||
parts = re.split(r'(\nclass\s+[A-Za-z0-9_]+\s*\([^\)]*BaseModel[^\)]*\)\s*:)', text)
|
||||
if len(parts) > 1:
|
||||
out = [parts[0]]
|
||||
for i in range(1, len(parts), 2):
|
||||
head = parts[i]
|
||||
body = parts[i+1]
|
||||
m = re.search(r'class\s+([A-Za-z0-9_]+)\s*\(', head)
|
||||
name = m.group(1) if m else ""
|
||||
if name.endswith(("Read","Out")):
|
||||
out.append(inject_cfg(head + body))
|
||||
else:
|
||||
out.append(head + body)
|
||||
text = "".join(out)
|
||||
return text
|
||||
|
||||
def fix_uuid_fields(text: str) -> str:
|
||||
# Преобразуем только в классах Read/Out
|
||||
def repl_in_class(cls_text: str) -> str:
|
||||
# заменяем аннотации полей
|
||||
repl = {
|
||||
r'(^\s*)(id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(pair_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(room_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(sender_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
r'(^\s*)(user_id)\s*:\s*str(\b)': r'\1\2: UUID\3',
|
||||
}
|
||||
for pat, sub in repl.items():
|
||||
cls_text = re.sub(pat, sub, cls_text, flags=re.M)
|
||||
return cls_text
|
||||
|
||||
patt = re.compile(r'(\nclass\s+[A-Za-z0-9_]+(Read|Out)\s*\([^\)]*BaseModel[^\)]*\)\s*:\n(?:\s+.*\n)+)', re.M)
|
||||
return patt.sub(lambda m: repl_in_class(m.group(1)), text)
|
||||
|
||||
changed_any = False
|
||||
for p in root.glob("**/*.py"):
|
||||
txt = p.read_text()
|
||||
new, c1 = ensure_future_and_uuid(txt)
|
||||
new = set_config(new)
|
||||
new2 = fix_uuid_fields(new)
|
||||
if new2 != txt:
|
||||
p.write_text(new2)
|
||||
print("fixed:", p)
|
||||
changed_any = True
|
||||
|
||||
print("DONE" if changed_any else "NO-CHANGES")
|
||||
PY
|
||||
|
||||
echo "[docker] rebuild & restart chat…"
|
||||
docker compose build --no-cache chat >/dev/null
|
||||
docker compose restart chat
|
||||
|
||||
echo "[gateway] restart..."
|
||||
docker compose restart gateway
|
||||
BASH
|
||||
|
||||
chmod +x scripts/patch_gateway_auth_header.sh
|
||||
./scripts/patch_gateway_auth_header.sh
|
||||
|
||||
Reference in New Issue
Block a user