feat: improve mindspace asset handling and local runtime paths

This commit is contained in:
john
2026-06-28 12:18:26 +08:00
parent 4a9bc710f1
commit ea19ffb5fa
36 changed files with 867 additions and 100 deletions
+1 -1
View File
@@ -36,6 +36,6 @@ say "本地与 103 HEAD 对比"
remote_memind="$(ssh -o BatchMode=yes -o ConnectTimeout=15 "${HOST}" "cd /Users/john/Project/Memind && git rev-parse --short HEAD")"
remote_adm="$(ssh -o BatchMode=yes -o ConnectTimeout=15 "${HOST}" "cd /Users/john/Project/memind_adm && git rev-parse --short HEAD")"
local_memind="$(git -C "$(cd "$(dirname "$0")/.." && pwd)" rev-parse --short HEAD)"
local_adm="$(git -C /Users/john/PycharmProjects/test/test-memindadm rev-parse --short HEAD)"
local_adm="$(git -C /Users/john/Project/memind_adm rev-parse --short HEAD)"
printf 'local_memind=%s remote_memind=%s\n' "${local_memind}" "${remote_memind}"
printf 'local_memindadm=%s remote_memindadm=%s\n' "${local_adm}" "${remote_adm}"
+1 -1
View File
@@ -22,7 +22,7 @@ DEPLOY_DIR="${ROOT}/deploy/plaza-105"
DEPLOY_ENV="${DEPLOY_DIR}/plaza-105.env"
EXCLUDE="${ROOT}/scripts/.rsync-exclude-plaza-web"
PLAZA_APP_DIR="${PLAZA_APP_DIR:-${ROOT}/../tkmind_go/ui/plaza}"
PLAZA_APP_DIR="${PLAZA_APP_DIR:-${ROOT}/../memind_plaza}"
PLAZA_DEPLOY_HOST="${PLAZA_DEPLOY_HOST:-root@120.26.184.105}"
PLAZA_REMOTE_DIR="${PLAZA_REMOTE_DIR:-/root/plaza/web}"
PLAZA_PROD_URL="${PLAZA_PROD_URL:-https://plaza.tkmind.cn}"
+4 -4
View File
@@ -13,11 +13,11 @@ EXCLUDE_FILE="${ROOT_DIR}/scripts/.rsync-exclude-test-host"
MEMIND_SRC="${MEMIND_SRC:-${ROOT_DIR}}"
ADM_SRC="${ADM_SRC:-${ROOT_DIR}/../memind_adm}"
PLAZA_SRC="${PLAZA_SRC:-${ROOT_DIR}/../tkmind_go/ui/plaza}"
PLAZA_SRC="${PLAZA_SRC:-${ROOT_DIR}/../memind_plaza}"
MEMIND_DST="${MEMIND_DST:-/Users/john/PycharmProjects/test/test-memind}"
ADM_DST="${ADM_DST:-/Users/john/PycharmProjects/test/test-memindadm}"
PLAZA_DST="${PLAZA_DST:-/Users/john/PycharmProjects/test/test-memindplaza}"
MEMIND_DST="${MEMIND_DST:-/Users/john/Project/Memind}"
ADM_DST="${ADM_DST:-/Users/john/Project/memind_adm}"
PLAZA_DST="${PLAZA_DST:-/Users/john/Project/memind_plaza}"
RSYNC_OPTS=(
-az
+2 -4
View File
@@ -12,9 +12,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const defaultPlazaDir = fs.existsSync(path.join(root, '../test-memindplaza/app/plaza/package.json'))
? path.join(root, '../test-memindplaza/app/plaza')
: path.join(root, '../tkmind_go/ui/plaza');
const defaultPlazaDir = path.join(root, '../memind_plaza');
const plazaDir = process.env.PLAZA_APP_DIR ?? defaultPlazaDir;
const portalPort = Number(process.env.H5_PORT ?? 8081);
const plazaPort = Number(process.env.PLAZA_PORT ?? 3001);
@@ -119,7 +117,7 @@ process.on('SIGINT', () => shutdown(0));
process.on('SIGTERM', () => shutdown(0));
if (!fs.existsSync(path.join(plazaDir, 'package.json'))) {
console.error(`未找到 Plaza Next.js${plazaDir}。请设置 PLAZA_APP_DIR 指向本地 Plaza 源码,例如 ../test-memindplaza/app/plaza`);
console.error(`未找到 Plaza Next.js${plazaDir}。请设置 PLAZA_APP_DIR 指向本地 Plaza 源码,例如 ../memind_plaza`);
process.exit(1);
}
+2 -4
View File
@@ -7,9 +7,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const defaultPlazaDir = fs.existsSync(path.join(root, '../test-memindplaza/app/plaza/package.json'))
? path.join(root, '../test-memindplaza/app/plaza')
: path.join(root, '../tkmind_go/ui/plaza');
const defaultPlazaDir = path.join(root, '../memind_plaza');
const plazaDir = process.env.PLAZA_APP_DIR ?? defaultPlazaDir;
const opsDir = path.join(root, 'ops');
const portalPort = Number(process.env.H5_PORT ?? 8081);
@@ -125,7 +123,7 @@ const opsEnv = {
function ensurePlazaApp() {
if (!fs.existsSync(path.join(plazaDir, 'package.json'))) {
throw new Error(`未找到 Plaza Next.js${plazaDir}。请设置 PLAZA_APP_DIR 指向本地 Plaza 源码,例如 ../test-memindplaza/app/plaza`);
throw new Error(`未找到 Plaza Next.js${plazaDir}。请设置 PLAZA_APP_DIR 指向本地 Plaza 源码,例如 ../memind_plaza`);
}
}
+8 -8
View File
@@ -44,22 +44,22 @@ gadm.tkmind.cn {
}
}
# img.tkmind.cn:图片缓存与授权 (forward_auth → imgproxy)
# 在生产环境(105)上
# 1. TLS 证书指向实际的 .acme.sh 目录
# 2. forward_auth 指向 103 的授权端点
# 3. reverse_proxy 指向 103 imgproxy 进程
# img.tkmind.cn:图片缩放入口(当前真实运行位应在 103 / Studio
# 注意
# 1. 现有 signer 只生成签名路径,不会附带 asset_id query
# 2. 因此下面的 forward_auth 示例目前还不能直接用于“按资产鉴权”
# 3. 先确保 103 本机已有 imgproxy 监听 127.0.0.1:20081,再接入公网域名
img.tkmind.cn {
# 本地开发时如果没有证书,可注释掉 tls 行
# 生产证书路径按 103 当前 .acme.sh 实际位置填写
# tls /Users/john/.acme.sh/img.tkmind.cn_ecc/fullchain.cer /Users/john/.acme.sh/img.tkmind.cn_ecc/img.tkmind.cn.key
# forward_auth 检查图片访问权限
# 如果后续 signer URL 带上 asset_id,可启用这层鉴权
# forward_auth 127.0.0.1:8081 /api/mindspace/v1/authorize-image {
# uri /api/mindspace/v1/authorize-image?asset_id={query.asset_id}
# copy_headers X-Request-Id
# }
# reverse_proxy 到 imgproxy(生产在 103
# reverse_proxy 到 103 本机 imgproxy
# reverse_proxy 127.0.0.1:20081 {
# flush_interval -1
# }
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="${ROOT:-/Users/john/Project/Memind}"
ENV_FILE="${ENV_FILE:-${ROOT}/.env}"
LABEL="${LABEL:-cn.tkmind.imgproxy}"
PLIST="${HOME}/Library/LaunchAgents/${LABEL}.plist"
LOG_DIR="${HOME}/Library/Logs"
LOG_FILE="${LOG_DIR}/imgproxy.log"
IMGPROXY_BIN="${IMGPROXY_BIN:-/opt/homebrew/bin/imgproxy}"
IMGPROXY_BIND="${IMGPROXY_BIND:-127.0.0.1:20081}"
UID_NUM="$(id -u)"
GUI="gui/${UID_NUM}"
if [[ ! -f "${ENV_FILE}" ]]; then
echo "未找到环境文件: ${ENV_FILE}" >&2
exit 1
fi
if [[ ! -x "${IMGPROXY_BIN}" ]]; then
echo "未找到 imgproxy: ${IMGPROXY_BIN}" >&2
echo "请先在 103 执行: brew install imgproxy" >&2
exit 1
fi
mkdir -p "${HOME}/Library/LaunchAgents" "${LOG_DIR}"
set -a
source "${ENV_FILE}"
set +a
MINDSPACE_STORAGE_ROOT="${MINDSPACE_STORAGE_ROOT:-${ROOT}/data/mindspace}"
IMGPROXY_SIGNING_KEY="${IMGPROXY_SIGNING_KEY:-}"
IMGPROXY_SIGNING_SALT="${IMGPROXY_SIGNING_SALT:-}"
if [[ -z "${IMGPROXY_SIGNING_KEY}" || -z "${IMGPROXY_SIGNING_SALT}" ]]; then
echo "缺少 IMGPROXY_SIGNING_KEY / IMGPROXY_SIGNING_SALT,无法安装 imgproxy" >&2
exit 1
fi
cat >"${PLIST}" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${LABEL}</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-lc</string>
<string>
export IMGPROXY_BIND='${IMGPROXY_BIND}';
export IMGPROXY_LOCAL_FILESYSTEM_ROOT='${MINDSPACE_STORAGE_ROOT}';
export IMGPROXY_KEY='${IMGPROXY_SIGNING_KEY}';
export IMGPROXY_SALT='${IMGPROXY_SIGNING_SALT}';
export IMGPROXY_USE_ETAG='true';
export IMGPROXY_ENABLE_WEBP_DETECTION='true';
exec '${IMGPROXY_BIN}'
</string>
</array>
<key>WorkingDirectory</key>
<string>${ROOT}</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/opt/homebrew/opt/node@24/bin:/usr/local/bin:/usr/bin:/bin</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>${LOG_FILE}</string>
<key>StandardErrorPath</key>
<string>${LOG_FILE}</string>
</dict>
</plist>
EOF
if launchctl print "${GUI}/${LABEL}" >/dev/null 2>&1; then
launchctl bootout "${GUI}/${LABEL}" 2>/dev/null || true
sleep 1
fi
launchctl bootstrap "${GUI}" "${PLIST}"
launchctl enable "${GUI}/${LABEL}"
launchctl kickstart -k "${GUI}/${LABEL}"
echo "等待 imgproxy 就绪..."
for _ in $(seq 1 20); do
if curl -sf "http://${IMGPROXY_BIND}/health" >/dev/null 2>&1; then
break
fi
sleep 1
done
echo ""
echo "=== imgproxy 状态 ==="
launchctl list | grep "${LABEL}" || true
curl -i "http://${IMGPROXY_BIND}/health" || true
echo ""
echo "LaunchAgent: ${PLIST}"
echo "Log file: ${LOG_FILE}"
echo "Storage: ${MINDSPACE_STORAGE_ROOT}"
echo "Bind: ${IMGPROXY_BIND}"
echo ""
echo "如果这是 103 生产环境,记得把 Portal .env 中的 IMGPROXY_BASE_URL 设为:"
echo " IMGPROXY_BASE_URL=https://img.tkmind.cn"
+1 -1
View File
@@ -192,7 +192,7 @@ const coverBase = process.env.PLAZA_COVER_BASE ?? '/plaza-covers';
const plazaPublicDir =
process.env.PLAZA_APP_DIR
? path.join(process.env.PLAZA_APP_DIR, 'public/plaza-covers')
: path.join(root, '../tkmind_go/ui/plaza/public/plaza-covers');
: path.join(root, '../memind_plaza/public/plaza-covers');
fs.mkdirSync(coversDir, { recursive: true });
+2 -4
View File
@@ -12,9 +12,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const defaultPlazaDir = fs.existsSync(path.join(root, '../test-memindplaza/app/plaza/package.json'))
? path.join(root, '../test-memindplaza/app/plaza')
: path.join(root, '../tkmind_go/ui/plaza');
const defaultPlazaDir = path.join(root, '../memind_plaza');
const plazaDir = process.env.PLAZA_APP_DIR ?? defaultPlazaDir;
const portalPort = Number(process.env.H5_PORT ?? 8081);
const plazaPort = Number(process.env.PLAZA_PORT ?? 3001);
@@ -102,7 +100,7 @@ process.on('SIGINT', () => shutdown(0));
process.on('SIGTERM', () => shutdown(0));
if (!fs.existsSync(path.join(plazaDir, 'package.json'))) {
console.error(`未找到 Plaza Next.js${plazaDir}。请设置 PLAZA_APP_DIR 指向本地 Plaza 源码,例如 ../test-memindplaza/app/plaza`);
console.error(`未找到 Plaza Next.js${plazaDir}。请设置 PLAZA_APP_DIR 指向本地 Plaza 源码,例如 ../memind_plaza`);
process.exit(1);
}