gunnybd01 commited on
Commit
ea1c807
·
verified ·
1 Parent(s): 4409c1d

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +87 -62
Dockerfile CHANGED
@@ -1,5 +1,4 @@
1
  FROM node:20-slim AS builder
2
-
3
  WORKDIR /app
4
  RUN apt-get update && apt-get install -y git python3 make g++ && rm -rf /var/lib/apt/lists/*
5
  RUN git clone --depth=1 https://github.com/decolua/9router.git .
@@ -7,26 +6,21 @@ RUN sed -i 's/sameSite: "lax"/sameSite: "none"/' src/app/api/auth/login/route.js
7
  RUN cat > next.config.mjs << 'NEXTEOF'
8
  const nextConfig = {
9
  output: "standalone",
10
- serverExternalPackages: ["better-sqlite3", "sql.js", "node:sqlite", "bun:sqlite"],
11
- images: { unoptimized: true },
12
- env: {},
13
- webpack: (config, { isServer }) => {
14
- if (!isServer) config.resolve.fallback = { ...config.resolve.fallback, fs: false, path: false };
15
- config.watchOptions = { ...config.watchOptions, ignored: /[\/](logs|\.next)[\/]/ };
16
  return config;
17
  },
18
- async headers() {
19
- return [{ source: "/(.*)", headers: [{ key: "X-Frame-Options", value: "ALLOWALL" }, { key: "Content-Security-Policy", value: "frame-ancestors *" }] }];
20
- },
21
- async rewrites() {
22
- return [
23
- { source: "/v1/v1/:path*", destination: "/api/v1/:path*" },
24
- { source: "/v1/v1", destination: "/api/v1" },
25
- { source: "/codex/:path*", destination: "/api/v1/responses" },
26
- { source: "/v1/:path*", destination: "/api/v1/:path*" },
27
- { source: "/v1", destination: "/api/v1" }
28
- ];
29
- }
30
  };
31
  export default nextConfig;
32
  NEXTEOF
@@ -34,7 +28,8 @@ RUN npm install && npm run build
34
 
35
  # ---- Runtime ----
36
  FROM node:20-slim
37
- RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
 
38
 
39
  WORKDIR /app
40
  COPY --from=builder /app/.next/standalone ./
@@ -49,59 +44,89 @@ ENV INITIAL_PASSWORD=123456
49
  ENV AUTH_COOKIE_SECURE=true
50
  ENV DB_PATH=/root/.9router/db/data.sqlite
51
  ENV HF_DATASET=gunnybd01/9router-db-backup
 
52
 
53
  EXPOSE 7860
54
 
55
- RUN cat > /sync_db.sh << 'SYNCEOF'
56
- #!/bin/sh
57
- DB_FILE="${DB_PATH:-/root/.9router/db/data.sqlite}"
58
- TOKEN="$HF_TOKEN"
59
- DATASET="${HF_DATASET:-gunnybd01/9router-db-backup}"
60
- if [ -z "$TOKEN" ]; then echo "[sync] no HF_TOKEN, skip"; exit 0; fi
61
- if [ ! -f "$DB_FILE" ]; then echo "[sync] no DB file, skip"; exit 0; fi
62
- echo "[sync] Pushing DB at $(date)..."
63
- HTTP=$(curl -s -o /tmp/sr.txt -w "%{http_code}" -X PUT -H "Authorization: Bearer $TOKEN" -T "$DB_FILE" "https://huggingface.co/api/datasets/${DATASET}/upload/main/data.sqlite")
64
- echo "[sync] HTTP $HTTP: $(cat /tmp/sr.txt | head -c 100)"
65
- SYNCEOF
66
 
67
- RUN cat > /restore_db.sh << 'RESTOREEOF'
68
- #!/bin/sh
69
- DB_FILE="${DB_PATH:-/root/.9router/db/data.sqlite}"
70
- DB_DIR=$(dirname "$DB_FILE")
71
- TOKEN="$HF_TOKEN"
72
- DATASET="${HF_DATASET:-gunnybd01/9router-db-backup}"
73
- mkdir -p "$DB_DIR"
74
- if [ -z "$TOKEN" ]; then echo "[restore] no HF_TOKEN"; exit 0; fi
75
- echo "[restore] Pulling DB from $DATASET..."
76
- HTTP=$(curl -L -s -o /tmp/db.sqlite -w "%{http_code}" -H "Authorization: Bearer $TOKEN" "https://huggingface.co/datasets/${DATASET}/resolve/main/data.sqlite")
77
- if [ "$HTTP" = "200" ]; then
78
- SZ=$(wc -c < /tmp/db.sqlite)
79
- if [ "$SZ" -gt "4096" ]; then
80
- cp /tmp/db.sqlite "$DB_FILE"
81
- echo "[restore] OK - ${SZ} bytes restored"
82
- else
83
- echo "[restore] Download too small (${SZ}b), starting fresh"
84
- fi
85
- else
86
- echo "[restore] HTTP $HTTP - no backup yet, starting fresh"
87
- fi
88
- RESTOREEOF
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
  RUN cat > /start.sh << 'STARTEOF'
91
  #!/bin/sh
92
- # 1. Restore DB from HF Dataset backup
93
- sh /restore_db.sh
94
- # 2. Start app
95
  node /app/server.js &
96
  APP_PID=$!
97
- # 3. Hourly sync in background
98
- (while true; do sleep 3600; sh /sync_db.sh; done) &
99
- # 4. Sync on shutdown (Space sleep)
100
- trap "sh /sync_db.sh; kill $APP_PID 2>/dev/null" TERM INT
 
101
  # 5. Wait
102
  wait $APP_PID
103
  STARTEOF
104
 
105
- RUN chmod +x /sync_db.sh /restore_db.sh /start.sh
106
-
107
  CMD ["/bin/sh", "/start.sh"]
 
1
  FROM node:20-slim AS builder
 
2
  WORKDIR /app
3
  RUN apt-get update && apt-get install -y git python3 make g++ && rm -rf /var/lib/apt/lists/*
4
  RUN git clone --depth=1 https://github.com/decolua/9router.git .
 
6
  RUN cat > next.config.mjs << 'NEXTEOF'
7
  const nextConfig = {
8
  output: "standalone",
9
+ serverExternalPackages: ["better-sqlite3","sql.js","node:sqlite","bun:sqlite"],
10
+ images:{unoptimized:true},env:{},
11
+ webpack:(config,{isServer})=>{
12
+ if(!isServer)config.resolve.fallback={...config.resolve.fallback,fs:false,path:false};
13
+ config.watchOptions={...config.watchOptions,ignored:/[\/](logs|\.next)[\/]/};
 
14
  return config;
15
  },
16
+ async headers(){return[{source:"/(.*)",headers:[{key:"X-Frame-Options",value:"ALLOWALL"},{key:"Content-Security-Policy",value:"frame-ancestors *"}]}];},
17
+ async rewrites(){return[
18
+ {source:"/v1/v1/:path*",destination:"/api/v1/:path*"},
19
+ {source:"/v1/v1",destination:"/api/v1"},
20
+ {source:"/codex/:path*",destination:"/api/v1/responses"},
21
+ {source:"/v1/:path*",destination:"/api/v1/:path*"},
22
+ {source:"/v1",destination:"/api/v1"}
23
+ ];}
 
 
 
 
24
  };
25
  export default nextConfig;
26
  NEXTEOF
 
28
 
29
  # ---- Runtime ----
30
  FROM node:20-slim
31
+ RUN apt-get update && apt-get install -y python3 python3-pip curl && rm -rf /var/lib/apt/lists/*
32
+ RUN pip3 install huggingface_hub --break-system-packages --quiet
33
 
34
  WORKDIR /app
35
  COPY --from=builder /app/.next/standalone ./
 
44
  ENV AUTH_COOKIE_SECURE=true
45
  ENV DB_PATH=/root/.9router/db/data.sqlite
46
  ENV HF_DATASET=gunnybd01/9router-db-backup
47
+ ENV SYNC_INTERVAL=3600
48
 
49
  EXPOSE 7860
50
 
51
+ # Python script: push DB lên HF Dataset
52
+ RUN cat > /sync_db.py << 'PYEOF'
53
+ import os, sys, time
54
+ from huggingface_hub import HfApi
 
 
 
 
 
 
 
55
 
56
+ DB_FILE = os.environ.get("DB_PATH", "/root/.9router/db/data.sqlite")
57
+ TOKEN = os.environ.get("HF_TOKEN", "")
58
+ DATASET = os.environ.get("HF_DATASET", "gunnybd01/9router-db-backup")
59
+
60
+ if not TOKEN:
61
+ print("[sync] HF_TOKEN not set, skip"); sys.exit(0)
62
+ if not os.path.exists(DB_FILE):
63
+ print(f"[sync] DB not found: {DB_FILE}"); sys.exit(0)
64
+
65
+ sz = os.path.getsize(DB_FILE)
66
+ print(f"[sync] Pushing DB ({sz} bytes) -> {DATASET} ...")
67
+ try:
68
+ api = HfApi(token=TOKEN)
69
+ api.upload_file(
70
+ path_or_fileobj=DB_FILE,
71
+ path_in_repo="data.sqlite",
72
+ repo_id=DATASET,
73
+ repo_type="dataset",
74
+ commit_message=f"DB sync {time.strftime('%Y-%m-%d %H:%M')}",
75
+ )
76
+ print(f"[sync] OK at {time.strftime('%Y-%m-%d %H:%M:%S')}")
77
+ except Exception as e:
78
+ print(f"[sync] ERROR: {e}")
79
+ PYEOF
80
+
81
+ # Python script: pull DB từ HF Dataset khi startup
82
+ RUN cat > /restore_db.py << 'PYEOF2'
83
+ import os, sys
84
+ from huggingface_hub import hf_hub_download
85
+
86
+ DB_FILE = os.environ.get("DB_PATH", "/root/.9router/db/data.sqlite")
87
+ TOKEN = os.environ.get("HF_TOKEN", "")
88
+ DATASET = os.environ.get("HF_DATASET", "gunnybd01/9router-db-backup")
89
+
90
+ os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
91
+
92
+ if not TOKEN:
93
+ print("[restore] HF_TOKEN not set, starting fresh"); sys.exit(0)
94
+
95
+ print(f"[restore] Pulling DB from {DATASET}...")
96
+ try:
97
+ path = hf_hub_download(
98
+ repo_id=DATASET,
99
+ filename="data.sqlite",
100
+ repo_type="dataset",
101
+ token=TOKEN,
102
+ local_dir="/tmp/hf_restore",
103
+ )
104
+ sz = os.path.getsize(path)
105
+ if sz > 4096:
106
+ import shutil
107
+ shutil.copy2(path, DB_FILE)
108
+ print(f"[restore] OK - {sz} bytes restored from backup")
109
+ else:
110
+ print(f"[restore] Backup too small ({sz}b), starting fresh")
111
+ except Exception as e:
112
+ print(f"[restore] No backup yet ({e}), starting fresh")
113
+ PYEOF2
114
 
115
  RUN cat > /start.sh << 'STARTEOF'
116
  #!/bin/sh
117
+ # 1. Restore DB from backup
118
+ python3 /restore_db.py
119
+ # 2. Start Next.js
120
  node /app/server.js &
121
  APP_PID=$!
122
+ # 3. Sync loop (default 1h)
123
+ INTERVAL="${SYNC_INTERVAL:-3600}"
124
+ (while true; do sleep "$INTERVAL"; python3 /sync_db.py; done) &
125
+ # 4. Sync on SIGTERM (Space sleeping)
126
+ trap "echo "[trap] Syncing before sleep..."; python3 /sync_db.py; kill $APP_PID 2>/dev/null; exit 0" TERM INT
127
  # 5. Wait
128
  wait $APP_PID
129
  STARTEOF
130
 
131
+ RUN chmod +x /start.sh
 
132
  CMD ["/bin/sh", "/start.sh"]