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')