main.py 21 KB

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