main.py 22 KB

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