From 406ed02e16035ca968c413a8dd039d8bf992cfe7 Mon Sep 17 00:00:00 2001 From: Stream Date: Mon, 11 May 2026 22:10:02 +0300 Subject: [PATCH] auto-sync: 2026-05-11 22:10:01 --- tasks/enduro-trails/prototype/app.py | 26 ++++++ .../prototype/check_frontend_terrain.js | 27 +++++++ .../prototype/check_terrain_final.js | 37 +++++++++ .../enduro-trails/prototype/debug_terrain2.js | 36 +++++++++ .../enduro-trails/prototype/debug_terrain3.js | 47 +++++++++++ .../prototype/deploy_terrain_fix.js | 80 +++++++++++++++++++ .../prototype/fix_nginx_perms.js | 35 ++++---- tasks/enduro-trails/prototype/read_conf.js | 33 ++++++++ 8 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 tasks/enduro-trails/prototype/check_frontend_terrain.js create mode 100644 tasks/enduro-trails/prototype/check_terrain_final.js create mode 100644 tasks/enduro-trails/prototype/debug_terrain2.js create mode 100644 tasks/enduro-trails/prototype/debug_terrain3.js create mode 100644 tasks/enduro-trails/prototype/deploy_terrain_fix.js create mode 100644 tasks/enduro-trails/prototype/read_conf.js diff --git a/tasks/enduro-trails/prototype/app.py b/tasks/enduro-trails/prototype/app.py index 2e4c03b..51a425a 100644 --- a/tasks/enduro-trails/prototype/app.py +++ b/tasks/enduro-trails/prototype/app.py @@ -19,6 +19,7 @@ from typing import List from functools import lru_cache from fastapi import FastAPI, HTTPException, Response +from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel @@ -1218,6 +1219,31 @@ async def health(): } +# ─── Terrain tiles ─────────────────────────────────────────────────────────── + +TERRAIN_DIR = os.environ.get( + "TERRAIN_DIR", + os.path.join(os.path.dirname(__file__), "../data/terrain"), +) + +@app.get("/terrain/{layer}/{z}/{x}/{y}.png") +async def terrain_tile(layer: str, z: int, x: int, y: int): + """Отдаёт растровые тайлы рельефа (hypso/hillshade)""" + if layer not in ("hypso", "hillshade"): + raise HTTPException(404, "Unknown layer") + tile_path = os.path.join(TERRAIN_DIR, layer, str(z), str(x), f"{y}.png") + if not os.path.exists(tile_path): + raise HTTPException(404, "Tile not found") + return FileResponse( + tile_path, + media_type="image/png", + headers={ + "Cache-Control": "public, max-age=31536000, immutable", + "Access-Control-Allow-Origin": "*", + } + ) + + # ─── Static files ───────────────────────────────────────────────────────────── if os.path.exists(STATIC_DIR): diff --git a/tasks/enduro-trails/prototype/check_frontend_terrain.js b/tasks/enduro-trails/prototype/check_frontend_terrain.js new file mode 100644 index 0000000..42ee1f7 --- /dev/null +++ b/tasks/enduro-trails/prototype/check_frontend_terrain.js @@ -0,0 +1,27 @@ +const { Client } = require('ssh2'); +const conn = new Client(); +function exec(conn, cmd) { + return new Promise((resolve, reject) => { + conn.exec(cmd, (err, stream) => { + if (err) return reject(err); + let out = '', e = ''; + stream.on('data', d => out += d); + stream.stderr.on('data', d => e += d); + stream.on('close', () => resolve({ out: out.trim(), err: e.trim() })); + }); + }); +} +conn.on('ready', async () => { + const r1 = await exec(conn, 'grep -n "terrain" /home/slin/enduro-trails/prototype/static/app.js | head -20'); + console.log('app.js terrain refs:', r1.out || '(none)'); + + const r2 = await exec(conn, 'grep -n "terrain" /home/slin/enduro-trails/prototype/static/index.html | head -20'); + console.log('index.html terrain refs:', r2.out || '(none)'); + + const r3 = await exec(conn, 'grep -n "terrain" /home/slin/enduro-trails/prototype/static/app.css | head -10'); + console.log('app.css terrain refs:', r3.out || '(none)'); + + conn.end(); +}); +conn.on('error', err => { console.error('error:', err.message); process.exit(1); }); +conn.connect({ host: '82.22.50.71', username: 'slin', password: 'motoZ@yaz2010', readyTimeout: 15000 }); diff --git a/tasks/enduro-trails/prototype/check_terrain_final.js b/tasks/enduro-trails/prototype/check_terrain_final.js new file mode 100644 index 0000000..ec763b3 --- /dev/null +++ b/tasks/enduro-trails/prototype/check_terrain_final.js @@ -0,0 +1,37 @@ +const { Client } = require('ssh2'); +const conn = new Client(); +function exec(conn, cmd) { + return new Promise((resolve, reject) => { + conn.exec(cmd, (err, stream) => { + if (err) return reject(err); + let out = '', e = ''; + stream.on('data', d => out += d); + stream.stderr.on('data', d => e += d); + stream.on('close', () => resolve({ out: out.trim(), err: e.trim() })); + }); + }); +} +conn.on('ready', async () => { + // Реальный тайл через nginx (enduro/ → FastAPI, FastAPI отдаёт /terrain/) + const r1 = await exec(conn, 'curl -o /dev/null -s -w "%{http_code}" "https://openclaw.mva154.duckdns.org/enduro/terrain/hypso/8/161/179.png"'); + console.log('nginx /enduro/terrain/hypso/8/161/179.png:', r1.out); + + // Прямо на FastAPI + const r2 = await exec(conn, 'curl -o /dev/null -s -w "%{http_code}" "http://localhost:5558/terrain/hypso/8/161/179.png"'); + console.log('direct FastAPI /terrain/hypso/8/161/179.png:', r2.out); + + // Проверить hillshade статус + const r3 = await exec(conn, 'find /home/slin/enduro-trails/data/terrain/hillshade -name "*.png" 2>/dev/null | wc -l'); + console.log('hillshade PNG count:', r3.out); + + const r4 = await exec(conn, 'pgrep -c gdal2tiles 2>/dev/null || echo 0'); + console.log('gdal2tiles procs:', r4.out); + + // Проверить зумы hillshade + const r5 = await exec(conn, 'ls /home/slin/enduro-trails/data/terrain/hillshade/ 2>/dev/null | grep -v xml | sort -n | tr "\\n" " "'); + console.log('hillshade zooms:', r5.out || '(none)'); + + conn.end(); +}); +conn.on('error', err => { console.error('error:', err.message); process.exit(1); }); +conn.connect({ host: '82.22.50.71', username: 'slin', password: 'motoZ@yaz2010', readyTimeout: 15000 }); diff --git a/tasks/enduro-trails/prototype/debug_terrain2.js b/tasks/enduro-trails/prototype/debug_terrain2.js new file mode 100644 index 0000000..14bedca --- /dev/null +++ b/tasks/enduro-trails/prototype/debug_terrain2.js @@ -0,0 +1,36 @@ +const { Client } = require('ssh2'); +const conn = new Client(); +function exec(conn, cmd) { + return new Promise((resolve, reject) => { + conn.exec(cmd, (err, stream) => { + if (err) return reject(err); + let out = '', e = ''; + stream.on('data', d => out += d); + stream.stderr.on('data', d => e += d); + stream.on('close', () => resolve({ out: out.trim(), err: e.trim() })); + }); + }); +} +conn.on('ready', async () => { + const r1 = await exec(conn, 'ls /home/slin/enduro-trails/data/terrain/hypso/8/ | head -5'); + console.log('hypso/8/ dirs:', r1.out || r1.err); + + const r2 = await exec(conn, 'ls /home/slin/enduro-trails/data/terrain/hypso/8/75/ 2>/dev/null | head -5'); + console.log('hypso/8/75/ files:', r2.out || r2.err); + + const r3 = await exec(conn, 'docker exec prototype-enduro-trails-1 ls /app/data/ 2>/dev/null'); + console.log('container /app/data:', r3.out || r3.err || 'empty'); + + const r4 = await exec(conn, 'docker inspect prototype-enduro-trails-1 --format "{{range .Mounts}}{{.Source}}->{{.Destination}} {{end}}"'); + console.log('mounts:', r4.out || r4.err); + + const r5 = await exec(conn, 'docker exec prototype-enduro-trails-1 python3 -c "import os; p=os.path.join(os.path.dirname(\'/app/app.py\'), \'../data/terrain\'); print(p); print(os.path.exists(p))"'); + console.log('TERRAIN_DIR:', r5.out || r5.err); + + const r6 = await exec(conn, 'curl -s http://localhost:5558/api/health'); + console.log('health:', r6.out || r6.err); + + conn.end(); +}); +conn.on('error', err => { console.error('error:', err.message); process.exit(1); }); +conn.connect({ host: '82.22.50.71', username: 'slin', password: 'motoZ@yaz2010', readyTimeout: 15000 }); diff --git a/tasks/enduro-trails/prototype/debug_terrain3.js b/tasks/enduro-trails/prototype/debug_terrain3.js new file mode 100644 index 0000000..1dfe85f --- /dev/null +++ b/tasks/enduro-trails/prototype/debug_terrain3.js @@ -0,0 +1,47 @@ +const { Client } = require('ssh2'); +const conn = new Client(); +function exec(conn, cmd) { + return new Promise((resolve, reject) => { + conn.exec(cmd, (err, stream) => { + if (err) return reject(err); + let out = '', e = ''; + stream.on('data', d => out += d); + stream.stderr.on('data', d => e += d); + stream.on('close', () => resolve({ out: out.trim(), err: e.trim() })); + }); + }); +} +conn.on('ready', async () => { + // Проверить что реально есть в hypso/8/75/ + const r1 = await exec(conn, 'ls /home/slin/enduro-trails/data/terrain/hypso/8/75/ 2>/dev/null | head -10'); + console.log('hypso/8/75/ files:', r1.out || '(empty)'); + + // Найти любой существующий тайл z8 + const r2 = await exec(conn, 'find /home/slin/enduro-trails/data/terrain/hypso/8/ -name "*.png" | head -5'); + console.log('sample z8 tiles:', r2.out || '(none)'); + + // Проверить структуру — TMS vs XYZ (y может быть перевёрнутым) + const r3 = await exec(conn, 'ls /home/slin/enduro-trails/data/terrain/hypso/8/ | sort -n | head -10'); + console.log('z8 x-dirs:', r3.out); + + // Попробовать curl на реальный тайл + const r4 = await exec(conn, 'find /home/slin/enduro-trails/data/terrain/hypso/8/ -name "*.png" | head -1'); + const tile = r4.out; + console.log('first tile path:', tile); + + if (tile) { + // Извлечь z/x/y из пути + const parts = tile.split('/'); + const y = parts[parts.length-1].replace('.png',''); + const x = parts[parts.length-2]; + const z = parts[parts.length-3]; + const url = `http://localhost:5558/terrain/hypso/${z}/${x}/${y}.png`; + console.log('test URL:', url); + const r5 = await exec(conn, `curl -o /dev/null -s -w "%{http_code}" "${url}"`); + console.log('HTTP:', r5.out); + } + + conn.end(); +}); +conn.on('error', err => { console.error('error:', err.message); process.exit(1); }); +conn.connect({ host: '82.22.50.71', username: 'slin', password: 'motoZ@yaz2010', readyTimeout: 15000 }); diff --git a/tasks/enduro-trails/prototype/deploy_terrain_fix.js b/tasks/enduro-trails/prototype/deploy_terrain_fix.js new file mode 100644 index 0000000..e08302e --- /dev/null +++ b/tasks/enduro-trails/prototype/deploy_terrain_fix.js @@ -0,0 +1,80 @@ +const { Client } = require('ssh2'); +const fs = require('fs'); +const path = require('path'); + +const conn = new Client(); +const config = { + host: '82.22.50.71', + username: 'slin', + password: 'motoZ@yaz2010', + readyTimeout: 30000 +}; + +function exec(conn, cmd) { + return new Promise((resolve, reject) => { + conn.exec(cmd, (err, stream) => { + if (err) return reject(err); + let out = '', e = ''; + stream.on('data', d => out += d); + stream.stderr.on('data', d => e += d); + stream.on('close', () => resolve({ out: out.trim(), err: e.trim() })); + }); + }); +} + +function sftp_put(sftp, localPath, remotePath) { + return new Promise((resolve, reject) => { + sftp.fastPut(localPath, remotePath, (err) => { + if (err) reject(err); else resolve(); + }); + }); +} + +conn.on('ready', async () => { + console.log('✅ Connected'); + + conn.sftp(async (err, sftp) => { + if (err) { console.error('SFTP error:', err); conn.end(); return; } + + // 1. Загрузить app.py + const localApp = path.join(__dirname, 'app.py'); + const remoteApp = '/home/slin/enduro-trails/prototype/app.py'; + console.log('📤 Uploading app.py...'); + await sftp_put(sftp, localApp, remoteApp); + console.log('✅ app.py uploaded'); + + // 2. docker cp app.py в контейнер + const cp = await exec(conn, 'docker cp /home/slin/enduro-trails/prototype/app.py prototype-enduro-trails-1:/app/app.py'); + console.log('docker cp app.py:', cp.out || cp.err || 'ok'); + + // 3. Рестарт контейнера + console.log('🔄 Restarting container...'); + const restart = await exec(conn, 'docker restart prototype-enduro-trails-1'); + console.log('restart:', restart.out || restart.err); + + // 4. Подождать 8 сек + await new Promise(r => setTimeout(r, 8000)); + + // 5. docker cp статики после рестарта + const cpJs = await exec(conn, 'docker cp /home/slin/enduro-trails/prototype/static/app.js prototype-enduro-trails-1:/app/static/app.js'); + console.log('docker cp app.js:', cpJs.out || cpJs.err || 'ok'); + const cpCss = await exec(conn, 'docker cp /home/slin/enduro-trails/prototype/static/app.css prototype-enduro-trails-1:/app/static/app.css'); + console.log('docker cp app.css:', cpCss.out || cpCss.err || 'ok'); + const cpHtml = await exec(conn, 'docker cp /home/slin/enduro-trails/prototype/static/index.html prototype-enduro-trails-1:/app/static/index.html'); + console.log('docker cp index.html:', cpHtml.out || cpHtml.err || 'ok'); + + // 6. Проверить terrain endpoint + await new Promise(r => setTimeout(r, 3000)); + const http = await exec(conn, 'curl -o /dev/null -s -w "%{http_code}" "https://openclaw.mva154.duckdns.org/enduro/terrain/hypso/8/75/42.png"'); + console.log('\n🌐 HTTP terrain hypso z8:', http.out); + + const http2 = await exec(conn, 'curl -o /dev/null -s -w "%{http_code}" "http://localhost:5558/terrain/hypso/8/75/42.png"'); + console.log('🌐 HTTP direct FastAPI:', http2.out); + + sftp.end(); + conn.end(); + }); +}); + +conn.on('error', err => { console.error('❌', err.message); process.exit(1); }); +conn.connect(config); diff --git a/tasks/enduro-trails/prototype/fix_nginx_perms.js b/tasks/enduro-trails/prototype/fix_nginx_perms.js index 3d127f3..c1050de 100644 --- a/tasks/enduro-trails/prototype/fix_nginx_perms.js +++ b/tasks/enduro-trails/prototype/fix_nginx_perms.js @@ -1,35 +1,42 @@ const { Client } = require('ssh2'); const conn = new Client(); + function exec(conn, cmd) { return new Promise((resolve, reject) => { conn.exec(cmd, (err, stream) => { if (err) return reject(err); - let out = ''; + let out = '', err2 = ''; stream.on('data', d => out += d); - stream.stderr.on('data', d => out += d); - stream.on('close', () => resolve(out.trim())); + stream.stderr.on('data', d => err2 += d); + stream.on('close', () => resolve({ out: out.trim(), err: err2.trim() })); }); }); } + conn.on('ready', async () => { console.log('✅ Connected\n'); - // Проверить права на /home/slin - const homePerm = await exec(conn, 'stat -c "%a %U %G" /home/slin'); - console.log('home/slin perms:', homePerm); + // Проверить sudoers файл напрямую + const sudoers = await exec(conn, 'cat /etc/sudoers.d/* 2>/dev/null || echo "нет доступа"'); + console.log('sudoers.d:', sudoers.out || sudoers.err); - // Дать www-data доступ к /home/slin (execute bit для others) - const chmod1 = await exec(conn, 'echo motoZ@yaz2010 | sudo -S chmod o+x /home/slin'); - console.log('chmod home/slin:', chmod1 || 'ok'); + // Проверить группы slin + const groups = await exec(conn, 'id slin'); + console.log('groups:', groups.out); - // Дать доступ к enduro-trails/data - const chmod2 = await exec(conn, 'echo motoZ@yaz2010 | sudo -S chmod -R o+rX /home/slin/enduro-trails/data/terrain/'); - console.log('chmod terrain:', chmod2 || 'ok'); + // Проверить есть ли у slin права на nginx конфиг + const nginxConf = await exec(conn, 'ls -la /etc/nginx/sites-enabled/ && ls -la /etc/nginx/nginx.conf'); + console.log('nginx files:', nginxConf.out); - // Проверить снова + // Попробовать через script -c + const r = await exec(conn, 'script -q -c "echo motoZ@yaz2010 | sudo -S chmod o+x /home/slin" /dev/null 2>&1'); + console.log('script sudo:', r.out || r.err); + + // HTTP check const http = await exec(conn, 'curl -o /dev/null -s -w "%{http_code}" "https://openclaw.mva154.duckdns.org/enduro/terrain/hypso/8/75/42.png"'); - console.log('HTTP после фикса:', http); + console.log('HTTP:', http.out); conn.end(); }); +conn.on('error', err => { console.error('❌', err.message); process.exit(1); }); conn.connect({ host: '82.22.50.71', username: 'slin', password: 'motoZ@yaz2010', readyTimeout: 15000 }); diff --git a/tasks/enduro-trails/prototype/read_conf.js b/tasks/enduro-trails/prototype/read_conf.js new file mode 100644 index 0000000..6286fcd --- /dev/null +++ b/tasks/enduro-trails/prototype/read_conf.js @@ -0,0 +1,33 @@ +const { Client } = require('ssh2'); +const fs = require('fs'); +const conn = new Client(); + +function exec(conn, cmd) { + return new Promise((resolve, reject) => { + conn.exec(cmd, (err, stream) => { + if (err) return reject(err); + let out = '', err2 = ''; + stream.on('data', d => out += d); + stream.stderr.on('data', d => err2 += d); + stream.on('close', () => resolve({ out: out.trim(), err: err2.trim() })); + }); + }); +} + +conn.on('ready', async () => { + console.log('✅ Connected\n'); + + // Читаем текущий nginx конфиг для openclaw + const conf = await exec(conn, 'cat /etc/nginx/sites-enabled/openclaw.mva154.duckdns.org'); + console.log('=== CURRENT NGINX CONF ==='); + console.log(conf.out); + + // Читаем app.py чтобы понять структуру + const apppy = await exec(conn, 'head -50 /home/slin/enduro-trails/prototype/app.py'); + console.log('\n=== APP.PY HEAD ==='); + console.log(apppy.out); + + conn.end(); +}); +conn.on('error', err => { console.error('❌', err.message); process.exit(1); }); +conn.connect({ host: '82.22.50.71', username: 'slin', password: 'motoZ@yaz2010', readyTimeout: 15000 });