This commit is contained in:
@@ -92,6 +92,20 @@ jobs:
|
||||
rm -f /tmp/backend-deploy.tar.gz
|
||||
REMOTE
|
||||
|
||||
- name: Free disk on deploy host (docker prune)
|
||||
run: |
|
||||
ssh deploy-target bash -s <<REMOTE
|
||||
set -euo pipefail
|
||||
PRUNE="${REMOTE_BACKEND}/scripts/docker-prune.sh"
|
||||
if [[ -x "\${PRUNE}" ]]; then
|
||||
bash "\${PRUNE}"
|
||||
else
|
||||
sudo docker builder prune -af || true
|
||||
sudo docker system prune -af || true
|
||||
df -h /
|
||||
fi
|
||||
REMOTE
|
||||
|
||||
- name: Rebuild and restart API container
|
||||
run: |
|
||||
ssh deploy-target bash -s <<REMOTE
|
||||
|
||||
@@ -36,8 +36,57 @@ func requestLangCode(r *http.Request) string {
|
||||
return translateNormalizePostLang(raw)
|
||||
}
|
||||
|
||||
func (t postTextI18n) pick(r *http.Request) string {
|
||||
return pickLangField(r, t.TextZh, t.TextEn, t.TextJa, t.TextKo, t.TextVi, t.TextId, t.TextMs)
|
||||
// textForLang returns body text stored for the post's source language only.
|
||||
func (t postTextI18n) textForLang(lang string) string {
|
||||
switch translateNormalizePostLang(lang) {
|
||||
case "en":
|
||||
return strings.TrimSpace(t.TextEn)
|
||||
case "ja":
|
||||
return strings.TrimSpace(t.TextJa)
|
||||
case "ko":
|
||||
return strings.TrimSpace(t.TextKo)
|
||||
case "vi":
|
||||
return strings.TrimSpace(t.TextVi)
|
||||
case "id":
|
||||
return strings.TrimSpace(t.TextId)
|
||||
case "ms":
|
||||
return strings.TrimSpace(t.TextMs)
|
||||
default:
|
||||
return strings.TrimSpace(t.TextZh)
|
||||
}
|
||||
}
|
||||
|
||||
func postTextI18nFromSingle(lang, text string) postTextI18n {
|
||||
lang = translateNormalizePostLang(lang)
|
||||
text = strings.TrimSpace(text)
|
||||
var t postTextI18n
|
||||
switch lang {
|
||||
case "en":
|
||||
t.TextEn = text
|
||||
case "ja":
|
||||
t.TextJa = text
|
||||
case "ko":
|
||||
t.TextKo = text
|
||||
case "vi":
|
||||
t.TextVi = text
|
||||
case "id":
|
||||
t.TextId = text
|
||||
case "ms":
|
||||
t.TextMs = text
|
||||
default:
|
||||
t.TextZh = text
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func normalizePostSourceLang(lang string) string {
|
||||
lang = translateNormalizePostLang(strings.TrimSpace(lang))
|
||||
switch lang {
|
||||
case "zh", "en", "ja", "ko", "vi", "id", "ms":
|
||||
return lang
|
||||
default:
|
||||
return "zh"
|
||||
}
|
||||
}
|
||||
|
||||
func (t postTextI18n) anyNonEmpty() bool {
|
||||
|
||||
@@ -34,7 +34,7 @@ type PostDTO struct {
|
||||
Language string `json:"language"` // UI locale from ?lang= (matches text selection)
|
||||
SourceLanguage string `json:"sourceLanguage,omitempty"` // DB source metadata (admin input language)
|
||||
Text string `json:"text,omitempty"`
|
||||
Localizations map[string]postLocalePayload `json:"localizations"`
|
||||
Localizations map[string]postLocalePayload `json:"localizations,omitempty"`
|
||||
Attachments []AttachmentDTO `json:"attachments"`
|
||||
IsRecommended bool `json:"isRecommended"`
|
||||
PublishedAt string `json:"publishedAt"`
|
||||
@@ -439,10 +439,10 @@ func scanPostRow(r *http.Request, row pgx.Row) (PostDTO, postTextI18n, error) {
|
||||
if slug != nil {
|
||||
dto.CategorySlug = *slug
|
||||
}
|
||||
dto.SourceLanguage = sourceLang
|
||||
dto.Language = requestLangCode(r)
|
||||
dto.Text = texts.pick(r)
|
||||
dto.Localizations = texts.toLocalizations()
|
||||
src := normalizePostSourceLang(sourceLang)
|
||||
dto.SourceLanguage = src
|
||||
dto.Language = src
|
||||
dto.Text = texts.textForLang(src)
|
||||
if pub != nil {
|
||||
dto.PublishedAt = pub.UTC().Format(time.RFC3339)
|
||||
} else if created != nil {
|
||||
|
||||
@@ -127,6 +127,9 @@ func listPostsQuery(w http.ResponseWriter, r *http.Request, searchMode bool) {
|
||||
typ = "all"
|
||||
}
|
||||
langFilter := strings.TrimSpace(r.URL.Query().Get("language"))
|
||||
if langFilter == "" {
|
||||
langFilter = strings.TrimSpace(r.URL.Query().Get("lang"))
|
||||
}
|
||||
|
||||
base := postSelectBase() + ` WHERE ` + publicPostWhere
|
||||
args := []any{}
|
||||
|
||||
38
scripts/docker-prune.sh
Executable file
38
scripts/docker-prune.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
# Free disk before docker compose build (build cache is the usual culprit for Go OOM / no space).
|
||||
# Safe on deploy hosts: does NOT use --volumes (keeps named DB volumes).
|
||||
set -euo pipefail
|
||||
|
||||
docker_cmd() {
|
||||
if docker info >/dev/null 2>&1; then
|
||||
docker "$@"
|
||||
elif sudo docker info >/dev/null 2>&1; then
|
||||
sudo docker "$@"
|
||||
else
|
||||
echo "docker not available — skip prune" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "==> disk before prune"
|
||||
df -h / 2>/dev/null || true
|
||||
if [[ -d /var/lib/docker ]]; then
|
||||
df -h /var/lib/docker 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if ! docker info >/dev/null 2>&1 && ! sudo docker info >/dev/null 2>&1; then
|
||||
echo "docker not available — skip prune" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "==> docker builder prune (build cache)"
|
||||
docker_cmd builder prune -af || true
|
||||
|
||||
echo "==> docker system prune (unused images, stopped containers, networks)"
|
||||
docker_cmd system prune -af || true
|
||||
|
||||
echo "==> disk after prune"
|
||||
df -h / 2>/dev/null || true
|
||||
if [[ -d /var/lib/docker ]]; then
|
||||
df -h /var/lib/docker 2>/dev/null || true
|
||||
fi
|
||||
Reference in New Issue
Block a user