commit e89fd52a8933677c9e5b99d47dd9d635dcd9709e Author: Vlad Date: Fri Jul 11 11:54:23 2025 +0300 обавлен вывод температуры и облачности с weatherapi.com, настройки (api_key, lat, lon, cache_ttl) вынесены в config.yaml, поддержка настройки времени кэширования погоды diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..7b5526b --- /dev/null +++ b/changelog.md @@ -0,0 +1,18 @@ +# Changelog + +## [feature] Онлайн-редактирование config.yaml через веб-интерфейс +- Добавлены API-эндпоинты /api/config (GET, POST) для получения и сохранения настроек +- На главной странице добавлена кнопка "Редактировать настройки" с модальным окном для редактирования config.yaml +- Реализована валидация yaml при сохранении настроек +- После сохранения настроек происходит их немедленное применение +- Удалена старая кнопка перехода к настройкам +- Добавлены тесты на pytest для проверки работы API (чтение, сохранение валидного и невалидного yaml, обновление файла) +- Встроен Monaco Editor для YAML с подсветкой синтаксиса, проверкой ошибок и возможностью увеличивать окно редактора +- Добавлен вывод температуры и облачности с weatherapi.com, настройки (api_key, lat, lon, cache_ttl) вынесены в config.yaml +- Поддержка настройки времени кэширования погоды через weather.cache_ttl (минуты, по умолчанию 60) + +## [init] Стартовая инициализация структуры Flask-приложения +- Создана структура каталогов: templates/, static/ +- Вынесены данные Applications и Bookmarks в config.yaml +- Добавлен базовый шаблон для главной страницы +- Перенесены ассеты из example/home_files в static/ \ No newline at end of file diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..c9b1200 --- /dev/null +++ b/config.yaml @@ -0,0 +1,181 @@ +applications: + - name: XPenelogy + description: NAS + url: https://192.168.1.122:5001/ + icon_name: server + - name: wg_pannel + description: vpn + url: http://74.48.138.132:59877/ + icon_name: shield + - name: mino + description: minio + url: https://minio.ddl.su/ + icon_name: database + - name: PiHole X86 + description: PiHole X86 + url: https://192.168.1.11/admin + icon_name: activity + - name: Ngnix Proxy + description: Ngnix proxy manager + url: https://npm.ddl.su/ + icon_name: repeat + - name: Bit + description: Bitwarder + url: https://bit.ddl.su/#/login + icon_name: lock + - name: Router + description: Router main + url: http://192.168.1.1/ + icon_name: wifi + - name: Portainer + description: Portainer + url: https://portainer.ddl.su/ + icon_name: box + - name: Uptime + description: 192.168.1.222:3001/dashboard + url: http://192.168.1.222:3001/dashboard + icon_name: clock + - name: Gitlab + description: git + url: https://gitlab.ddl.su/ + icon_name: git-branch + - name: Grafana + description: 192.168.1.143:3000 + url: http://192.168.1.143:3000/ + icon_name: bar-chart-2 + - name: PVE1 + description: 192.168.1.201:8006 + url: https://192.168.1.201:8006/ + icon_name: cpu + - name: Docker log service + description: 192.168.1.222:9999 + url: http://192.168.1.222:9999/ + icon_name: file-text + - name: ha + description: 192.168.1.88:8123 + url: http://192.168.1.88:8123/ + icon_name: home + - name: mywiki + description: mywiki + url: https://wiki.ddl.su/ + icon_name: book-open + - name: VPN panel + description: panel.ddl.su + url: https://panel.ddl.su/ + icon_name: key + - name: Prometeus + description: 192.168.1.143:9090 + url: http://192.168.1.143:9090/ + icon_name: activity + - name: filebrowser + description: FileBrowser + url: http://st.ddl.su/ + icon_name: folder + - name: cron + description: cron + url: http://192.168.1.112:9000/ + icon_name: repeat + - name: Mikrotik Graphana + description: 192.168.1.148:3000 + url: http://192.168.1.148:3000/ + icon_name: bar-chart + - name: QS + description: Qnap work + url: https://qs.ddl.su/ + icon_name: hard-drive + +bookmarks: + - group: Hm + links: + - name: Gmail + url: https://mail.google.com/mail/u/0/?pli=1#inbox + icon_name: mail + - name: SynologyMail + url: https://192.168.1.122:5001/?launchApp=SYNO.SDS.MailClient.Application&SynoToken=S4WX9.Qjfum4w#inbox + icon_name: mail + - name: Yandex Mail + url: https://mail.yandex.ru/ + icon_name: mail + - name: Youtube + url: https://youtube.com/ + icon_name: youtube + - name: myip + url: http://192.168.1.90:8732/ + icon_name: globe + - group: Магазины + links: + - name: AVITO + url: https://www.avito.ru/ + icon_name: shopping-bag + - name: AliExpress + url: https://www.aliexpress.com/ + icon_name: shopping-cart + - name: OZON + url: https://www.ozon.ru/ + icon_name: shopping-cart + - name: Yandex Market + url: https://market.yandex.ru/ + icon_name: shopping-cart + - name: auto.ru + url: http://auto.ru/ + icon_name: truck + - group: Work + links: + - name: COPART + url: https://www.copart.com/ + icon_name: briefcase + - name: IAAI + url: https://www.iaai.com/ + icon_name: briefcase + - group: 3D + links: + - name: cults3d + url: https://cults3d.com/ + icon_name: cube + - name: printables + url: https://www.printables.com/ + icon_name: cube + - name: thingiverse + url: https://www.thingiverse.com/ + icon_name: cube + - group: VM + links: + - name: whatsapp + url: https://web.whatsapp.com/ + icon_name: message-circle + - group: DWH + links: + - name: 3ui gui + url: https://192.168.1.129:9443/ + icon_name: grid + - group: Salvagedb + links: + - name: Goaccess + url: http://185.87.192.205:6784/report.html + icon_name: bar-chart + - name: adsense + url: https://www.google.com/adsense/new/u/1/pub-2964481252074108/home + icon_name: dollar-sign + - name: analytics + url: https://metrika.yandex.ru/overview?id=99030590&period=today&group=hour&isMinSamplingEnabled=false + icon_name: pie-chart + - name: ngnix dashboard + url: http://192.168.1.143:3000/d/MsjffzSZz/nginx?orgId=1&refresh=5s&from=now-24h&to=now + icon_name: activity + - name: Salvagedb + url: https://salvagedb.com/ + icon_name: database + - group: VPN + links: + - name: 3ui gui + url: https://vip.salvagedb.com:59332/V8rzsW2LxuCZ5zI/ + icon_name: shield + - name: Amnezia Panel + url: https://vip.salvagedb.com:7443/ + icon_name: shield-off + +weather: + api_key: "8e548386d3c6492f8ef220308231903" + lat: 55.751244 + lon: 37,618423 + cache_ttl: 60 \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..a5cb7f4 --- /dev/null +++ b/main.py @@ -0,0 +1,89 @@ +from flask import Flask, render_template, request, jsonify, make_response +import yaml +from datetime import datetime +import os +import requests +import time + +app = Flask(__name__) + +CONFIG_PATH = 'config.yaml' + +# Кэш для погоды +weather_cache = {'data': None, 'ts': 0} + +def load_config(): + with open(CONFIG_PATH, encoding='utf-8') as f: + return yaml.safe_load(f) + +def save_config(yaml_text): + with open(CONFIG_PATH, 'w', encoding='utf-8') as f: + f.write(yaml_text) + +def get_weather_cache_ttl(): + config = load_config() + w = config.get('weather', {}) + ttl = w.get('cache_ttl') + try: + return int(ttl) * 60 if ttl else 3600 + except Exception: + return 3600 + + +def get_weather(): + global weather_cache + now = time.time() + WEATHER_CACHE_TTL = get_weather_cache_ttl() + if weather_cache['data'] and now - weather_cache['ts'] < WEATHER_CACHE_TTL: + return weather_cache['data'] + config = load_config() + w = config.get('weather', {}) + api_key = w.get('api_key') + lat = w.get('lat') + lon = w.get('lon') + if not api_key or not lat or not lon: + return None + url = f"https://api.weatherapi.com/v1/current.json?key={api_key}&q={lat},{lon}&lang=ru" + try: + resp = requests.get(url, timeout=5) + resp.raise_for_status() + data = resp.json() + temp = data['current']['temp_c'] + cloud = data['current']['cloud'] + result = {'temp': temp, 'cloud': cloud} + weather_cache = {'data': result, 'ts': now} + return result + except Exception: + return None + +@app.route('/api/config', methods=['GET']) +def get_config(): + try: + with open(CONFIG_PATH, encoding='utf-8') as f: + content = f.read() + return jsonify({'content': content}) + except Exception as e: + return make_response(jsonify({'error': str(e)}), 500) + +@app.route('/api/config', methods=['POST']) +def update_config(): + data = request.get_json() + yaml_text = data.get('content', '') + try: + # Проверка валидности yaml + yaml.safe_load(yaml_text) + save_config(yaml_text) + return jsonify({'status': 'ok'}) + except Exception as e: + return make_response(jsonify({'error': str(e)}), 400) + +config = load_config() + +@app.route('/') +def index(): + now = datetime.now().strftime('%A, %d %B %Y - %H:%M:%S') + weather = get_weather() + return render_template('index.html', applications=config['applications'], bookmarks=config['bookmarks'], now=now, weather=weather) + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..3218c82 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,166 @@ + + + + + Домашняя страница + + + + + + + + +
+
+
+
+

{{ now }}

+ Go to Settings + +

Good morning!

+
+
+
+ {% if weather %} + {{ weather.temp }}°C + {{ weather.cloud }}% облачность + {% else %} + --°C + --% облачность + {% endif %} +
+
+
+
+

Applications

+
+ {% for app in applications %} + +
+ +
+
+
{{ app.name }}
+ {{ app.description }} +
+
+ {% endfor %} +
+
+

Bookmarks

+
+ {% for group in bookmarks %} +
+

{{ group.group }}

+
+ {% for link in group.links %} + +
+ +
{{ link.name }} +
+ {% endfor %} +
+
+ {% endfor %} +
+ + + Редактировать настройки + +
+ +
+
+ + + + + + \ No newline at end of file