main.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. import datetime
  2. import os
  3. from flask import Flask, render_template, request, url_for
  4. from flask_login import login_user, current_user, LoginManager, logout_user, login_required
  5. from flask_wtf import CSRFProtect
  6. from flask_restful import abort
  7. from werkzeug.datastructures import CombinedMultiDict
  8. from werkzeug.utils import redirect
  9. from itsdangerous import URLSafeTimedSerializer, SignatureExpired
  10. from sqlalchemy import or_
  11. from json import loads
  12. from functions import check_password, mail, init_db_default, get_projects_data, get_user_data, save_project_logo, \
  13. overdue_quest_project
  14. from forms.edit_profile import EditProfileForm
  15. from forms.login import LoginForm
  16. from forms.find_project import FindProjectForm
  17. from forms.register import RegisterForm
  18. from forms.project import ProjectForm
  19. from forms.recovery import RecoveryForm, NewPasswordForm
  20. from forms.conf_delete_project import DeleteProjectForm
  21. from data.users import User
  22. from data.quests import Quests
  23. from data.files import Files
  24. from data.projects import Projects
  25. from data.staff_projects import StaffProjects
  26. from waitress import serve
  27. from data import db_session
  28. app = Flask(__name__)
  29. with open('incepted.config', 'r', encoding='utf-8') as file:
  30. file = file.read()
  31. file = loads(file)
  32. key = file["encrypt_key"]
  33. app.config['SECRET_KEY'] = key
  34. csrf = CSRFProtect(app)
  35. s = URLSafeTimedSerializer(key)
  36. login_manager = LoginManager()
  37. login_manager.init_app(app)
  38. @app.route('/')
  39. def base():
  40. if not current_user.is_authenticated:
  41. return render_template('main.html', title='Главная')
  42. else:
  43. return redirect('/projects')
  44. @app.route('/project/<int:id_project>/task/new')
  45. def new_task_project(id_project):
  46. if current_user.is_authenticated:
  47. data_session = db_session.create_session()
  48. current_project = data_session.query(Projects).filter(Projects.id == id_project).first()
  49. if current_project:
  50. pass
  51. else:
  52. abort(404)
  53. else:
  54. return redirect('/login')
  55. @app.route('/project/<int:id_project>/edit', methods=['GET', 'POST'])
  56. def edit_project(id_project):
  57. if current_user.is_authenticated:
  58. data_session = db_session.create_session()
  59. current_project = data_session.query(Projects).filter(Projects.id == id_project).first()
  60. if current_project:
  61. staff = data_session.query(StaffProjects).filter(StaffProjects.project == current_project.id).all()
  62. if current_user.id == current_project.creator or current_user.id in list(map(lambda x: x.user, staff)):
  63. list_users = list(
  64. map(lambda x: get_user_data(x), data_session.query(User).filter(User.id != current_user.id).all()))
  65. staff = list(map(lambda x: get_user_data(x), data_session.query(User).filter(
  66. User.id.in_(list(map(lambda x: x.user, staff)))).all())) if staff else []
  67. form = ProjectForm()
  68. if form.save.data:
  69. new_staff = []
  70. for i in list_users:
  71. if request.form.getlist(f"choose_{i['login']}") and i['id'] != current_user.id:
  72. new_staff.append(i)
  73. if i not in staff:
  74. new_staffer = StaffProjects(
  75. user=i['id'],
  76. project=current_project.id,
  77. role='user',
  78. permission=3
  79. )
  80. data_session.add(new_staffer)
  81. data_session.commit()
  82. if sorted(new_staff, key=lambda x: x['id']) != sorted(staff, key=lambda x: x['id']):
  83. for i in staff:
  84. if i not in new_staff:
  85. data_session.delete(data_session.query(StaffProjects).filter(
  86. StaffProjects.user == i['id'], StaffProjects.project == current_project.id).first())
  87. data_session.commit()
  88. if form.logo.data:
  89. current_project.photo = save_project_logo(form.logo.data)
  90. data_session.commit()
  91. current_project.name = form.name.data
  92. current_project.description = form.description.data
  93. data_session.commit()
  94. return redirect(f'/project/{current_project.id}')
  95. if form.del_photo.data:
  96. os.remove(current_project.photo)
  97. current_project.photo = 'static/images/none_project.png'
  98. data_session.commit()
  99. return redirect(f'/project/{current_project.id}/edit')
  100. form.name.data = current_project.name
  101. form.description.data = current_project.description
  102. return render_template('edit_project.html', title='Изменение проекта', form=form, list_users=list_users,
  103. staff=staff, project=current_project)
  104. else:
  105. abort(403)
  106. else:
  107. abort(404)
  108. else:
  109. return redirect('/login')
  110. @app.route('/project/<int:id_project>')
  111. def project(id_project):
  112. if current_user.is_authenticated:
  113. data_session = db_session.create_session()
  114. current_project = data_session.query(Projects).filter(Projects.id == id_project).first()
  115. if current_project:
  116. staff = data_session.query(StaffProjects).filter(StaffProjects.project == current_project.id).all()
  117. if current_user.id == current_project.creator or current_user.id in list(map(lambda x: x.user, staff)):
  118. staff = list(map(lambda x: get_user_data(x), data_session.query(User).filter(
  119. User.id.in_(list(map(lambda x: x.user, staff)))).all())) if staff else []
  120. quests = data_session.query(Quests).filter(Quests.project == current_project.id).all()
  121. if quests:
  122. quests.sort(key=lambda x: (x.realized, x.deadline))
  123. quests = list(map(lambda x: overdue_quest_project(x), quests))
  124. return render_template('project.html',
  125. project=current_project,
  126. title=current_project.name,
  127. staff=staff,
  128. quests=quests)
  129. else:
  130. abort(403)
  131. else:
  132. abort(404)
  133. else:
  134. return redirect('/login')
  135. @app.route('/recovery/confirmation/<token>', methods=['GET', 'POST'])
  136. def conf_recovery(token):
  137. try:
  138. user_email = s.loads(token, max_age=86400)
  139. data_session = db_session.create_session()
  140. user = data_session.query(User).filter(User.email == user_email).first()
  141. if user:
  142. form = NewPasswordForm()
  143. if form.validate_on_submit():
  144. if form.password.data != form.repeat_password.data:
  145. return render_template('recovery.html', title='Восстановление', form=form, recovery=0,
  146. message='Пароли не совпадают')
  147. status_password = check_password(form.password.data)
  148. if status_password != 'OK':
  149. return render_template('recovery.html', title='Восстановление', form=form, recovery=0,
  150. message=str(status_password))
  151. user.set_password(form.password.data)
  152. data_session.commit()
  153. mail(f'Для аккаунта {user.login}, успешно был обновлен пароль', user.email,
  154. 'Изменение пароля')
  155. return redirect('/login?message=Пароль обновлен')
  156. return render_template('recovery.html', title='Восстановление', form=form, recovery=0, message='')
  157. else:
  158. return redirect('/login?message=Пользователь не найден&danger=True')
  159. except SignatureExpired:
  160. return redirect('/login?message=Срок действия ссылки истек&danger=True')
  161. @app.route('/recovery', methods=['GET', 'POST'])
  162. def recovery():
  163. if not current_user.is_authenticated:
  164. form = RecoveryForm()
  165. if form.validate_on_submit():
  166. token = s.dumps(form.email.data)
  167. link_conf = url_for('conf_recovery', token=token, _external=True)
  168. mail(f'Для сбросы пароля пройдите по ссылке: {link_conf}', form.email.data,
  169. 'Восстановление доступа')
  170. return redirect('/login?message=Мы выслали ссылку для сброса вам на почту')
  171. return render_template('recovery.html', title='Восстановление пароля', form=form, recovery=True, message='')
  172. else:
  173. return redirect('/')
  174. @app.route('/project/<int:id_project>/delete', methods=['GET', 'POST'])
  175. def delete_project(id_project):
  176. if current_user.is_authenticated:
  177. data_session = db_session.create_session()
  178. project_del = data_session.query(Projects).filter(Projects.id == id_project).first()
  179. if project_del:
  180. if project_del.creator == current_user.id:
  181. form = DeleteProjectForm()
  182. if form.validate_on_submit():
  183. if form.conf.data != f'delete/{project_del.name}':
  184. return render_template('delete_project.html', title='Удаление проекта', form=form,
  185. project=project_del,
  186. message='Вы не правильно ввели фразу')
  187. staff = data_session.query(StaffProjects).filter(StaffProjects.project == id_project).all()
  188. for i in staff:
  189. data_session.delete(i)
  190. if 'none_project' not in project_del.photo:
  191. os.remove(project_del.photo)
  192. shutil.rmtree(f'static/app_files/all_projects/{str(project_del.id)}')
  193. data_session.delete(project_del)
  194. data_session.commit()
  195. return redirect('/projects')
  196. return render_template('delete_project.html', title='Удаление проекта', form=form, project=project_del,
  197. message='')
  198. else:
  199. abort(403)
  200. else:
  201. abort(404)
  202. else:
  203. return redirect('/login')
  204. @app.route('/user/<string:_login>', methods=['GET', 'POST'])
  205. def user_view(_login):
  206. if current_user.is_authenticated:
  207. data_session = db_session.create_session()
  208. user = data_session.query(User).filter(User.login == _login).first()
  209. if user:
  210. current_projects = data_session.query(Projects).filter(or_(Projects.creator == user.id, Projects.id.in_(
  211. list(map(lambda x: x[0], data_session.query(
  212. StaffProjects.project).filter(
  213. StaffProjects.user == user.id).all()))))).all()
  214. resp = list(map(lambda x: get_projects_data(x), current_projects))
  215. return render_template('user_view.html', title=user.name + ' ' + user.surname, user=user,
  216. list_projects=resp)
  217. else:
  218. abort(404)
  219. else:
  220. return redirect('/login')
  221. @app.route('/projects/new', methods=['GET', 'POST'])
  222. def new_project():
  223. if current_user.is_authenticated:
  224. form = ProjectForm()
  225. data_session = db_session.create_session()
  226. list_users = list(
  227. map(lambda x: get_user_data(x), data_session.query(User).filter(User.id != current_user.id).all()))
  228. if form.validate_on_submit():
  229. currnet_project = Projects(
  230. name=form.name.data,
  231. description=form.description.data,
  232. date_create=datetime.datetime.now(),
  233. creator=current_user.id
  234. )
  235. currnet_project.photo = save_project_logo(
  236. form.logo.data) if form.logo.data else 'static/images/none_project.png'
  237. data_session.add(currnet_project)
  238. data_session.flush()
  239. data_session.refresh(currnet_project)
  240. for i in list_users:
  241. if request.form.getlist(f"choose_{i['login']}") and i['id'] != current_user.id:
  242. new_staffer = StaffProjects(
  243. user=i['id'],
  244. project=currnet_project.id,
  245. role='user',
  246. permission=3
  247. )
  248. data_session.add(new_staffer)
  249. data_session.commit()
  250. os.mkdir(f'static/app_files/all_projects/{str(currnet_project.id)}')
  251. return redirect('/projects')
  252. return render_template('new_project.html', title='Новый проект', form=form, list_users=list_users)
  253. else:
  254. return redirect('/login')
  255. @app.route('/projects', methods=['GET', 'POST'])
  256. def projects():
  257. if current_user.is_authenticated:
  258. find = False
  259. form = FindProjectForm()
  260. data_session = db_session.create_session()
  261. resp = []
  262. current_projects = \
  263. data_session.query(Projects).filter(or_(Projects.creator == current_user.id,
  264. Projects.id.in_(
  265. list(map(lambda x: x[0],
  266. data_session.query(
  267. StaffProjects.project).filter(
  268. StaffProjects.user
  269. == current_user.id).all()))))).all()
  270. if form.validate_on_submit():
  271. new_resp = []
  272. for i in range(len(current_projects)):
  273. if str(form.project.data).lower().strip() in str(current_projects[i].name).lower().strip():
  274. new_resp.append(current_projects[i])
  275. current_projects = new_resp
  276. find = True
  277. resp = list(map(lambda x: get_projects_data(x), current_projects))
  278. return render_template('projects.html', title='Проекты', list_projects=resp, form=form, find=find)
  279. else:
  280. return redirect('/login')
  281. @app.route('/profile', methods=['GET', 'POST'])
  282. def profile():
  283. if current_user.is_authenticated:
  284. form = EditProfileForm(
  285. CombinedMultiDict((request.files, request.form)),
  286. email=current_user.email,
  287. name=current_user.name,
  288. surname=current_user.surname,
  289. about=current_user.about,
  290. birthday=current_user.birthday
  291. )
  292. if form.del_photo.data:
  293. data_session = db_session.create_session()
  294. user = data_session.query(User).filter(User.id == current_user.id).first()
  295. if not user:
  296. return render_template('profile.html', title='Профиль', form=form,
  297. message='Ошибка, пользователь ненайден')
  298. os.remove(current_user.photo)
  299. user.photo = 'static/images/none_logo.png'
  300. data_session.commit()
  301. if form.validate_on_submit():
  302. data_session = db_session.create_session()
  303. user = data_session.query(User).filter(User.id == current_user.id).first()
  304. if not user:
  305. return render_template('profile.html', title='Профиль', form=form,
  306. message='Ошибка, пользователь ненайден')
  307. if form.email.data != current_user.email:
  308. token = s.dumps(form.email.data)
  309. link_conf = url_for('confirmation', token=token, _external=True)
  310. mail(f'Для изменения почты пройдите по ссылке: {link_conf}', form.email.data,
  311. 'Изменение почты')
  312. user.activated = False
  313. user.email = form.email.data
  314. if form.photo.data:
  315. with open(f'static/app_files/user_logo/{current_user.login}.png', 'wb') as file:
  316. form.photo.data.save(file)
  317. user.photo = f'static/app_files/user_logo/{current_user.login}.png'
  318. user.name = form.name.data
  319. user.surname = form.surname.data
  320. user.about = form.about.data
  321. user.birthday = form.birthday.data
  322. data_session.commit()
  323. return redirect('/profile')
  324. return render_template('profile.html', title='Профиль', form=form, message='')
  325. else:
  326. return redirect('/login')
  327. @login_manager.user_loader
  328. def load_user(user_id):
  329. db_sess = db_session.create_session()
  330. return db_sess.query(User).get(user_id)
  331. @app.route('/login', methods=['GET', 'POST'])
  332. def login():
  333. if not current_user.is_authenticated:
  334. message = request.args.get('message') if request.args.get('message') else ''
  335. danger = request.args.get('danger') if request.args.get('danger') else False
  336. form = LoginForm()
  337. if form.validate_on_submit():
  338. data_session = db_session.create_session()
  339. user = data_session.query(User).filter(User.email == form.login.data).first()
  340. if not user:
  341. user = data_session.query(User).filter(User.login == form.login.data).first()
  342. if user and user.check_password(form.password.data):
  343. if user.activated:
  344. login_user(user, remember=form.remember_me.data)
  345. return redirect('/projects')
  346. else:
  347. return render_template('login.html',
  348. message="Ваша почта не подтверждена",
  349. danger=True,
  350. form=form)
  351. return render_template('login.html',
  352. message="Неправильный логин или пароль",
  353. danger=True,
  354. form=form)
  355. return render_template('login.html', title='Авторизация', form=form, message=message,
  356. danger=danger)
  357. else:
  358. return redirect('/projects')
  359. @app.route('/logout')
  360. @login_required
  361. def logout():
  362. logout_user()
  363. return redirect("/")
  364. @app.route('/register', methods=['GET', 'POST'])
  365. def register():
  366. if not current_user.is_authenticated:
  367. form = RegisterForm()
  368. if form.validate_on_submit():
  369. data_session = db_session.create_session()
  370. if data_session.query(User).filter(User.login == form.login.data).first():
  371. return render_template('register.html', form=form, message="Такой пользователь уже есть",
  372. title='Регистрация')
  373. if data_session.query(User).filter(User.email == form.email.data).first():
  374. return render_template('register.html', form=form, message="Такая почта уже есть", title='Регистрация')
  375. status_password = check_password(form.password.data)
  376. if status_password != 'OK':
  377. return render_template('register.html', form=form, message=status_password, title='Регистрация')
  378. user = User(
  379. email=form.email.data,
  380. name=form.name.data,
  381. login=form.login.data,
  382. activity=datetime.datetime.now(),
  383. data_reg=datetime.date.today(),
  384. photo='static/images/none_logo.png',
  385. role=1
  386. )
  387. user.set_password(form.password.data)
  388. data_session.add(user)
  389. data_session.commit()
  390. token = s.dumps(form.email.data)
  391. link_conf = url_for('confirmation', token=token, _external=True)
  392. mail(f'Для завершения регистрации пройдите по ссылке: {link_conf}', form.email.data,
  393. 'Подтверждение регистрации')
  394. return redirect('/login?message=Мы выслали ссылку для подтверждения почты')
  395. return render_template('register.html', form=form, message='', title='Регистрация')
  396. else:
  397. return redirect('/projects')
  398. @app.route('/confirmation/<token>')
  399. def confirmation(token):
  400. try:
  401. user_email = s.loads(token, max_age=86400)
  402. data_session = db_session.create_session()
  403. user = data_session.query(User).filter(User.email == user_email).first()
  404. if user:
  405. user.activated = True
  406. data_session.commit()
  407. return redirect('/login?message=Почта успешно подтверждена')
  408. else:
  409. return redirect('/login?message=Пользователь не найден&danger=True')
  410. except SignatureExpired:
  411. data_session = db_session.create_session()
  412. users = data_session.query(User).filter(
  413. User.activated == 0 and User.activated < datetime.datetime.now() - datetime.timedelta(days=1)).all()
  414. if users:
  415. list(map(lambda x: data_session.delete(x), users))
  416. data_session.commit()
  417. return redirect('/login?message=Срок действия ссылки истек, данные удалены&danger=True')
  418. @app.errorhandler(500)
  419. def internal_server_error(error):
  420. return render_template('page_error.html', title='Ошибка сервера', error='500', message='Технические шоколадки')
  421. @app.errorhandler(404)
  422. def page_not_found(error):
  423. return render_template('page_error.html', title='Страница не найдена', error='404', message='Страница не найдена')
  424. @app.errorhandler(403)
  425. def access_error(error):
  426. return render_template('page_error.html', title='Ошибка доступа', error='403', message='Доступ сюда запрещен')
  427. def main():
  428. db_path = 'db/incepted.db'
  429. db = os.path.exists(db_path)
  430. db_session.global_init(db_path)
  431. if not db:
  432. init_db_default()
  433. serve(app, host='0.0.0.0', port=5000)
  434. if __name__ == '__main__':
  435. main()