FROM node:20 RUN apt-get update && apt-get install -y \ python3 make g++ git \ && rm -rf /var/lib/apt/lists/* WORKDIR /app RUN git clone https://github.com/KayolEnjoy/ai-sawit.git . RUN npm install --omit=dev --no-audit RUN npm install mysql2 http-proxy --save RUN npm run download-dist || echo "skip" RUN mkdir -p /app/data && chmod -R 777 /app/data RUN node -e "\ var fs=require('fs'),path=require('path');\ var d=path.dirname(require.resolve('mysql2'));\ var f=path.join(d,'lib','connection_config.js');\ var s=fs.readFileSync(f,'utf8');\ fs.writeFileSync(f,s.replace(/options\.ssl\s*\|\|\s*false/g,'options.ssl||{rejectUnauthorized:false}'));\ var p=path.join(d,'package.json');\ var j=JSON.parse(fs.readFileSync(p,'utf8'));\ delete j.exports;\ fs.writeFileSync(p,JSON.stringify(j));\ console.log('[PATCH] TLS+PKG OK');" EXPOSE 7860 RUN cat > /app/patch.js << 'PATCHEOF' var C = require('mysql2/lib/connection'); var _q = C.prototype.query; var _e = C.prototype.execute; function fix(s) { if (typeof s !== 'string') return s; s = s.replace(/default\s*\(('[^']*')\)/gi, 'default $1'); s = s.replace(/(`\w+`\s+(?:text|tinytext|mediumtext|longtext|blob|tinyblob|mediumblob|longblob|json)(?:\s+not\s+null)?)\s+default\s+'[^']*'/gi, '$1'); if (/^\s*alter\s+table\b/i.test(s) && /\bforeign\s+key\b/i.test(s)) return 'SELECT 1'; s = s.replace(/,\s*(?:constraint\s+`[^`]+`\s+)?foreign\s+key\s*\([^)]+\)\s*references\s+`[^`]+`\s*\([^)]+\)(?:\s+on\s+(?:delete|update)\s+(?:cascade|restrict|set\s+null|no\s+action|set\s+default))*/gi, ''); return s; } C.prototype.query = function(sql, values, cb) { if (typeof sql === 'string') sql = fix(sql); else if (sql && typeof sql.sql === 'string') sql.sql = fix(sql.sql); return _q.call(this, sql, values, cb); }; if (_e) { C.prototype.execute = function(sql, values, cb) { if (typeof sql === 'string') sql = fix(sql); else if (sql && typeof sql.sql === 'string') sql.sql = fix(sql.sql); return _e.call(this, sql, values, cb); }; } console.log('[SQL] patched'); PATCHEOF RUN cat > /app/init.js << 'INITEOF' var fs = require('fs'); var raw = process.env.DATABASE_URL || ''; if (raw) { var clean = raw.replace(/^mysql[^:]*:\/\//, ''); var ai = clean.lastIndexOf('@'); var creds = clean.substring(0, ai); var rest = clean.substring(ai + 1); var ci = creds.indexOf(':'); var user = creds.substring(0, ci); var pass = creds.substring(ci + 1); var si = rest.indexOf('/'); var hp = si !== -1 ? rest.substring(0, si) : rest; var db = si !== -1 ? rest.substring(si + 1).split('?')[0] : 'uptime_kuma'; var lc = hp.lastIndexOf(':'); var host = lc !== -1 ? hp.substring(0, lc) : hp; var port = lc !== -1 ? parseInt(hp.substring(lc + 1)) : 4000; var cfg = { type: 'mariadb', hostname: host, port: port, username: user, password: pass, dbName: db }; try { fs.mkdirSync('/app/data', { recursive: true }); } catch(e) {} fs.writeFileSync('/app/data/db-config.json', JSON.stringify(cfg, null, 4)); console.log('[INIT] DB: ' + host + ':' + port + '/' + db + ' user=' + user); } else { console.log('[INIT] No DATABASE_URL'); } INITEOF RUN cat > /app/gateway.js << 'GWEOF' var http = require('http'); var httpProxy = require('http-proxy'); var DOMAIN = process.env.DOMAIN || ''; var proxy = httpProxy.createProxyServer({ target: 'http://127.0.0.1:3001', ws: true }); proxy.on('error', function(e, req, res) { if (res && res.writeHead) { res.writeHead(502); res.end('502'); } }); function ok(req) { if (!DOMAIN) return true; var h = (req.headers['x-real-domain'] || '') + '|' + (req.headers['x-forwarded-host'] || '') + '|' + (req.headers['host'] || ''); return h.indexOf(DOMAIN) !== -1; } var s = http.createServer(function(req, res) { if (ok(req)) { proxy.web(req, res); } else { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('200 OK'); } }); s.on('upgrade', function(req, socket, head) { if (ok(req)) { proxy.ws(req, socket, head); } else { socket.destroy(); } }); s.listen(7860, function() { console.log('[GW] 7860'); }); GWEOF RUN cat > /start.sh << 'SHEOF' #!/bin/sh node /app/init.js PORT=3001 node --require /app/patch.js server/server.js --port 3001 & sleep 5 exec node /app/gateway.js SHEOF RUN chmod +x /start.sh CMD ["/start.sh"]