File size: 3,765 Bytes
d0df8d0 3f6d118 ea1c807 37bd4db 3f6d118 ea1c807 37bd4db ea1c807 3f6d118 4409c1d d0df8d0 3f6d118 792069d 37bd4db ea1c807 d0df8d0 37bd4db fd92c36 37bd4db 4409c1d ea1c807 792069d 4b2635d ea1c807 37bd4db ea1c807 37bd4db ea1c807 37bd4db ea1c807 37bd4db ea1c807 37bd4db ea1c807 37bd4db ea1c807 4409c1d 37bd4db 4409c1d ea1c807 4409c1d ea1c807 37bd4db 4409c1d ea1c807 4409c1d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | FROM node:20-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y git python3 make g++ && rm -rf /var/lib/apt/lists/*
RUN git clone --depth=1 https://github.com/decolua/9router.git .
RUN sed -i 's/sameSite: "lax"/sameSite: "none"/' src/app/api/auth/login/route.js
RUN cat > next.config.mjs << 'NEXTEOF'
const nextConfig = {
output: "standalone",
serverExternalPackages: ["better-sqlite3","sql.js","node:sqlite","bun:sqlite"],
images:{unoptimized:true},env:{},
webpack:(config,{isServer})=>{
if(!isServer)config.resolve.fallback={...config.resolve.fallback,fs:false,path:false};
config.watchOptions={...config.watchOptions,ignored:/[\/](logs|\.next)[\/]/};return config;
},
async headers(){return[{source:"/(.*)",headers:[{key:"X-Frame-Options",value:"ALLOWALL"},{key:"Content-Security-Policy",value:"frame-ancestors *"}]}];},
async rewrites(){return[
{source:"/v1/v1/:path*",destination:"/api/v1/:path*"},{source:"/v1/v1",destination:"/api/v1"},
{source:"/codex/:path*",destination:"/api/v1/responses"},{source:"/v1/:path*",destination:"/api/v1/:path*"},
{source:"/v1",destination:"/api/v1"}
];}
};
export default nextConfig;
NEXTEOF
RUN npm install && npm run build
# ---- Runtime ----
FROM node:20-slim
RUN apt-get update && apt-get install -y python3 python3-pip && rm -rf /var/lib/apt/lists/*
RUN pip3 install huggingface_hub --break-system-packages --quiet
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
COPY --from=builder /app/open-sse ./open-sse
ENV PORT=7860 HOSTNAME=0.0.0.0
ENV NEXT_PUBLIC_BASE_URL=https://gunnybd01-9router.hf.space
ENV INITIAL_PASSWORD=123456 AUTH_COOKIE_SECURE=true
ENV DB_PATH=/root/.9router/db/data.sqlite
ENV HF_DATASET=gunnybd01/9router-db-backup
ENV SYNC_INTERVAL=3600
EXPOSE 7860
RUN cat > /sync_db.py << 'PYEOF'
import os,sys,time
from huggingface_hub import HfApi
db=os.environ.get("DB_PATH","/root/.9router/db/data.sqlite")
tok=os.environ.get("HF_TOKEN",""); ds=os.environ.get("HF_DATASET","gunnybd01/9router-db-backup")
if not tok: print("[sync] no HF_TOKEN"); sys.exit(0)
if not os.path.exists(db): print("[sync] no DB"); sys.exit(0)
print(f"[sync] {os.path.getsize(db)}b -> {ds} @ {time.strftime('%H:%M:%S')}")
try:
HfApi(token=tok).upload_file(path_or_fileobj=db,path_in_repo="data.sqlite",
repo_id=ds,repo_type="dataset",commit_message=f"backup {time.strftime('%Y-%m-%d %H:%M')}")
print("[sync] OK")
except Exception as e: print(f"[sync] ERR: {e}")
PYEOF
RUN cat > /restore_db.py << 'PYEOF2'
import os,sys,shutil
from huggingface_hub import hf_hub_download
db=os.environ.get("DB_PATH","/root/.9router/db/data.sqlite")
tok=os.environ.get("HF_TOKEN",""); ds=os.environ.get("HF_DATASET","gunnybd01/9router-db-backup")
os.makedirs(os.path.dirname(db),exist_ok=True)
if not tok: print("[restore] no HF_TOKEN"); sys.exit(0)
print(f"[restore] Pulling from {ds}...")
try:
dl=hf_hub_download(repo_id=ds,filename="data.sqlite",repo_type="dataset",token=tok,local_dir="/tmp/hfr")
sz=os.path.getsize(dl)
if sz>4096: shutil.copy2(dl,db); print(f"[restore] OK {sz}b")
else: print(f"[restore] too small {sz}b, fresh")
except Exception as e: print(f"[restore] no backup: {e}")
PYEOF2
# Wrapper script cho trap (tranh sh quoting issues)
RUN printf '#!/bin/sh\npython3 /sync_db.py\n' > /do_sync.sh && chmod +x /do_sync.sh
RUN cat > /start.sh << 'STARTEOF'
#!/bin/sh
python3 /restore_db.py
node /app/server.js &
APP_PID=$!
INTERVAL="${SYNC_INTERVAL:-3600}"
echo "[start] Sync every ${INTERVAL}s, PID=$$"
(while true; do sleep "$INTERVAL"; /do_sync.sh; done) &
trap /do_sync.sh TERM INT
wait $APP_PID
STARTEOF
RUN chmod +x /start.sh
CMD ["/bin/sh", "/start.sh"] |