184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
from flask import Flask, render_template, request, jsonify, make_response
|
||
import yaml
|
||
from datetime import datetime
|
||
import os
|
||
import requests
|
||
import time
|
||
import locale
|
||
import socket
|
||
from threading import Thread
|
||
|
||
app = Flask(__name__)
|
||
|
||
CONFIG_PATH = 'config.yaml'
|
||
|
||
# Кэш для погоды
|
||
weather_cache = {'data': None, 'ts': 0}
|
||
|
||
# Кэш для прогноза
|
||
weather_forecast_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']
|
||
condition = data['current'].get('condition', {})
|
||
icon = condition.get('icon', '')
|
||
text = condition.get('text', '')
|
||
result = {'temp': temp, 'cloud': cloud, 'icon': icon, 'text': text}
|
||
weather_cache = {'data': result, 'ts': now}
|
||
return result
|
||
except Exception:
|
||
return None
|
||
|
||
def get_weather_forecast():
|
||
global weather_forecast_cache
|
||
now = time.time()
|
||
WEATHER_CACHE_TTL = get_weather_cache_ttl()
|
||
if weather_forecast_cache['data'] and now - weather_forecast_cache['ts'] < WEATHER_CACHE_TTL:
|
||
return weather_forecast_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:
|
||
raise Exception('Не заданы api_key, lat или lon')
|
||
url = f"https://api.weatherapi.com/v1/forecast.json?key={api_key}&q={lat},{lon}&days=10&lang=ru"
|
||
try:
|
||
resp = requests.get(url, timeout=5)
|
||
resp.raise_for_status()
|
||
data = resp.json()
|
||
forecast = []
|
||
for day in data['forecast']['forecastday']:
|
||
d = day['day']
|
||
temp = d.get('avgtemp_c')
|
||
if temp is None:
|
||
temp = d.get('maxtemp_c')
|
||
cloud = d.get('cloud', 0)
|
||
condition = d.get('condition', {})
|
||
forecast.append({
|
||
'date': day['date'],
|
||
'temp': temp,
|
||
'cloud': cloud,
|
||
'text': condition.get('text', ''),
|
||
'icon': condition.get('icon', '')
|
||
})
|
||
weather_forecast_cache = {'data': forecast, 'ts': now}
|
||
return forecast
|
||
except Exception as e:
|
||
try:
|
||
return {'_error': resp.text}
|
||
except:
|
||
return {'_error': str(e)}
|
||
|
||
def check_services_status(services, timeout=2):
|
||
"""
|
||
Проверяет доступность всех сервисов из списка.
|
||
Возвращает True, если все доступны, иначе False.
|
||
"""
|
||
import requests
|
||
for srv in services:
|
||
url = srv.get('url')
|
||
name = srv.get('name', url)
|
||
try:
|
||
resp = requests.get(url, timeout=timeout, verify=False)
|
||
if not (200 <= resp.status_code < 400):
|
||
return False, name
|
||
except Exception:
|
||
return False, name
|
||
return True, 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)
|
||
|
||
@app.route('/api/weather-forecast', methods=['GET'])
|
||
def api_weather_forecast():
|
||
forecast = get_weather_forecast()
|
||
if forecast is None:
|
||
return {'error': 'Ошибка получения прогноза'}, 500
|
||
if isinstance(forecast, dict) and '_error' in forecast:
|
||
return {'error': forecast['_error']}, 500
|
||
return {'forecast': forecast}
|
||
|
||
@app.route('/api/services-status', methods=['GET'])
|
||
def api_services_status():
|
||
config = load_config()
|
||
check_online = config.get('checkOnline', [])
|
||
status, failed_name = check_services_status(check_online) if check_online else (None, None)
|
||
return jsonify({'all_services_up': status, 'failed_service': failed_name})
|
||
|
||
# config = load_config() # Удаляем глобальную переменную
|
||
|
||
@app.route('/')
|
||
def index():
|
||
# Словари для русских дат
|
||
days = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота', 'Воскресенье']
|
||
months = [
|
||
'', 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
|
||
'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'
|
||
]
|
||
now = datetime.now()
|
||
day = days[now.weekday()]
|
||
month = months[now.month]
|
||
now_str = f"{day}, {now.day} {month} {now.year}"
|
||
current_time = now.strftime('%H:%M:%S')
|
||
weather = get_weather()
|
||
config = load_config() # Загружаем актуальный конфиг при каждом запросе
|
||
return render_template('index.html', applications=config['applications'], bookmarks=config['bookmarks'], now=now_str, current_time=current_time, weather=weather)
|
||
|
||
if __name__ == '__main__':
|
||
app.run(debug=True, host='0.0.0.0')
|