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"]