main.py 17 KB

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