File size: 6,038 Bytes
262a183
2a2fa33
7bffa9f
2a2fa33
01e6a7a
ad533eb
c18cf88
 
956a293
47b9f31
2a2fa33
4c2e97f
ad533eb
9e8330a
 
 
 
c18cf88
 
9e8330a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9a9d9c6
9e8330a
 
 
 
 
 
 
 
 
4c2e97f
9e8330a
b7f676f
9e8330a
 
 
 
 
 
b7f676f
9e8330a
 
 
 
 
 
9a9d9c6
9e8330a
4c2e97f
b7f676f
9e8330a
 
 
 
 
 
 
4c2e97f
9a9d9c6
9e8330a
 
 
 
4c2e97f
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
FROM node:22-alpine
RUN apk add --no-cache sqlite sqlite-dev
WORKDIR /app
RUN npm install -g omniroute

# ๅŸบ็ก€็ฝ‘็ปœไธŽ็Žฏๅขƒ้…็ฝฎ
ENV PORT=7860
ENV OMNIROUTE_PORT=7860
ENV HOST=0.0.0.0
ENV NODE_ENV=production

# ๐Ÿ”‘ ่ฎพ็ฝฎๆ–ฐๅฏ†็ 
ENV INITIAL_PASSWORD=Amd3dnow!

# ๐Ÿ”‘ ไธ‹่ฝฝๆŽฅๅฃ็š„่ฎฟ้—ฎไปค็‰Œ๏ผˆๅœจ HF Space Settings โ†’ Variables ้‡Œ่ฆ†็›–่ฟ™ไธชๅ€ผ๏ผ๏ผ‰
ENV DOWNLOAD_TOKEN=change_me_please

EXPOSE 7860

# ๅ†™ๅ…ฅไธ‹่ฝฝๆœๅŠก่„šๆœฌ๏ผˆ็บฏ Node.js๏ผŒๆ— ้œ€้ขๅค–ไพ่ต–๏ผ‰
RUN cat > /app/download_server.js << 'EOF'
const http = require('http');
const fs   = require('fs');
const path = require('path');
const { execSync } = require('child_process');

const PORT  = 7861;
const TOKEN = process.env.DOWNLOAD_TOKEN || 'change_me_please';

// ๅ…่ฎธไธ‹่ฝฝ็š„ๅฎ‰ๅ…จ็›ฎๅฝ•็™ฝๅๅ•
const ALLOWED_DIRS = ['/data', '/root/.omniroute'];

function safeResolvePath(filename) {
  // ๅชๅ…่ฎธ็บฏๆ–‡ไปถๅ๏ผŒๆ‹’็ปไปปไฝ•่ทฏๅพ„ๅˆ†้š”็ฌฆ๏ผˆ้˜ฒ่ทฏๅพ„็ฉฟ่ถŠ๏ผ‰
  if (!filename || filename.includes('/') || filename.includes('\\') || filename.includes('..')) {
    return null;
  }
  // ไพๆฌกๅœจ็™ฝๅๅ•็›ฎๅฝ•ไธญๆŸฅๆ‰พๆ–‡ไปถ
  for (const dir of ALLOWED_DIRS) {
    const fullPath = path.join(dir, filename);
    if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
      return fullPath;
    }
  }
  return null;
}

const server = http.createServer((req, res) => {
  const url    = new URL(req.url, `http://localhost:${PORT}`);
  const token  = url.searchParams.get('token') || '';
  const route  = url.pathname;

  // โ”€โ”€ ็ปŸไธ€้‰ดๆƒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  // ไฝฟ็”จ้€ๅญ—็ฌฆๆฏ”่พƒ้˜ฒๆญขๆ—ถๅบๆ”ปๅ‡ป
  const tokenValid =
    token.length === TOKEN.length &&
    token.split('').every((c, i) => c === TOKEN[i]);

  if (!tokenValid) {
    res.writeHead(403, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Forbidden: invalid token' }));
    return;
  }

  // โ”€โ”€ GET /list โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  if (route === '/list') {
    const result = {};
    for (const dir of ALLOWED_DIRS) {
      try {
        result[dir] = fs.readdirSync(dir).map(name => {
          const stat = fs.statSync(path.join(dir, name));
          return { name, size: stat.size, mtime: stat.mtime };
        });
      } catch (_) {
        result[dir] = `directory not found`;
      }
    }
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify(result, null, 2));
    return;
  }

  // โ”€โ”€ GET /download?token=...&file=omni_storage.sqlite โ”€โ”€โ”€โ”€โ”€โ”€
  if (route === '/download') {
    const filename = url.searchParams.get('file') || '';
    const fullPath = safeResolvePath(filename);

    if (!fullPath) {
      res.writeHead(404, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: `File not found: ${filename}` }));
      return;
    }

    const stat = fs.statSync(fullPath);
    res.writeHead(200, {
      'Content-Type':        'application/octet-stream',
      'Content-Disposition': `attachment; filename="${filename}"`,
      'Content-Length':      stat.size,
    });
    fs.createReadStream(fullPath).pipe(res);
    console.log(`[download] ${fullPath} (${stat.size} bytes)`);
    return;
  }

  // โ”€โ”€ 404 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  res.writeHead(404, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ error: 'Unknown route', routes: ['/list', '/download'] }));
});

