2021-05-18 19:00:28 -04:00
|
|
|
from flask import Flask, request, jsonify, render_template
|
2021-03-29 15:48:22 -04:00
|
|
|
from flask_httpauth import HTTPBasicAuth
|
|
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
2021-03-15 17:33:18 -04:00
|
|
|
import json
|
|
|
|
import sqlite3
|
|
|
|
import os
|
2021-05-18 19:00:28 -04:00
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
2021-03-15 17:33:18 -04:00
|
|
|
DB_PATH = os.path.join(os.path.dirname(__file__), 'links.db')
|
|
|
|
|
2021-05-18 19:00:28 -04:00
|
|
|
load_dotenv()
|
|
|
|
|
2021-03-29 15:48:22 -04:00
|
|
|
app = Flask(__name__)
|
2021-04-02 16:14:50 -04:00
|
|
|
auth = HTTPBasicAuth('CustomBasic')
|
2021-03-29 15:48:22 -04:00
|
|
|
|
2021-05-18 19:00:28 -04:00
|
|
|
password = os.environ.get("PASSWORD")
|
|
|
|
if not password:
|
|
|
|
raise Exception("PASSWORD must be set")
|
|
|
|
|
|
|
|
port = int(os.environ.get("PORT") or 3000)
|
|
|
|
|
|
|
|
out_path = os.environ.get("OUT_PATH") or "/srv/www-csc-links/index.html"
|
|
|
|
|
2021-03-29 15:48:22 -04:00
|
|
|
users = {
|
2021-05-18 19:00:28 -04:00
|
|
|
"admin": generate_password_hash(password),
|
2021-03-29 15:48:22 -04:00
|
|
|
}
|
|
|
|
|
2021-04-02 21:56:12 -04:00
|
|
|
def get_data_from_query(query):
|
2021-03-15 17:33:18 -04:00
|
|
|
con = sqlite3.connect(DB_PATH)
|
|
|
|
con.row_factory = sqlite3.Row
|
|
|
|
cur = con.cursor()
|
2021-04-02 21:56:12 -04:00
|
|
|
cur.execute(query)
|
2021-03-15 17:33:18 -04:00
|
|
|
|
|
|
|
links_list = []
|
|
|
|
for row in cur.fetchall():
|
2021-04-02 21:56:12 -04:00
|
|
|
d = dict(zip(row.keys(), row))
|
|
|
|
links_list.append(d)
|
2021-03-15 17:33:18 -04:00
|
|
|
|
|
|
|
con.close()
|
2021-04-02 21:56:12 -04:00
|
|
|
return links_list
|
|
|
|
|
2021-05-18 19:00:28 -04:00
|
|
|
def regen_html(path):
|
|
|
|
"""Gets links from DB and outputs them in HTML"""
|
|
|
|
outfile = open(path, 'w')
|
2021-04-02 23:18:05 -04:00
|
|
|
links_list = get_data_from_query('SELECT url, name FROM links WHERE active=1 ORDER BY position')
|
2021-05-18 19:00:28 -04:00
|
|
|
html = render_template('template.html', links_list=links_list)
|
|
|
|
print(html, file=outfile)
|
|
|
|
outfile.close()
|
2021-03-04 10:23:21 -05:00
|
|
|
|
2021-03-29 15:48:22 -04:00
|
|
|
@auth.verify_password
|
|
|
|
def verify_password(username, password):
|
|
|
|
if username in users and \
|
|
|
|
check_password_hash(users.get(username), password):
|
|
|
|
return username
|
|
|
|
|
2021-04-02 21:56:12 -04:00
|
|
|
@app.route('/links', methods = ['GET'])
|
|
|
|
def get_links():
|
2021-04-02 23:18:05 -04:00
|
|
|
links_list = get_data_from_query('SELECT url, name FROM links WHERE active=1 ORDER BY position')
|
2021-04-02 21:56:12 -04:00
|
|
|
return jsonify(links_list)
|
|
|
|
|
|
|
|
@app.route('/editor/links', methods = ['POST'])
|
2021-03-29 15:48:22 -04:00
|
|
|
@auth.login_required
|
|
|
|
def update_links():
|
2021-05-18 19:00:28 -04:00
|
|
|
|
2021-03-29 15:48:22 -04:00
|
|
|
con = sqlite3.connect(DB_PATH)
|
|
|
|
cur = con.cursor()
|
|
|
|
try:
|
|
|
|
data = request.json['links']
|
2021-05-18 19:00:28 -04:00
|
|
|
|
2021-04-02 23:18:05 -04:00
|
|
|
items = 'url', 'name', 'clicks', 'active'
|
2021-03-29 15:48:22 -04:00
|
|
|
for i in range(len(data)):
|
2021-04-02 21:56:12 -04:00
|
|
|
if not(all(e in data[i] for e in items)):
|
2021-03-29 15:48:22 -04:00
|
|
|
return "Bad request, some items missing from link object", 400
|
|
|
|
|
2021-05-18 19:00:28 -04:00
|
|
|
links = []
|
|
|
|
cur.execute("begin")
|
|
|
|
cur.execute('DELETE FROM links')
|
|
|
|
for i in range(len(data)):
|
2021-03-29 15:48:22 -04:00
|
|
|
url = data[i]['url']
|
|
|
|
name = data[i]['name']
|
|
|
|
clicks = data[i]['clicks']
|
2021-04-02 23:18:05 -04:00
|
|
|
active = data[i]['active']
|
2021-04-02 21:56:12 -04:00
|
|
|
position = i
|
2021-03-29 15:48:22 -04:00
|
|
|
|
2021-04-02 23:18:05 -04:00
|
|
|
newlink = (url, name, clicks, position, active)
|
2021-03-29 15:48:22 -04:00
|
|
|
links.append(newlink)
|
|
|
|
|
2021-04-02 23:18:05 -04:00
|
|
|
cur.executemany('INSERT INTO links VALUES (?,?,?,?,?)', links)
|
2021-03-29 15:48:22 -04:00
|
|
|
con.commit()
|
2021-04-02 21:56:12 -04:00
|
|
|
con.close()
|
2021-05-18 19:00:28 -04:00
|
|
|
regen_html(out_path)
|
2021-04-02 21:56:12 -04:00
|
|
|
except Exception as e:
|
2021-03-29 15:48:22 -04:00
|
|
|
cur.execute("rollback")
|
2021-04-02 21:56:12 -04:00
|
|
|
con.close()
|
|
|
|
raise e
|
2021-03-29 18:56:59 -04:00
|
|
|
|
2021-04-02 23:18:05 -04:00
|
|
|
links_list = get_data_from_query('SELECT name, url, clicks, active FROM links ORDER BY position')
|
2021-04-02 21:56:12 -04:00
|
|
|
return jsonify(links_list)
|
2021-03-29 18:56:59 -04:00
|
|
|
|
2021-04-02 21:56:12 -04:00
|
|
|
@app.route('/editor/links', methods = ['GET'])
|
|
|
|
@auth.login_required
|
|
|
|
def get_editor_links():
|
|
|
|
"""endpoint lists all URLs and clicks, returns json object for editor."""
|
2021-04-02 23:18:05 -04:00
|
|
|
links_list = get_data_from_query('SELECT name, url, clicks, active FROM links ORDER BY position')
|
2021-03-29 18:56:59 -04:00
|
|
|
return jsonify(links_list)
|
|
|
|
|
2021-03-28 18:23:05 -04:00
|
|
|
@app.route('/clicks', methods=['POST'])
|
|
|
|
def update_clicks():
|
|
|
|
if ('url' not in request.json or 'name' not in request.json):
|
|
|
|
return 'url and/or name not found', 500
|
|
|
|
else:
|
|
|
|
url_id = request.json['url']
|
|
|
|
url_name = request.json['name']
|
|
|
|
con = sqlite3.connect(DB_PATH)
|
|
|
|
cur = con.cursor()
|
|
|
|
cur.execute("UPDATE links SET clicks=clicks + 1 WHERE url=? AND name=?", [url_id, url_name])
|
|
|
|
con.commit()
|
|
|
|
con.close()
|
|
|
|
return 'ok'
|
|
|
|
|
2021-03-04 10:23:21 -05:00
|
|
|
if __name__ == "__main__":
|
2021-05-18 19:00:28 -04:00
|
|
|
app.run(port=port, host="0.0.0.0")
|