server-tx part : web UI, PDF generator & transfer to RX server
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
from flask import Blueprint
|
||||
|
||||
m_admin = Blueprint(
|
||||
'admin',
|
||||
__name__,
|
||||
template_folder='templates'
|
||||
)
|
||||
|
||||
from . import views
|
||||
@@ -0,0 +1,101 @@
|
||||
{% extends "general.html" %}
|
||||
|
||||
{% block title %}Панель администратора{% endblock %}
|
||||
{% block header %}Админ: {{ user.username }} | Панель | <a href="/">На главную</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2>Список пользователей</h2>
|
||||
<p><a href = "/admin/create">Тык</a>, чтобы создать пользователя</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="username">Логин</th>
|
||||
<th class="team">Команда</th>
|
||||
<th class="actions">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cuser in users %}
|
||||
<tr>
|
||||
<td>{{ cuser.username }}</td>
|
||||
<td>{{ cuser.team }}</td>
|
||||
<td class="action-buttons">
|
||||
{% if cuser.team not in ("$root$") %}
|
||||
<a href="edit/{{ cuser.id }}" class="icon-button" title="Редактировать">✏️</button>
|
||||
<a href="remove/{{ cuser.id }}" class="icon-button" title="Удалить!">🗑️</button>
|
||||
{% else %}
|
||||
Только по запросу | ID: {{ cuser.id }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Активность пользователей</h2>
|
||||
<p>Активность администраторов и преподавательского состава не отображается.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Логин</th>
|
||||
<th>Последняя печать</th>
|
||||
<th>Количество страниц</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cuser in users %}
|
||||
{% if cuser.team not in ("$root$", "$tutor$") %}
|
||||
<tr>
|
||||
<td>{{ cuser.username }}</td>
|
||||
<td>{{ cuser.lastprint_ts|format_timestamp }}</td>
|
||||
<td>{{ cuser.pages }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Настройки печати</h2>
|
||||
<br>
|
||||
<form action="edit/settings" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="watermark">Watermark на листах (доступные переменные: $time$, $team$, $page и $loc$):</label>
|
||||
<input type="text" id="watermark" name="watermark" value="{{ settings['print']['watermark'] }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="pages-per-request">Лимит сгенерированных страниц на один запрос</label>
|
||||
<input type="number" id="pages-per-request" name="pages-per-request" value="{{ settings['print']['limits']['pages-per-request'] }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="seconds-per-request">Лимит запросов на пользователя (задержка между запросами в секундах)</label>
|
||||
<input type="number" id="seconds-per-request" name="seconds-per-request" value="{{ settings['print']['limits']['seconds-per-request'] }}" required>
|
||||
</div>
|
||||
<button type="submit" class="button">Сохранить текущие настройки</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Настройки принимающего сервера</h2>
|
||||
<p><span style = "color: red; font-weight: bold;">Перед любыми изменениям убедитесь, что вы знаете, что делаете!</span> По любым вопросам обращайтесь к Администратору системы.</p>
|
||||
<p></p>
|
||||
<br>
|
||||
<form action="edit/server-rx" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="server-ip">Принимающий сервер : IP</label>
|
||||
<input type="text" id="server-ip" name="server-ip" value="{{ settings['server']['ip'] }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server-port">Принимающий сервер : Port</label>
|
||||
<input type="number" id="server-port" name="server-port" value="{{ settings['server']['port'] }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="server-secret">Принимающий сервер : Секретная фраза</label>
|
||||
<input type="texts" id="server-secret" name="server-secret" value="{{ settings['server']['secret'] }}" required>
|
||||
</div>
|
||||
<button type="submit" class="button">Сохранить и скачать server-rx.json</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,29 @@
|
||||
{% extends "general.html" %}
|
||||
|
||||
{% block title %}Создание пользователя{% endblock %}
|
||||
{% block header %}Создание пользователя{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2>Создание новой команды</h2>
|
||||
<form action="account_created" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="username">Логин</label>
|
||||
<input type="text" id="username" name="username" placeholder="Логин, например, ahmet" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="team_name">Команда</label>
|
||||
<input type="text" id="team_name" name="team_name" placeholder="Короткое название команды, например, Физтех.Бегемотики" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Позиция (стол, секция, пр.)</label>
|
||||
<input type="text" id="location" name="location" placeholder="Метка, например, '1р1с' (т.е., 1 ряд 1 стол)" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Пароль</label>
|
||||
<input type="text" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="button">Создать пользователя</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,29 @@
|
||||
{% extends "general.html" %}
|
||||
|
||||
{% block title %}Создание пользователя{% endblock %}
|
||||
{% block header %}Создание пользователя{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2>Создание новой команды</h2>
|
||||
<form action="account_created_many" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="username">Логин</label>
|
||||
<input type="text" id="username" name="username" placeholder="Логин, например, ahmet" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="team_name">Команда</label>
|
||||
<input type="text" id="team_name" name="team_name" placeholder="Короткое название команды, например, Физтех.Бегемотики" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Позиция (стол, секция, пр.)</label>
|
||||
<input type="text" id="location" name="location" placeholder="Метка, например, '1р1с' (т.е., 1 ряд 1 стол)" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Пароль</label>
|
||||
<input type="text" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="button">Создать пользователя</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,33 @@
|
||||
{% extends "general.html" %}
|
||||
|
||||
{% block title %}Пользователь создан{% endblock %}
|
||||
{% block header %}Пользователь создан{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2>Успешно создан пользователь {{ cuser.username }}!</h2>
|
||||
<form action="">
|
||||
<div class="form-group">
|
||||
<label for="username">Логин</label>
|
||||
<input type="text" id="username" name="username" value="{{ cuser.username }}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="team_name">Команда</label>
|
||||
<input type="text" id="team_name" name="team_name" value="{{ cuser.team }}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Позиция (стол, секция, пр.)</label>
|
||||
<input type="text" id="location" name="locat ion" value="{{ cuser.loc }}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Пароль (отображается один раз, на данной странице)</label>
|
||||
<input type="text" id="password" name="password" value="{{ password_onetime }}" readonly>
|
||||
</div>
|
||||
Данную страницу можно отправить новому пользователю платформы!
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<a href="../admin">Вернуться обратно</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,29 @@
|
||||
{% extends "general.html" %}
|
||||
|
||||
{% block title %}Редактирование пользователя{% endblock %}
|
||||
{% block header %}Редактирование {{ edituser.username }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h2>Редактирование команды</h2>
|
||||
<form action="proc/{{ edituser.id }}" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="username">Логин (не изменяем)</label>
|
||||
<input type="text" id="username" name="username" value="{{ edituser.username }}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="team_name">Команда</label>
|
||||
<input type="text" id="team_name" name="team_name" value="{{ edituser.team }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Позиция (стол, секция, пр.)</label>
|
||||
<input type="text" id="location" name="location" value="{{ edituser.loc }}" required>
|
||||
</div>
|
||||
<div class="form-group" id="new_password">
|
||||
<label for="new_password">Новый пароль (должен превышать 3 символа, если хотите изменить, иначе - пустое)</label>
|
||||
<input type="text" id="new_password" name="new_password">
|
||||
</div>
|
||||
<button type="submit" class="button">Сохранить изменения</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,187 @@
|
||||
from . import m_admin
|
||||
|
||||
import json
|
||||
|
||||
from datetime import datetime
|
||||
from flask import render_template
|
||||
from flask import Response
|
||||
from flask import redirect
|
||||
from flask import request
|
||||
from flask import session
|
||||
from app._tools.decorators import auth_required
|
||||
from app._tools.decorators import admin_required
|
||||
from app._tools.database import DataManager
|
||||
from app._tools.database import SessionManager
|
||||
from app._tools.database import ConfigManager
|
||||
from app.config import _SUPER_ADMINS as SUPER
|
||||
from app.config import _SALT as SALT
|
||||
from app._tools import passhash
|
||||
|
||||
@m_admin.app_template_filter("format_timestamp")
|
||||
def format_timestamp(value):
|
||||
try:
|
||||
dt = datetime.fromtimestamp(value)
|
||||
return dt.strftime("%d.%m.%Y %H:%M")
|
||||
except:
|
||||
return "Invalid timestamp"
|
||||
|
||||
@m_admin.route('/')
|
||||
@auth_required
|
||||
@admin_required
|
||||
def panel():
|
||||
user = DataManager.get_user(SessionManager.get_session(session["token"]).user_id)
|
||||
users = DataManager.get_all_users()
|
||||
return render_template('admin/admin_main.html', user = user, users = users, settings = ConfigManager.get_config())
|
||||
|
||||
@m_admin.route('/create')
|
||||
@auth_required
|
||||
@admin_required
|
||||
def create():
|
||||
return render_template("admin/user_create.html")
|
||||
|
||||
@m_admin.route('/create_many')
|
||||
@auth_required
|
||||
@admin_required
|
||||
def create_manu():
|
||||
return render_template("admin/user_create_many.html")
|
||||
|
||||
@m_admin.route('/account_created_many', methods=['POST'])
|
||||
@auth_required
|
||||
@admin_required
|
||||
def create_many_proc():
|
||||
data = dict(request.form)
|
||||
usernames = str(data['username']).split(';')
|
||||
passwords = str(data['password']).split(';')
|
||||
teams = str(data['team_name']).split(';')
|
||||
locations = str(data['location']).split(';')
|
||||
|
||||
for i in range(len(usernames)):
|
||||
if (DataManager.get_user_by_name(usernames[i]) != None):
|
||||
return render_template("error.html", error = "Пользователь с таким логином существует!")
|
||||
if len(passwords[i]) < 4:
|
||||
return render_template("error.html", error = "Длина пароля должна превышать 3 символа!")
|
||||
user = DataManager.get_user(SessionManager.get_session(session["token"]).user_id)
|
||||
if teams[i] == "$root$" and user.username not in SUPER:
|
||||
return render_template("error.html", error = "У вас нет прав на работу с группой $root$!")
|
||||
DataManager.add_user(
|
||||
usernames[i],
|
||||
teams[i],
|
||||
locations[i],
|
||||
passhash.hashPassword(passwords[i], SALT)
|
||||
)
|
||||
cuser = DataManager.get_user_by_name(data['username'])
|
||||
return "Done"
|
||||
|
||||
@m_admin.route('/account_created', methods=['POST'])
|
||||
@auth_required
|
||||
@admin_required
|
||||
def create_proc():
|
||||
data = dict(request.form)
|
||||
if (DataManager.get_user_by_name(data['username']) != None):
|
||||
return render_template("error.html", error = "Пользователь с таким логином существует!")
|
||||
if len(data['password']) < 4:
|
||||
return render_template("error.html", error = "Длина пароля должна превышать 3 символа!")
|
||||
user = DataManager.get_user(SessionManager.get_session(session["token"]).user_id)
|
||||
if data['team_name'] == "$root$" and user.username not in SUPER:
|
||||
return render_template("error.html", error = "У вас нет прав на работу с группой $root$!")
|
||||
DataManager.add_user(
|
||||
data['username'],
|
||||
data['team_name'],
|
||||
data['location'],
|
||||
passhash.hashPassword(data['password'], SALT)
|
||||
)
|
||||
cuser = DataManager.get_user_by_name(data['username'])
|
||||
return render_template("admin/user_created.html", cuser = cuser, password_onetime = data['password'])
|
||||
|
||||
@m_admin.route('/edit/<id>')
|
||||
@auth_required
|
||||
@admin_required
|
||||
def edit_team(id : int):
|
||||
euser = DataManager.get_user(id)
|
||||
return render_template('admin/user_edit.html', edituser = euser)
|
||||
|
||||
@m_admin.route('/edit/settings', methods=['POST'])
|
||||
@auth_required
|
||||
@admin_required
|
||||
def edit_settings():
|
||||
data = dict(request.form)
|
||||
cur_config = ConfigManager.get_config()
|
||||
cur_config['print']['watermark'] = data['watermark']
|
||||
data['pages-per-request'] = int(data['pages-per-request'])
|
||||
data['seconds-per-request'] = int(data['seconds-per-request'])
|
||||
if data['pages-per-request'] in range(1, 51):
|
||||
cur_config['print']['limits']['pages-per-request'] = data['pages-per-request']
|
||||
else:
|
||||
return render_template("error.html", error = "Лимит страниц должен быть в рамках от 1 до 50 страниц!")
|
||||
if data['seconds-per-request'] in range(5, 61):
|
||||
cur_config['print']['limits']['seconds-per-request'] = data['seconds-per-request']
|
||||
else:
|
||||
return render_template("error.html", error = "Задержка между запросами должна быть от 5 до 60 секунд!")
|
||||
ConfigManager.set_config(cur_config)
|
||||
return redirect("/admin/")
|
||||
|
||||
@m_admin.route('/edit/server-rx', methods=['POST'])
|
||||
@auth_required
|
||||
@admin_required
|
||||
def edit_server_rx():
|
||||
data = dict(request.form)
|
||||
cur_config = ConfigManager.get_config()
|
||||
if int(data['server-port']) not in range(1, 65536):
|
||||
return render_template("error.html", error = "Порт в диапазоне от 1 до 65535!")
|
||||
cur_config['server']['port'] = int(data['server-port'])
|
||||
cur_config['server']['ip'] = str(data['server-ip'])
|
||||
cur_config['server']['secret'] = str(data['server-secret'])
|
||||
ConfigManager.set_config(cur_config)
|
||||
gen_json = dict()
|
||||
gen_json = cur_config['server']
|
||||
gen_json = json.dumps(
|
||||
gen_json,
|
||||
indent = 4
|
||||
)
|
||||
response = Response(gen_json, mimetype='application/json')
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=server-rx.json'
|
||||
response.headers['Content-Type'] = 'application/json'
|
||||
response.headers['Refresh'] = '1; url=' + "/admin"
|
||||
return response
|
||||
|
||||
@m_admin.route('/edit/proc/<id>', methods=['POST'])
|
||||
@auth_required
|
||||
@admin_required
|
||||
def edit_proc(id : int):
|
||||
data = dict(request.form)
|
||||
e_user = DataManager.get_user(id)
|
||||
user = DataManager.get_user(SessionManager.get_session(session["token"]).user_id)
|
||||
if data['team_name'] == "$root$" and user.username not in SUPER:
|
||||
return render_template("error.html", error = "У вас нет прав на работу с группой $root$!")
|
||||
if e_user.team == "$root$" and user.username not in SUPER:
|
||||
return render_template("error.html", error = "Вы не можете редактировать других Администраторов!")
|
||||
if (len(data['new_password']) > 3):
|
||||
DataManager.edit_user(
|
||||
id,
|
||||
'passkey',
|
||||
passhash.hashPassword(data['new_password'], SALT)
|
||||
)
|
||||
if e_user.team != data['team_name']:
|
||||
DataManager.edit_user(
|
||||
id,
|
||||
'team',
|
||||
data['team_name']
|
||||
)
|
||||
if e_user.loc != data['location']:
|
||||
DataManager.edit_user(
|
||||
id,
|
||||
'loc',
|
||||
data['location']
|
||||
)
|
||||
return redirect("/admin")
|
||||
|
||||
@m_admin.route('/remove/<id>')
|
||||
@auth_required
|
||||
@admin_required
|
||||
def remove_team(id : int):
|
||||
user = DataManager.get_user(SessionManager.get_session(session["token"]).user_id)
|
||||
if user.username not in SUPER:
|
||||
return render_template("error.html", error = "Обратитесь к Супер-администратору. У вас нет прав на удаление пользователей!")
|
||||
SessionManager.remove_session_by_user(id)
|
||||
DataManager.del_user(id)
|
||||
return redirect("/admin")
|
||||
Reference in New Issue
Block a user