myprecious/__main__.py (view raw)
1from flask import Flask, request, render_template, url_for, redirect
2from flask_login import LoginManager, UserMixin, login_user, logout_user, current_user
3from werkzeug.utils import secure_filename
4from contextlib import suppress
5import os, json, base64, sqlite3
6import hashlib, uuid
7import Constants as c
8if c.DEBUG_SWITCH:
9 from GamesApiTest import search_game
10else:
11 from GamesApi import search_game
12
13app = Flask(__name__)
14login_manager = LoginManager(app)
15
16def hash(password: str):
17 salt = uuid.uuid4().hex
18 return hashlib.sha512(password + salt).hexdigest(), salt
19
20def db_query_one(query, parameters):
21 with sqlite3.connect(c.DB_PATH) as db_connection:
22 curs = db_connection.cursor()
23 curs.execute(query, parameters)
24 try:
25 return list(curs.fetchone())
26 except TypeError:
27 return None
28
29def run_sql(sql_path: str):
30 with open(sql_path, 'r', encoding="utf-8") as f:
31 with sqlite3.connect(c.DB_PATH) as con:
32 curs = con.cursor()
33 sql = f.read()
34 curs.executescript(sql)
35
36def add_user(username, password, email):
37 query_str = "insert or ignore into login (username, password, email) values (?,?,?);"
38 query_param = [username, password, email]
39 return db_query_one(query_str, query_param)
40
41def init_db():
42 run_sql(c.MIGRATIONS_INIT_PATH)
43 add_user(c.DEFAULT_ADMIN_USER, c.DEFAULT_ADMIN_PW, c.DEFAULT_ADMIN_EMAIL)
44
45
46class User(UserMixin):
47 def __init__(self, user_id, username, password, email):
48 self.id = user_id
49 self.username = username
50 self.password = password
51 self.email = email
52
53def construct_user(id, username, password, email):
54 try:
55 return User(int(id), username, password, email)
56 except TypeError:
57 return None
58
59def render(template):
60 return render_template(template, user=current_user)
61
62
63def handle_platform(game, platform):
64 try:
65 game_cover = "https:" + game["cover"]["url"]
66 except KeyError:
67 game_cover = c.MISSING_COVER_URL
68 temp_obj = {
69 "game_id": game["id"],
70 "platform_id": platform["id"],
71 "cover": game_cover,
72 "title": game["name"],
73 "platform": platform["name"]
74 }
75 temp_json = json.dumps(temp_obj)
76 temp_bytes = temp_json.encode("utf-8")
77 temp_base64 = base64.b64encode(temp_bytes)
78 temp_obj["info"] = temp_base64.decode("utf-8")
79 return temp_obj
80
81def handle_game(game):
82 return [ handle_platform(game, platform) for platform in game["platforms"] ]
83
84def collapse_list_of_lists(l):
85 return [ item for sublist in l for item in sublist ]
86
87@login_manager.user_loader
88def load_user(user_id):
89 lu = db_query_one("SELECT * from login where user_id = (?)", [user_id])
90 if lu is None:
91 return None
92 return construct_user(lu[0], lu[1], lu[2], lu[3])
93
94@app.route('/')
95def route_index():
96 return render("index.html")
97
98@app.route('/login', methods=['GET', 'POST'])
99def route_login():
100 if current_user.is_authenticated:
101 return redirect('/')
102
103 if request.method == "GET":
104 return render("login.html")
105
106 form = request.form
107 username = form["username"].lower()
108 password = form["password"]
109 try:
110 remember = bool(form["remember"])
111 except KeyError:
112 remember = False
113
114 r = db_query_one("SELECT * FROM login where username = (?)", [username])
115 if r is None:
116 return render_template("login.html", user=current_user, last_user=username)
117 user = construct_user(r[0], r[1], r[2], r[3])
118
119 if user is None:
120 return redirect(url_for("login"))
121
122 if user.password == password:
123 login_user(user, remember=remember)
124 return redirect("/")
125 else:
126 return render_template("login.html", user=current_user, last_user=username)
127
128@app.route('/logout')
129def route_logout():
130 logout_user()
131 return redirect("/")
132
133@app.route('/search', methods=['GET', 'POST'])
134def route_search():
135 if not current_user.is_authenticated:
136 return redirect('/login')
137 if request.method == 'GET':
138 return render("search.html")
139
140 query = request.form["query"]
141 search_response = search_game(query)
142
143 games = collapse_list_of_lists([ handle_game(x) for x in search_response ])
144 return render_template("search.html", user=current_user, games=games, query=query)
145
146@app.route('/upload', methods=['GET', 'POST'])
147def route_upload():
148 if not current_user.is_authenticated:
149 return redirect('/login')
150 if request.method == 'GET':
151 info = request.args.get("info")
152 if info is None:
153 return render_template("upload.html", user=current_user, game=c.NO_GAME)
154 # info = base64
155 base64_bytes = info.encode('utf-8')
156 message_bytes = base64.b64decode(base64_bytes)
157 message = message_bytes.decode('utf-8')
158 game = json.loads(message)
159 return render_template("upload.html", user=current_user, game=game)
160
161 f = request.files['file']
162 try:
163 game_id = int(request.form['game_id'])
164 platform_id = int(request.form['platform_id'])
165 except ValueError:
166 return redirect("/upload")
167
168 # TODO: use IGDB api to validate game_id, platform_id and title before adding
169 # TODO: save game in DB
170
171 save_folder = os.path.join(c.BASE_DIRECTORY, c.CONTENT_DIRECTORY, str(current_user.id), str(game_id), str(platform_id))
172 with suppress(FileExistsError):
173 os.makedirs(save_folder)
174 if f.filename is None:
175 return redirect("/upload")
176 save_file = os.path.join(save_folder, secure_filename(f.filename))
177 f.save(save_file)
178 return render("index.html")
179
180if __name__ == "__main__":
181 app.debug=c.DEBUG_SWITCH
182 app.secret_key = c.SECRET_KEY
183 app.config['SESSION_TYPE'] = 'filesystem'
184 init_db()
185 app.run(port=1111)