server.listen(PORT, '0.0.0.0', () => {
  console.log(`[download-server] listening on port ${PORT}`);
  console.log(`[download-server] routes: /list  /download?file=<filename>`);
});
EOF

CMD \
  # โ”€โ”€ 1. ๅฏๅŠจ่ฏŠๆ–ญๆ—ฅๅฟ— โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \
  echo "=== ๅญ˜ๅ‚จ่ฏŠๆ–ญๅผ€ๅง‹ ===" && \
  echo "--- /data ็›ฎๅฝ• ---" && \
  ls -lah /data 2>&1 || echo "/data ็›ฎๅฝ•ไธๅญ˜ๅœจ" && \
  echo "--- /root/.omniroute ็›ฎๅฝ• ---" && \
  ls -lah /root/.omniroute 2>&1 || echo "OmniRoute ็›ฎๅฝ•ไธๅญ˜ๅœจ" && \
  echo "--- ็ฃ็›˜ๆŒ‚่ฝฝๆƒ…ๅ†ต ---" && \
  df -h && \
  echo "=== ๅญ˜ๅ‚จ่ฏŠๆ–ญ็ป“ๆŸ ===" && \
  \
  # โ”€โ”€ 2. ๅผ€ๆœบไปŽๆŒไน…ๅŒ–ๅญ˜ๅ‚จๆขๅคๆ•ฐๆฎ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \
  if [ -f /data/omni_storage.sqlite ]; then \
    mkdir -p /root/.omniroute && \
    cp /data/omni_storage.sqlite /root/.omniroute/storage.sqlite && \
    echo "โœ… ๆขๅค storage.sqlite ๆˆๅŠŸ"; \
  else \
    echo "โš ๏ธ  /data/omni_storage.sqlite ไธๅญ˜ๅœจ๏ผŒ่ทณ่ฟ‡ๆขๅค"; \
  fi && \
  if [ -f /data/omni_settings.json ]; then \
    mkdir -p /root/.omniroute && \
    cp /data/omni_settings.json /root/.omniroute/settings.json && \
    echo "โœ… ๆขๅค settings.json ๆˆๅŠŸ"; \
  else \
    echo "โš ๏ธ  /data/omni_settings.json ไธๅญ˜ๅœจ๏ผŒ่ทณ่ฟ‡ๆขๅค"; \
  fi && \
  \
  # โ”€โ”€ 3. ๅŽๅฐ๏ผšๆฏ 60 ็ง’ๆŠŠๆ•ฐๆฎๅค‡ไปฝๅˆฐ /data โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \
  (while true; do \
    sleep 60; \
    mkdir -p /data; \
    [ -f /root/.omniroute/storage.sqlite ] && \
      cp /root/.omniroute/storage.sqlite /data/omni_storage.sqlite && \
      echo "๐Ÿ’พ [backup] storage.sqlite โ†’ /data"; \
    [ -f /root/.omniroute/settings.json  ] && \
      cp /root/.omniroute/settings.json  /data/omni_settings.json  && \
      echo "๐Ÿ’พ [backup] settings.json  โ†’ /data"; \
  done) & \
  \
  # โ”€โ”€ 4. ๅŽๅฐ๏ผšๅฏๅŠจไธ‹่ฝฝๆœๅŠก๏ผˆ7861 ็ซฏๅฃ๏ผ‰โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  \
  node /app/download_server.js & \
  \
  # โ”€โ”€ 5. ๅ‰ๅฐ๏ผšๅฏๅŠจไธป็จ‹ๅบ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \
  PORT=7860 omniroute