1
0

app.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. import datetime as datetime
  2. from urllib.parse import urlparse
  3. import webbrowser
  4. from emailtool.emailer import Emailer
  5. from flask import Flask, render_template, request, redirect, url_for, session, send_file
  6. from gevent.pywsgi import WSGIServer
  7. import socket
  8. import logging
  9. from peewee import Model, CharField, DateTimeField, SqliteDatabase, BooleanField, fn
  10. import hashlib
  11. import os
  12. import csv
  13. import time
  14. import threading
  15. import atexit
  16. db_name = 'app.db'
  17. db = SqliteDatabase(db_name)
  18. class User(Model):
  19. username = CharField()
  20. password = CharField()
  21. date_created = DateTimeField(default=datetime.datetime.now)
  22. logged_in = BooleanField(default=False)
  23. class Meta:
  24. database = db
  25. class IlsUser(Model):
  26. username = CharField()
  27. email = CharField()
  28. reset_datetime = DateTimeField(default=datetime.datetime.now)
  29. class Meta:
  30. database = db
  31. class Settings(Model):
  32. name = CharField()
  33. value = CharField()
  34. class Meta:
  35. database = db
  36. class Schedule(Model):
  37. interval = CharField()
  38. class Meta:
  39. database = db
  40. class Log(Model):
  41. date = DateTimeField(default=datetime.datetime.now)
  42. username = CharField()
  43. action = CharField()
  44. class Meta:
  45. database = db
  46. class PasswordResetLog(Model):
  47. date = DateTimeField(default=datetime.datetime.now)
  48. username = CharField()
  49. class Meta:
  50. database = db
  51. class EmailReminderLog(Model):
  52. date = CharField(default=datetime.datetime.now().strftime('%Y-%m-%d'))
  53. email = CharField()
  54. interval = CharField()
  55. sent = BooleanField(default=False)
  56. class Meta:
  57. database = db
  58. def scheduler(stop_event):
  59. while not stop_event.is_set():
  60. # Get all scheduled intervals from the DB
  61. intervals = Schedule.select().execute()
  62. users = IlsUser.select().execute()
  63. # Get settings
  64. domain_name_setting = Settings.get(Settings.name == 'Domain Name')
  65. domain_name = domain_name_setting.value
  66. password_reset_interval_setting = Settings.get(Settings.name == 'Password Reset Interval')
  67. password_reset_interval = password_reset_interval_setting.value
  68. smtp_host_setting = Settings.get(Settings.name == 'SMTP Host')
  69. smtp_host = smtp_host_setting.value
  70. smtp_port_setting = Settings.get(Settings.name == 'SMTP Port')
  71. smtp_port = smtp_port_setting.value
  72. smtp_username_setting = Settings.get(Settings.name == 'SMTP Username')
  73. smtp_username = smtp_username_setting.value
  74. smtp_password_setting = Settings.get(Settings.name == 'SMTP Password')
  75. smtp_password = smtp_password_setting.value
  76. # Loop through each user and calculate the number of days until the users password
  77. # expires based on the password_reset_interval value.
  78. # Loop through all users
  79. # Schedule emails to be sent for each user and interval
  80. for user in users:
  81. # get the scheduled intervals
  82. for interval in intervals:
  83. # Calculate the date (yyyy-mm-dd) that the password will expire and subtract the interval
  84. # from that date to get the date that the email should be sent.
  85. # Example: If the password reset interval is 90 days and the current date is 2019-01-01
  86. # then the password will expire on 2019-04-01. If the interval is 30 days then the
  87. # email should be sent on 2019-03-02.
  88. # Calculate the date that the password will expire
  89. password_expiration_date = user.reset_datetime + datetime.timedelta(days=int(password_reset_interval))
  90. # Subtract the interval from the password expiration date to get the date that the email should be sent
  91. email_date = password_expiration_date - datetime.timedelta(days=int(interval.interval))
  92. # check if reminder has already been saved to the DB
  93. reminder = EmailReminderLog.select().where(EmailReminderLog.email == user.email, EmailReminderLog.interval == interval.interval).execute()
  94. if reminder:
  95. continue
  96. else:
  97. # Store the date in the DB
  98. EmailReminderLog.create(date=email_date.strftime('%Y-%m-%d'), email=user.email, interval=interval.interval).save()
  99. # Get all email reminders from the DB
  100. # if today's date matches the EmailReminderLog date then send the email and mark the reminder as sent
  101. reminders = EmailReminderLog.select().where(EmailReminderLog.date == datetime.datetime.now().strftime('%Y-%m-%d')).execute()
  102. for reminder in reminders:
  103. # Send the email
  104. emailer = Emailer(smtp_host, smtp_port, smtp_username, smtp_password)
  105. emailer.send_email(reminder.email, 'Password Reset Reminder', 'Your password will expire in ' + reminder.interval + ' days. Please visit ' + domain_name + ' to reset your password.')
  106. # Mark the reminder as sent
  107. reminder.sent = True
  108. reminder.save()
  109. time.sleep(5) # Sleep for 15 minutes
  110. # Encrypt the password with SHA256
  111. def encrypt_password(password):
  112. hash = hashlib.sha256(password.encode('utf-8')).hexdigest()
  113. return hash
  114. # Check for DB tables and create if they don't exist
  115. if db.table_exists('user') is False:
  116. db.create_tables([User, ])
  117. User.create(username='admin', password=encrypt_password('admin'),
  118. date_created=datetime.datetime.now(), logged_in=False).save()
  119. if db.table_exists('ilsuser') is False:
  120. db.create_tables([IlsUser, ])
  121. if db.table_exists('settings') is False:
  122. db.create_tables([Settings, ])
  123. Settings.create(name='Debug Mode', value=False).save()
  124. Settings.create(name='HTTP Port', value=5055).save()
  125. Settings.create(name='SMTP Host', value="").save()
  126. Settings.create(name='SMTP Port', value="587").save()
  127. Settings.create(name='SMTP Username', value="").save()
  128. Settings.create(name='SMTP Password', value="").save()
  129. Settings.create(name='Domain Name', value="lynx").save()
  130. Settings.create(name='Password Reset URL', value="https://terminal.idaho-lynx.org").save()
  131. Settings.create(name='Password Reset Interval', value="90").save()
  132. if db.table_exists('log') is False:
  133. db.create_tables([Log, ])
  134. if db.table_exists('password_reset_log') is False:
  135. db.create_tables([PasswordResetLog, ])
  136. if db.table_exists('schedule') is False:
  137. db.create_tables([Schedule, ])
  138. if db.table_exists('email_reminder_log') is False:
  139. db.create_tables([EmailReminderLog, ])
  140. db.close()
  141. settings = Settings.select().execute()
  142. debug_setting = Settings.get(Settings.name == 'Debug Mode')
  143. debug = debug_setting.value
  144. http_port_setting = Settings.get(Settings.name == 'HTTP Port')
  145. http_port = http_port_setting.value
  146. domain_name_setting = Settings.get(Settings.name == 'Domain Name')
  147. domain_name = domain_name_setting.value
  148. password_reset_url_setting = Settings.get(Settings.name == 'Password Reset URL')
  149. password_reset_url = password_reset_url_setting.value
  150. password_reset_interval_setting = Settings.get(Settings.name == 'Password Reset Interval')
  151. password_reset_interval = password_reset_interval_setting.value
  152. log = logging.getLogger('werkzeug')
  153. log.setLevel(logging.INFO)
  154. if debug.lower() == 'true':
  155. debug = True
  156. else:
  157. debug = False
  158. # send_email('aday@twinfallspubliclibrary.org', 'TEST', 'This is a test email')
  159. def shutdown_session(exception=None):
  160. print('Stopping HTTP Service...')
  161. http_server.stop()
  162. # Get the systems hostname
  163. def get_hostname():
  164. return socket.gethostname()
  165. # Get systems IP address
  166. def get_ip_address():
  167. return socket.gethostbyname(socket.gethostname())
  168. # Method to check if a URL is valid using regex
  169. def is_valid_url(url):
  170. try:
  171. result = urlparse(url)
  172. if all([result.scheme, result.netloc]):
  173. url = '{uri.scheme}://{uri.netloc}/'.format(uri=result)
  174. else:
  175. url = False
  176. return url
  177. except:
  178. return False
  179. def requires_auth():
  180. if 'username' in session:
  181. username = session['username']
  182. user = User.get(User.username == username)
  183. if user.logged_in is True:
  184. return True
  185. else:
  186. return False
  187. else:
  188. return False
  189. def admin_password_check():
  190. admin_user = User.get(User.username == 'admin')
  191. if admin_user.password == encrypt_password('admin'):
  192. return True
  193. db.close()
  194. # Start the scheduler loop in another thread
  195. shutdown_scheduler = threading.Event()
  196. scheduler_thread = threading.Thread(target=scheduler, args=(shutdown_scheduler,))
  197. scheduler_thread.start()
  198. # Start the HTTP Server
  199. app = Flask(__name__)
  200. app.secret_key = os.urandom(24)
  201. def format_time_ago(timestamp):
  202. """Calculate the time passed since a datetime stamp and format it as a human-readable string."""
  203. now = datetime.datetime.now()
  204. diff = now - timestamp
  205. if diff.days > 365:
  206. years = diff.days // 365
  207. return f"{years} year{'s' if years > 1 else ''} ago"
  208. if diff.days > 30:
  209. months = diff.days // 30
  210. return f"{months} month{'s' if months > 1 else ''} ago"
  211. if diff.days > 0:
  212. return f"{diff.days} day{'s' if diff.days > 1 else ''} ago"
  213. if diff.seconds > 3600:
  214. hours = diff.seconds // 3600
  215. return f"{hours} hour{'s' if hours > 1 else ''} ago"
  216. if diff.seconds > 60:
  217. minutes = diff.seconds // 60
  218. return f"{minutes} minute{'s' if minutes > 1 else ''} ago"
  219. return "just now"
  220. app.jinja_env.filters['time_since'] = format_time_ago
  221. @app.before_request
  222. def before_request():
  223. db.connect()
  224. @app.after_request
  225. def after_request(response):
  226. db.close()
  227. return response
  228. @app.route('/admin/password/reset', methods=['GET', 'POST'])
  229. def admin_password_reset():
  230. message = None
  231. if request.method == 'POST':
  232. password = request.form.get('password')
  233. password_confirm = request.form.get('password_confirm')
  234. if password != password_confirm:
  235. message = 'Passwords do not match'
  236. else:
  237. try:
  238. user = User.get(User.username == 'admin')
  239. user.password = encrypt_password(password)
  240. user.logged_in = True
  241. user.save()
  242. session['username'] = 'admin'
  243. return redirect(url_for('admin'))
  244. except Exception as e:
  245. print(e)
  246. message = 'Username not found'
  247. context = {
  248. 'message': message,
  249. }
  250. return render_template('admin_password_reset.html', context=context)
  251. # Create a route for the home page
  252. @app.route('/', methods=['GET', 'POST'])
  253. def index():
  254. error = None
  255. reset = False
  256. reset_url = is_valid_url(password_reset_url)
  257. reset_url_error = False
  258. if reset_url is False:
  259. reset_url_error = True
  260. if request.method == 'POST':
  261. username = request.form.get('username')
  262. # Check for the username in the DB
  263. try:
  264. user = IlsUser.filter(IlsUser.username == username).first()
  265. except Exception as e:
  266. print(e)
  267. user = None
  268. if user:
  269. # Reset login datetime
  270. user.reset_datetime = datetime.datetime.now()
  271. PasswordResetLog.create(username=user.username, date_created=datetime.datetime.now()).save()
  272. user.save()
  273. # Open the reset URL in a new tab if the URL is valid
  274. if reset_url is not False:
  275. webbrowser.open_new_tab(str(reset_url))
  276. # Set reset to True to pass back to the view to display the correct content back to the user.
  277. reset = True
  278. else:
  279. error = 'Invalid username'
  280. context = {
  281. 'domain': domain_name,
  282. 'error': error,
  283. 'reset': reset,
  284. 'reset_url': reset_url,
  285. 'reset_url_error': reset_url_error,
  286. }
  287. return render_template('index.html', context=context)
  288. # Create a route for admin page
  289. @app.route('/admin/')
  290. def admin():
  291. if admin_password_check():
  292. return redirect(url_for('admin_password_reset'))
  293. # Check to see if user is logged in
  294. if not requires_auth():
  295. return redirect(url_for('login'))
  296. # Get a count of all IlsUsers
  297. try:
  298. ils_users = IlsUser.select().count()
  299. except Exception as e:
  300. ils_users = None
  301. # Get a list of coming email reminders for the next 7 days
  302. try:
  303. email_reminders = PasswordResetLog.select().where(PasswordResetLog.date_created < datetime.datetime.now() + datetime.timedelta(days=7)).execute()
  304. except Exception as e:
  305. email_reminders = None
  306. # Get a list ils users that have passwords expiring in the next 7 days
  307. try:
  308. ils_users_expiring = IlsUser.select().where(IlsUser.password_expires < datetime.datetime.now() + datetime.timedelta(days=7)).execute()
  309. except Exception as e:
  310. ils_users_expiring = None
  311. # get a list of email notifications sent in the last 7 days
  312. try:
  313. # Get a list of all future email reminders
  314. email_reminders = EmailReminderLog.select().order_by(EmailReminderLog.date).limit(15).execute()
  315. except Exception as e:
  316. email_reminders = None
  317. context = {
  318. 'ils_user_count': ils_users,
  319. 'ils_users_expiring': ils_users_expiring,
  320. 'email_reminders': email_reminders,
  321. }
  322. return render_template('admin.html', context=context)
  323. @app.route('/admin/users/', methods=['GET', 'POST'])
  324. def admin_users():
  325. # Check to see if user is logged in
  326. if not requires_auth():
  327. return redirect(url_for('login'))
  328. message = None
  329. if request.method == 'POST':
  330. username = request.form.get('username')
  331. password = request.form.get('password')
  332. confirm_password = request.form.get('confirm_password')
  333. # Check to see if username already exists
  334. try:
  335. user = User.filter(User.username == username).first()
  336. except Exception as e:
  337. print(e)
  338. user = None
  339. if user:
  340. message = 'Username already exists'
  341. else:
  342. if password == confirm_password:
  343. User.create(username=username, password=encrypt_password(password),
  344. date_created=datetime.datetime.now(), logged_in=False).save()
  345. message = 'User created successfully'
  346. Log.create(username=session['username'], action='Created admin user: %s' % username, ).save()
  347. else:
  348. message = 'Passwords do not match'
  349. # Get all admin users from the DB
  350. users = User.select().execute()
  351. context = {
  352. 'users': users,
  353. 'message': message,
  354. }
  355. return render_template('admin_users.html', context=context)
  356. @app.route('/admin/users/edit/<int:id>', methods=['GET', 'POST'])
  357. def admin_users_edit(id):
  358. # Check to see if user is logged in
  359. if not requires_auth():
  360. return redirect(url_for('login'))
  361. # Get the user from the DB
  362. user = User.get(User.id == id)
  363. message = None
  364. if request.method == 'POST':
  365. username = request.form.get('username')
  366. password = request.form.get('password')
  367. confirm_password = request.form.get('confirm_password')
  368. # Check to see if username already exists
  369. all_users = list()
  370. users = User.select().execute()
  371. for u in users:
  372. if u.username != user.username:
  373. all_users.append(u.username)
  374. if username in all_users:
  375. message = 'Username already exists'
  376. else:
  377. user.username = username
  378. if password is not None or password != '':
  379. if password == confirm_password:
  380. user.password = encrypt_password(password)
  381. else:
  382. message = 'Passwords do not match'
  383. user.save()
  384. message = 'User updated successfully'
  385. Log.create(username=session['username'], action='Updated admin user: %s' % username, ).save()
  386. context = {
  387. 'user': user,
  388. 'message': message,
  389. }
  390. return render_template('admin_user_edit.html', context=context)
  391. @app.route('/admin/users/delete/<int:id>', methods=['GET', 'POST'])
  392. def admin_users_delete(id):
  393. # Check to see if user is logged in
  394. if not requires_auth():
  395. return redirect(url_for('login'))
  396. # Get the user from the DB
  397. user = User.get(User.id == id)
  398. if user.username != 'admin':
  399. # Unset session variable if the user is deleting their own account
  400. if user.username == session['username']:
  401. session.pop('username', None)
  402. username = user.username
  403. user.delete_instance()
  404. Log.create(username=session['username'], action='Removed admin user: %s' % username, ).save()
  405. return redirect(url_for('admin_users'))
  406. @app.route('/admin/users/ils', methods=['GET', 'POST'])
  407. def admin_ils_users():
  408. # Check to see if user is logged in
  409. if not requires_auth():
  410. return redirect(url_for('login'))
  411. message = None
  412. if request.method == 'POST':
  413. username = request.form.get('username')
  414. email = request.form.get('email')
  415. # Check to see if username already exists
  416. try:
  417. user = IlsUser.filter(IlsUser.username == username).first()
  418. except Exception as e:
  419. print(e)
  420. user = None
  421. if user:
  422. message = 'Username already exists'
  423. else:
  424. IlsUser.create(username=username, email=email, reset_datetime=datetime.datetime.now()).save()
  425. message = 'ILS User: %s created successfully' % username
  426. Log.create(username=session['username'], action='Created ILS User: %s' % username, ).save()
  427. # Get all admin users from the DB
  428. users = IlsUser.select().execute()
  429. context = {
  430. 'users': users,
  431. 'message': message,
  432. }
  433. return render_template('admin_ils_users.html', context=context)
  434. @app.route('/admin/users/ils/delete/<int:id>', methods=['GET', 'POST'])
  435. def admin_ils_users_delete(id):
  436. # Check to see if user is logged in
  437. if not requires_auth():
  438. return redirect(url_for('login'))
  439. # Get the user from the DB
  440. user = IlsUser.get(IlsUser.id == id)
  441. username = user.username
  442. # Remove all email reminders for this user
  443. reminders = EmailReminderLog.select().where(EmailReminderLog.email == user.email).execute()
  444. for reminder in reminders:
  445. reminder.delete_instance()
  446. user.delete_instance()
  447. Log.create(username=session['username'], action='Removed ILS user: %s' % username, ).save()
  448. return redirect(url_for('admin_ils_users'))
  449. @app.route('/admin/users/ils/edit/<int:id>', methods=['GET', 'POST'])
  450. def admin_ils_users_edit(id):
  451. # Check to see if user is logged in
  452. if not requires_auth():
  453. return redirect(url_for('login'))
  454. # Get the user from the DB
  455. user = IlsUser.get(IlsUser.id == id)
  456. message = None
  457. if request.method == 'POST':
  458. username = request.form.get('username')
  459. email = request.form.get('email')
  460. # Check to see if username already exists
  461. all_users = list()
  462. users = IlsUser.select().execute()
  463. for u in users:
  464. if u.username != user.username:
  465. all_users.append(u.username)
  466. if username in all_users:
  467. message = 'Username already exists'
  468. else:
  469. user.username = username
  470. user.email = email
  471. user.save()
  472. message = 'User updated successfully'
  473. Log.create(username=session['username'], action='Updated ILS user: %s' % username, ).save()
  474. context = {
  475. 'user': user,
  476. 'message': message,
  477. }
  478. return render_template('admin_ils_user_edit.html', context=context)
  479. # create a route for generating a CSV file for download
  480. @app.route('/admin/users/ils/csv/download', methods=['GET', 'POST'])
  481. def admin_ils_users_csv_download():
  482. # Check to see if user is logged in
  483. if not requires_auth():
  484. return redirect(url_for('login'))
  485. # Create a CSV file with the users and don't add a blank line between rows
  486. with open('users.csv', 'w', newline='') as f:
  487. writer = csv.writer(f)
  488. writer.writerow(['username', 'email'])
  489. users = IlsUser.select().execute()
  490. for user in users:
  491. writer.writerow([user.username, user.email])
  492. Log.create(username=session['username'], action='Downloaded ILS user CSV file.').save()
  493. # return the CSV file to the user
  494. return send_file('users.csv', as_attachment=True)
  495. @app.route('/admin/users/ils/csv/import', methods=['GET', 'POST'])
  496. def admin_ils_users_csv_import():
  497. # Check to see if user is logged in
  498. if not requires_auth():
  499. return redirect(url_for('login'))
  500. message = None
  501. if request.method == 'POST':
  502. csv_file = request.files['csv']
  503. if csv_file.filename != '':
  504. csv_file.save(os.path.join('uploads', csv_file.filename))
  505. with open(os.path.join('uploads', csv_file.filename), 'r') as f:
  506. reader = csv.reader(f)
  507. for row in reader:
  508. username = row[0]
  509. email = row[1]
  510. # ignore the header row
  511. if username == 'username':
  512. continue
  513. # ignore blank rows
  514. if username == '':
  515. continue
  516. # Check if user already exists and if it does update the entry
  517. try:
  518. user = IlsUser.filter(IlsUser.username == username).first()
  519. except Exception as e:
  520. print(e)
  521. user = None
  522. if user:
  523. user.email = email
  524. user.reset_datetime = datetime.datetime.now()
  525. user.save()
  526. else:
  527. IlsUser.create(username=username, email=email, reset_datetime=datetime.datetime.now()).save()
  528. # Delete the uploaded file
  529. os.remove(os.path.join('uploads', csv_file.filename))
  530. return redirect(url_for('admin_ils_users'))
  531. context = {
  532. 'message': message,
  533. }
  534. return render_template('csv.html', context=context)
  535. @app.route('/admin/settings', methods=['GET', 'POST'])
  536. def settings():
  537. # Check to see if user is logged in
  538. if not requires_auth():
  539. return redirect(url_for('login'))
  540. message = None
  541. # Process form submission
  542. if request.method == 'POST':
  543. # Assign form values to variables
  544. id = request.form.get('id')
  545. value = request.form.get('value')
  546. # Check if the setting exists
  547. try:
  548. setting = Settings.get(Settings.id == id)
  549. except Exception as e:
  550. print(e)
  551. setting = None
  552. if setting:
  553. # Scrub the values based on the setting name
  554. if setting.name == 'Debug Mode':
  555. if value.lower() == 'true':
  556. value = True
  557. else:
  558. value = False
  559. value = bool(value)
  560. elif setting.name == 'HTTP Port':
  561. value = str(value)
  562. elif setting.name == 'SMTP Host':
  563. value = str(value)
  564. elif setting.name == 'SMTP Port':
  565. value = str(value)
  566. elif setting.name == 'SMTP Username':
  567. value = str(value)
  568. elif setting.name == 'SMTP Password':
  569. value = str(value)
  570. elif setting.name == 'Domain Name':
  571. value = str(value)
  572. elif setting.name == 'Password Reset URL':
  573. value = str(value)
  574. elif setting.name == 'Password Reset Interval':
  575. if value.isdigit():
  576. value = int(value)
  577. else:
  578. value = setting.value
  579. # Update the setting
  580. old_value = setting.value
  581. setting.value = value
  582. setting.save()
  583. Log.create(username=session['username'], action='Changed %s setting from "%s" to "%s"' % (setting.name,
  584. old_value,
  585. value)).save()
  586. message = '%s updated successfully' % setting.name
  587. # Get settings from DB
  588. all_settings = Settings.select().execute()
  589. context = {
  590. 'settings': all_settings,
  591. 'message': message,
  592. }
  593. return render_template('settings.html', context=context)
  594. @app.route('/admin/schedule', methods=['GET', 'POST'])
  595. def schedule():
  596. # Check to see if user is logged in
  597. if not requires_auth():
  598. return redirect(url_for('login'))
  599. message = None
  600. # Get all schedules from the DB
  601. schedules = Schedule.select().order_by(Schedule.interval.cast("INTEGER")).execute()
  602. # add schedule
  603. if request.method == 'POST':
  604. # Assign form values to variables
  605. interval = request.form.get('interval')
  606. # Check if interval is a number
  607. try:
  608. int(interval)
  609. except Exception as e:
  610. print(e)
  611. message = 'Error creating schedule. Value submitted must be a whole number.'
  612. return render_template('schedule.html', context={'message': message, 'schedules': schedules})
  613. # Check if the schedule already exists
  614. try:
  615. schedule = Schedule.get(Schedule.interval == interval)
  616. except Exception as e:
  617. print(e)
  618. schedule = None
  619. if schedule:
  620. message = 'Schedule already exists'
  621. else:
  622. # Create the schedule
  623. Schedule.create(interval=interval).save()
  624. Log.create(username=session['username'], action='Created schedule for %s day interval.' % interval).save()
  625. message = 'Schedule: %s created successfully' % interval
  626. context = {
  627. 'schedules': schedules,
  628. 'message': message,
  629. }
  630. return render_template('schedule.html', context=context)
  631. @app.route('/admin/schedule/emails')
  632. def scheduled_emails():
  633. # Check to see if user is logged in
  634. if not requires_auth():
  635. return redirect(url_for('login'))
  636. # Get all future emailreminderlogs
  637. reminders = EmailReminderLog.select().where(EmailReminderLog.date > datetime.datetime.now()).order_by(
  638. -EmailReminderLog.date).execute()
  639. context = {
  640. 'reminders': reminders,
  641. }
  642. return render_template('scheduled_emails.html', context=context)
  643. # remove schedule
  644. @app.route('/admin/schedule/remove/reminder/<int:id>')
  645. def reminder_remove(id):
  646. # Check to see if user is logged in
  647. if not requires_auth():
  648. return redirect(url_for('login'))
  649. # Get EmailReminderLogs with the interval of the schedule being removed
  650. email_reminder_log = EmailReminderLog.get(EmailReminderLog.id == id)
  651. Log.create(username=session['username'],
  652. action='Removed %s day reminder for %s' % (email_reminder_log.interval, email_reminder_log.email)).save()
  653. email_reminder_log.delete_instance()
  654. return redirect(url_for('scheduled_emails'))
  655. # remove schedule
  656. @app.route('/admin/schedule/remove/<int:id>', methods=['GET', 'POST'])
  657. def schedule_remove(id):
  658. # Check to see if user is logged in
  659. if not requires_auth():
  660. return redirect(url_for('login'))
  661. # Get EmailReminderLogs with the interval of the schedule being removed
  662. email_reminder_logs = EmailReminderLog.select().where(EmailReminderLog.interval == id).execute()
  663. for email_reminder_log in email_reminder_logs:
  664. email_reminder_log.delete_instance()
  665. # Get the schedule from the DB
  666. schedule = Schedule.get(Schedule.id == id)
  667. schedule.delete_instance()
  668. Log.create(username=session['username'], action='Removed schedule for a %s day reminder.' % schedule.interval).save()
  669. return redirect(url_for('schedule'))
  670. @app.route('/admin/system/log')
  671. def system_log():
  672. # Check to see if user is logged in
  673. if not requires_auth():
  674. return redirect(url_for('login'))
  675. # Get all logs from the DB
  676. logs = Log.select().order_by(Log.id.desc()).execute()
  677. context = {
  678. 'logs': logs,
  679. }
  680. return render_template('system_log.html', context=context)
  681. @app.route('/admin/system/log/password/resets')
  682. def password_reset_log():
  683. # Check to see if user is logged in
  684. if not requires_auth():
  685. return redirect(url_for('login'))
  686. # Get all logs from the DB
  687. logs = PasswordResetLog.select().order_by(PasswordResetLog.id.desc()).execute()
  688. context = {
  689. 'logs': logs,
  690. }
  691. return render_template('password_reset_log.html', context=context)
  692. @app.route('/logout')
  693. def logout():
  694. if 'username' in session:
  695. username = session['username']
  696. user = User.get(User.username == username)
  697. user.logged_in = False
  698. Log.create(username=session['username'], action='Logged out').save()
  699. user.save()
  700. session.pop('username', None)
  701. return redirect(url_for('login'))
  702. @app.route('/login', methods=['GET', 'POST'])
  703. def login():
  704. if request.method == 'POST':
  705. username = request.form.get('username')
  706. password = encrypt_password(request.form.get('password'))
  707. try:
  708. user = User.filter(User.username == username and User.password == password).first()
  709. except Exception as e:
  710. print(e)
  711. user = None
  712. session.pop('username', None)
  713. if user:
  714. # Login user
  715. session['username'] = request.form.get('username')
  716. user.logged_in = True
  717. Log.create(username=session['username'], action='Logged in').save()
  718. user.save()
  719. return redirect(url_for('admin'))
  720. else:
  721. error = 'Invalid Credentials. Please try again.'
  722. context = {
  723. 'error': error
  724. }
  725. return render_template('login.html', context=context)
  726. context = {
  727. }
  728. return render_template('login.html', context=context)
  729. def clean_up():
  730. shutdown_scheduler.set()
  731. http_server.stop()
  732. if __name__ == "__main__":
  733. print("------------------------- Start up -----------------------------")
  734. print("Starting HTTP Service on port %s..." % http_port)
  735. if debug is True:
  736. print("Debug mode is enabled.")
  737. http_server = WSGIServer(('0.0.0.0', int(http_port)), app)
  738. else:
  739. http_server = WSGIServer(('0.0.0.0', int(http_port)), app, log=log, error_log=log)
  740. print("HTTP Service Started.")
  741. print("--------------------- Application Details ---------------------")
  742. print("Application started at %s" % datetime.datetime.now())
  743. print("System IP Address: %s" % get_ip_address())
  744. print("System Hostname: %s" % get_hostname())
  745. print("Access the Dashboard using a web browser using any of the following:")
  746. print("http://%s:%s or http://%s:%s" % (get_hostname(), http_port, get_ip_address(), http_port))
  747. print("---------------------------------------------------------------")
  748. print("To stop the application close this window.")
  749. print("---------------------------------------------------------------")
  750. http_server.serve_forever()
  751. atexit.register(clean_up)