auto-sync: 2026-05-11 22:10:01

This commit is contained in:
Stream
2026-05-11 22:10:02 +03:00
parent 56f9a597b9
commit 406ed02e16
8 changed files with 307 additions and 14 deletions

View File

@@ -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):

View File

@@ -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 });

View File

@@ -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 });

View File

@@ -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 });

View File

@@ -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 });

View File

@@ -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);

View File

@@ -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 });

View File

@@ -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 });