599 lines
22 KiB
Python
599 lines
22 KiB
Python
from flask import Flask, redirect, request, jsonify, render_template, url_for
|
|
from config import Config
|
|
from db import (
|
|
BirthdateScheduler,
|
|
PostScheduler,
|
|
Protection,
|
|
UsersBirthdate,
|
|
UsersMedods,
|
|
VkAPI,
|
|
VkPost,
|
|
db,
|
|
MedodsAPI,
|
|
ApiEndpoint,
|
|
)
|
|
from medods_handler import updateMedodsUsers, updateUsersBirthdate
|
|
from scheduler import (
|
|
enable_birthdate_job,
|
|
get_birthdate_scheduler_status,
|
|
get_scheduler_status,
|
|
init_scheduler,
|
|
enable_publish_job,
|
|
)
|
|
from http_client import send_request
|
|
import logging
|
|
import os
|
|
|
|
|
|
app = Flask(__name__)
|
|
app.config.from_object(Config)
|
|
|
|
db.init_app(app)
|
|
init_scheduler(app)
|
|
|
|
os.makedirs("logs", exist_ok=True)
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
handlers=[
|
|
logging.FileHandler(Config.LOG_FILE, encoding="utf-8"),
|
|
logging.StreamHandler(),
|
|
],
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def getLogs():
|
|
from collections import deque
|
|
|
|
with open(Config.LOG_FILE, "r", encoding="utf-8") as f:
|
|
last_lines = deque(f, maxlen=500)
|
|
return "".join(reversed(last_lines))
|
|
|
|
|
|
@app.route("/", methods=["GET", "POST"])
|
|
def index():
|
|
match request.method:
|
|
case "GET":
|
|
exitData = {}
|
|
medodsApi = MedodsAPI.query.first()
|
|
if medodsApi:
|
|
exitData["medodsApi"] = {
|
|
"updated_at": medodsApi.updated_at.strftime("%Y-%m-%d %H:%M:%S")
|
|
}
|
|
if medodsApi.url:
|
|
exitData["medodsApi"]["url"] = True
|
|
else:
|
|
exitData["medodsApi"]["url"] = False
|
|
if medodsApi.identity and medodsApi.secretKey:
|
|
exitData["medodsApi"]["apiKey"] = True
|
|
else:
|
|
exitData["medodsApi"]["apiKey"] = False
|
|
apiEndpointsCount = ApiEndpoint.query.count()
|
|
exitData["medodsApi"]["apiEndpointsCount"] = apiEndpointsCount
|
|
vkApi = VkAPI.query.first()
|
|
if vkApi:
|
|
exitData["vkApi"] = {
|
|
"updated_at": vkApi.updated_at.strftime("%Y-%m-%d %H:%M:%S")
|
|
}
|
|
if vkApi.group_id:
|
|
exitData["vkApi"]["group_id"] = True
|
|
else:
|
|
exitData["vkApi"]["group_id"] = False
|
|
if vkApi.access_token:
|
|
exitData["vkApi"]["access_token"] = True
|
|
else:
|
|
exitData["vkApi"]["access_token"] = False
|
|
if vkApi.base_photo_url:
|
|
exitData["vkApi"]["base_photo_url"] = True
|
|
else:
|
|
exitData["vkApi"]["base_photo_url"] = False
|
|
vkPost = VkPost.query.first()
|
|
if vkPost:
|
|
exitData["vkPost"] = {
|
|
"updated_at": vkPost.updated_at.strftime("%Y-%m-%d %H:%M:%S")
|
|
}
|
|
if vkPost.static_text:
|
|
exitData["vkPost"]["static_text"] = True
|
|
else:
|
|
exitData["vkPost"]["static_text"] = False
|
|
if vkPost.selected_users:
|
|
exitData["vkPost"]["selected_users"] = len(vkPost.selected_users)
|
|
else:
|
|
exitData["vkPost"]["selected_users"] = False
|
|
if vkPost.full_name is not None:
|
|
exitData["vkPost"]["full_name"] = vkPost.full_name
|
|
if vkPost.post_id and vkApi and vkApi.group_id:
|
|
exitData["vkPost"][
|
|
"post_link"
|
|
] = f"https://vk.com/wall-{vkApi.group_id}_{vkPost.post_id}"
|
|
else:
|
|
exitData["vkPost"]["post_link"] = False
|
|
if vkPost.publish_at:
|
|
exitData["vkPost"]["publish_at"] = vkPost.publish_at.strftime(
|
|
"%Y-%m-%d %H:%M:%S"
|
|
)
|
|
else:
|
|
exitData["vkPost"]["publish_at"] = False
|
|
usersMedods = UsersMedods.query.count()
|
|
exitData["vkPost"]["usersMedods"] = usersMedods
|
|
exitData["vkPost"]["scheduler"] = get_scheduler_status()
|
|
|
|
return render_template("index.html", exitData=exitData)
|
|
case "POST":
|
|
return jsonify(logs=getLogs())
|
|
case _:
|
|
return "Method not allowed", 405
|
|
|
|
|
|
@app.route("/medods", methods=["GET"])
|
|
def medods():
|
|
medods_api = MedodsAPI.query.first()
|
|
data = {}
|
|
if medods_api:
|
|
apiKey = False
|
|
if medods_api.identity and medods_api.secretKey:
|
|
apiKey = True
|
|
data = {
|
|
"url": medods_api.url,
|
|
"apiKey": apiKey,
|
|
}
|
|
return render_template("medods.html", data=data)
|
|
|
|
|
|
@app.route("/vk", methods=["GET"])
|
|
def vk():
|
|
vkDB = VkAPI.query.first()
|
|
data = {}
|
|
if vkDB:
|
|
data = {"vk_settings": vkDB.toDict()}
|
|
return render_template("vk.html", data=data)
|
|
|
|
|
|
@app.route("/posts", methods=["GET"])
|
|
def posts():
|
|
medodsUsers = UsersMedods.query.all()
|
|
if medodsUsers:
|
|
medodsUsers = [user.toDict() for user in medodsUsers]
|
|
vkPost = VkPost.query.first()
|
|
if vkPost:
|
|
vkPost = vkPost.toDict()
|
|
schedulerStatus = get_scheduler_status()
|
|
schedulerSettings = PostScheduler.query.first()
|
|
if schedulerSettings:
|
|
schedulerSettings = schedulerSettings.toDict()
|
|
return render_template(
|
|
"posts.html",
|
|
data={
|
|
"medodsUsers": medodsUsers,
|
|
"vkPost": vkPost,
|
|
"schedulerStatus": schedulerStatus,
|
|
"schedulerSettings": schedulerSettings,
|
|
},
|
|
)
|
|
|
|
|
|
@app.route("/birthdate", methods=["GET"])
|
|
def birthdate():
|
|
schedulerStatus = get_birthdate_scheduler_status()
|
|
schedulerSettings = BirthdateScheduler.query.first()
|
|
if schedulerSettings:
|
|
schedulerSettings = schedulerSettings.toDict()
|
|
return render_template(
|
|
"birthdate.html",
|
|
data={
|
|
"schedulerStatus": schedulerStatus,
|
|
"schedulerSettings": schedulerSettings,
|
|
},
|
|
)
|
|
|
|
|
|
@app.route("/api/medods", methods=["POST"])
|
|
def api_medods():
|
|
try:
|
|
data = request.json
|
|
apiKey = data.get("apiKey", None)
|
|
url = data.get("url", None)
|
|
if url is not None:
|
|
logger.info("Получен url")
|
|
try:
|
|
medodsRecord = MedodsAPI.query.first()
|
|
medodsRecord.url = url
|
|
db.session.commit()
|
|
logger.info("Обновлен url")
|
|
except Exception:
|
|
db.session.merge(MedodsAPI(url=url))
|
|
db.session.commit()
|
|
logger.info("Добавлен url")
|
|
|
|
if apiKey:
|
|
logger.info("Получены ключи")
|
|
try:
|
|
medodsRecord = MedodsAPI.query.first()
|
|
medodsRecord.identity = apiKey["identity"]
|
|
medodsRecord.secretKey = apiKey["secretKey"]
|
|
db.session.commit()
|
|
logger.info("Обновлены ключи")
|
|
except Exception:
|
|
db.session.merge(
|
|
MedodsAPI(
|
|
identity=apiKey["identity"], secretKey=apiKey["secretKey"]
|
|
)
|
|
)
|
|
db.session.commit()
|
|
logger.info("Добавлены ключи")
|
|
return jsonify({"ok": True})
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при обновлении ключей: {e}")
|
|
return jsonify({"ok": False}), 500
|
|
|
|
|
|
@app.route("/api/requests", methods=["GET", "POST", "PATCH", "DELETE"])
|
|
def api_requests():
|
|
requestData = (
|
|
request.json if request.method in ["POST", "PATCH", "DELETE"] else None
|
|
)
|
|
match request.method:
|
|
case "DELETE":
|
|
try:
|
|
db.session.execute(
|
|
db.delete(ApiEndpoint).where(ApiEndpoint.id == requestData["id"])
|
|
)
|
|
db.session.commit()
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при удалении запроса {requestData['id']}: {e}")
|
|
logger.info("Удален запрос")
|
|
|
|
return jsonify({"status": "ok"})
|
|
case "POST":
|
|
logger.info("Добавлен/обновлен запрос")
|
|
if "id" in requestData:
|
|
logger.info("Обновлен запрос")
|
|
try:
|
|
db.session.execute(
|
|
db.update(ApiEndpoint)
|
|
.where(ApiEndpoint.id == requestData["id"])
|
|
.values(
|
|
method=requestData.get("method"),
|
|
title=requestData.get("title"),
|
|
url_path=requestData.get("url_path"),
|
|
payload=requestData.get("payload", {}),
|
|
query_params=requestData.get("query_params", {}),
|
|
)
|
|
)
|
|
db.session.commit()
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при обновлении запроса: {e}")
|
|
return jsonify({"status": "ok"})
|
|
else:
|
|
logger.info("Добавлен запрос")
|
|
try:
|
|
db.session.merge(ApiEndpoint(**requestData))
|
|
db.session.commit()
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при добавлении запроса: {e}")
|
|
return jsonify({"status": "ok"})
|
|
|
|
case "PATCH":
|
|
logger.info("Выполнен запрос")
|
|
try:
|
|
requestParams = ApiEndpoint.query.filter_by(
|
|
id=requestData["id"]
|
|
).first()
|
|
medodsDB = MedodsAPI.query.first()
|
|
baseUrl = medodsDB.url
|
|
response = send_request(
|
|
requestParams.method,
|
|
f"{baseUrl}{requestParams.url_path}",
|
|
requestParams.payload,
|
|
requestParams.query_params,
|
|
)
|
|
exitData = {}
|
|
try:
|
|
exitData = response.json()
|
|
except:
|
|
exitData = response.text
|
|
return jsonify(exitData)
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при выполнении запроса: {e}")
|
|
return jsonify({"status": "error"}), 500
|
|
|
|
case "GET":
|
|
logger.info("Получен список запросов")
|
|
|
|
requestsDB = ApiEndpoint.query.all()
|
|
if requestsDB:
|
|
requestsList = [r.toDict() for r in requestsDB]
|
|
return jsonify(
|
|
{
|
|
"status": "ok",
|
|
"requests": requestsList,
|
|
}
|
|
)
|
|
|
|
case _:
|
|
logger.error("Неверный метод запроса")
|
|
return jsonify({"status": "error"}), 405
|
|
|
|
|
|
@app.route("/api/birthdate", methods=["GET", "POST", "PATCH"])
|
|
def api_birthdate():
|
|
match request.method:
|
|
case "POST":
|
|
reqData = request.json
|
|
userUpdate = reqData.get("userUpdate", None)
|
|
if userUpdate:
|
|
logger.info("Обновлен пользователь")
|
|
userId = userUpdate["userId"]
|
|
userData = userUpdate["userData"]
|
|
try:
|
|
userDB = UsersBirthdate.query.filter_by(id=userId).first()
|
|
userDB.photo_link = userData["photoLink"]
|
|
userDB.congratulations = userData["congratulations"]
|
|
db.session.commit()
|
|
return jsonify({"status": "ok"})
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при обновлении пользователя: {e}")
|
|
return jsonify({"status": "error"}), 500
|
|
scheduleSettings = reqData.get("scheduleSettings")
|
|
if scheduleSettings:
|
|
logger.info("Обновлены настройки расписания")
|
|
try:
|
|
scheduleDB = BirthdateScheduler.query.first()
|
|
if scheduleDB:
|
|
scheduleDB.hour = scheduleSettings["hour"]
|
|
scheduleDB.minute = scheduleSettings["minute"]
|
|
scheduleDB.enabled = scheduleSettings["enabled"]
|
|
else:
|
|
scheduleDB = BirthdateScheduler(
|
|
hour=scheduleSettings["hour"],
|
|
minute=scheduleSettings["minute"],
|
|
enabled=scheduleSettings["enabled"],
|
|
)
|
|
db.session.add(scheduleDB)
|
|
db.session.commit()
|
|
enable_birthdate_job()
|
|
scheduleInfo = get_birthdate_scheduler_status()
|
|
return jsonify(
|
|
{
|
|
"status": "ok",
|
|
"next_run_time": scheduleInfo.get("next_run_time"),
|
|
}
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при обновлении настройки расписания: {e}")
|
|
return jsonify({"status": "error"}), 500
|
|
|
|
case "GET":
|
|
logger.info("Получен список пользователей")
|
|
|
|
users = UsersBirthdate.query.all()
|
|
if users:
|
|
users = [u.toDict() for u in users]
|
|
return jsonify(
|
|
{
|
|
"status": "ok",
|
|
"users": users,
|
|
}
|
|
)
|
|
|
|
case "PATCH":
|
|
success = updateUsersBirthdate()
|
|
if success:
|
|
return jsonify({"status": "ok"})
|
|
else:
|
|
return jsonify({"status": "error"}), 500
|
|
|
|
case _:
|
|
logger.error("Неверный метод запроса")
|
|
return jsonify({"status": "error"}), 405
|
|
|
|
|
|
@app.route("/api/vk", methods=["POST"])
|
|
def api_vk():
|
|
requestData = request.json
|
|
if "id" in requestData:
|
|
logger.info("Обновлен запрос")
|
|
try:
|
|
db.session.execute(
|
|
db.update(VkAPI)
|
|
.where(VkAPI.id == requestData["id"])
|
|
.values(
|
|
group_id=(
|
|
int(requestData.get("group_id", 0))
|
|
if requestData.get("group_id")
|
|
else 0
|
|
),
|
|
access_token=requestData.get("access_token"),
|
|
base_photo_url=(
|
|
int(requestData.get("base_photo_url", 0))
|
|
if requestData.get("base_photo_url")
|
|
else 0
|
|
),
|
|
)
|
|
)
|
|
db.session.commit()
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при обновлении запроса: {e}")
|
|
return jsonify({"status": "ok"})
|
|
else:
|
|
logger.info("Добавлен запрос")
|
|
try:
|
|
db.session.merge(VkAPI(**requestData))
|
|
db.session.commit()
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при добавлении запроса: {e}")
|
|
return jsonify({"status": "ok"})
|
|
|
|
|
|
@app.route("/api/posts", methods=["POST", "GET"])
|
|
def api_posts():
|
|
response = {"status": "ok"}
|
|
match request.method:
|
|
case "POST":
|
|
requestData = request.json
|
|
logger.info("Настройки публикации и расписания")
|
|
vkPostData = requestData.get("vkPostData", None)
|
|
if vkPostData:
|
|
selectedUsers = vkPostData.get("selectedUsers", None)
|
|
static_text = vkPostData.get("static_text", None)
|
|
full_name = vkPostData.get("full_name", None)
|
|
logger.info("Обновление настроек публикации")
|
|
try:
|
|
vkPost = VkPost.query.first()
|
|
if vkPost:
|
|
if selectedUsers:
|
|
vkPost.selected_users = selectedUsers
|
|
if static_text:
|
|
vkPost.static_text = static_text
|
|
if full_name is not None:
|
|
vkPost.full_name = full_name
|
|
else:
|
|
vkPostData["selected_users"] = vkPostData.pop("selectedUsers")
|
|
db.session.merge(VkPost(**vkPostData))
|
|
db.session.commit()
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при обновлении настроек публикации: {e}")
|
|
schedulerData = requestData.get("schedulerData", None)
|
|
if schedulerData:
|
|
logger.info("Обновление расписания публикации")
|
|
try:
|
|
scheduler = PostScheduler.query.first()
|
|
hour = schedulerData.get("hour", None)
|
|
minute = schedulerData.get("minute", None)
|
|
enabled = schedulerData.get("enabled", None)
|
|
if scheduler:
|
|
if hour is not None:
|
|
if scheduler.hour != hour:
|
|
scheduler.hour = hour
|
|
if minute is not None:
|
|
if scheduler.minute != minute:
|
|
scheduler.minute = minute
|
|
if enabled is not None:
|
|
if scheduler.enabled != enabled:
|
|
scheduler.enabled = enabled
|
|
else:
|
|
db.session.merge(
|
|
PostScheduler(
|
|
hour=int(hour),
|
|
minute=int(minute),
|
|
enabled=enabled,
|
|
)
|
|
)
|
|
db.session.commit()
|
|
enable_publish_job()
|
|
scheduleInfo = get_scheduler_status()
|
|
response["next_run_time"] = scheduleInfo.get("next_run_time")
|
|
except Exception as e:
|
|
logger.error(f"Ошибка при обновлении расписания публикации: {e}")
|
|
return jsonify(response)
|
|
|
|
case "GET":
|
|
queryParams = request.args.to_dict()
|
|
action = queryParams.get("action", None)
|
|
if action:
|
|
match action:
|
|
case "update_users":
|
|
logger.info("Обновить список пользователей")
|
|
result = updateMedodsUsers()
|
|
return jsonify({"ok": result})
|
|
case "handle_posts":
|
|
from vk_handler import handle_vk_post
|
|
|
|
logger.info("Выполнить публикацию")
|
|
handle_vk_post()
|
|
return jsonify({"ok": True})
|
|
case _:
|
|
logger.error("Неверный метод запроса")
|
|
return jsonify({"status": "error"}), 405
|
|
return jsonify({"ok": False, "status": "error", "message": "no action"})
|
|
|
|
case _:
|
|
logger.error("Неверный метод запроса")
|
|
return jsonify({"status": "error"}), 405
|
|
|
|
|
|
@app.route("/login", methods=["GET", "POST", "PATCH"])
|
|
def login():
|
|
match request.method:
|
|
case "GET":
|
|
return render_template("login.html")
|
|
case "POST":
|
|
data = request.get_json()
|
|
password = data.get("password")
|
|
|
|
protection = Protection.query.first()
|
|
if protection:
|
|
if not protection.verify_password(password):
|
|
return (
|
|
jsonify({"status": "error", "errorMessage": "Неверный пароль"}),
|
|
401,
|
|
)
|
|
else:
|
|
protection.generate_token()
|
|
db.session.commit()
|
|
else:
|
|
protection = Protection()
|
|
protection.set_password(password)
|
|
protection.generate_token()
|
|
|
|
db.session.add(protection)
|
|
db.session.commit()
|
|
|
|
return jsonify({"status": "ok", "token": protection.token}), 200
|
|
case "PATCH":
|
|
data = request.get_json()
|
|
new_password = data.get("password")
|
|
|
|
if not new_password:
|
|
return {"status": "error", "errorMessage": "Пароль не задан"}, 400
|
|
|
|
protection = Protection.query.first()
|
|
if not protection:
|
|
protection = Protection()
|
|
protection.set_password(new_password)
|
|
protection.generate_token()
|
|
|
|
db.session.add(protection)
|
|
db.session.commit()
|
|
else:
|
|
protection.set_password(new_password)
|
|
db.session.commit()
|
|
|
|
return {"status": "ok"}
|
|
case _:
|
|
logger.error("Неверный метод запроса")
|
|
return jsonify({"status": "error"}), 405
|
|
|
|
|
|
def init_app():
|
|
with app.app_context():
|
|
db.create_all()
|
|
enable_publish_job()
|
|
enable_birthdate_job()
|
|
logger.info("Приложение запущено")
|
|
|
|
|
|
@app.before_request
|
|
def check_auth_cookie():
|
|
endpoint = request.endpoint
|
|
|
|
if endpoint is None:
|
|
return
|
|
|
|
if endpoint == "login" or endpoint.startswith("static"):
|
|
return
|
|
|
|
p = Protection.query.first()
|
|
|
|
if not p or not p.verify_token(request.cookies.get("auth_token")):
|
|
return redirect(url_for("login"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
init_app()
|
|
app.run(host="0.0.0.0", port=80)
|