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
62def handle_platform(game, platform):
63 try:
64 game_cover = "https:" + game["cover"]["url"]
65 except KeyError:
66 game_cover = c.MISSING_COVER_URL
67 temp_obj = {
68 "game_id": game["id"],
69 "platform_id": platform["id"],
70 "cover": game_cover,
71 "title": game["name"],
72 "platform": platform["name"]
73 }
74 temp_json = json.dumps(temp_obj)
75 temp_bytes = temp_json.encode("utf-8")
76 temp_base64 = base64.b64encode(temp_bytes)
77 temp_obj["info"] = temp_base64.decode("utf-8")
78 return temp_obj
79
80def handle_game(game):
81 return [ handle_platform(game, platform) for platform in game["platforms"] ]
82
83def collapse_list_of_lists(l):
84 return [ item for sublist in l for item in sublist ]
85
86@login_manager.user_loader
87def load_user(user_id):
88 lu = db_query_one("SELECT * from login where user_id = (?)", [user_id])
89 if lu is None:
90 return None
91 return construct_user(lu[0], lu[1], lu[2], lu[3])
92
93@app.route('/')
94def route_index():
95 return render("index.html")
96
97@app.route('/login', methods=['GET', 'POST'])
98def route_login():
99 if current_user.is_authenticated:
100 return redirect('/')
101
102 if request.method == "GET":
103 return render("login.html")
104
105 form = request.form
106 username = form["username"].lower()
107 password = form["password"]
108 try:
109 remember = bool(form["remember"])
110 except KeyError:
111 remember = False
112
113 r = db_query_one("SELECT * FROM login where username = (?)", [username])
114 if r is None:
115 return render_template("login.html", user=current_user, last_user=username)
116 user = construct_user(r[0], r[1], r[2], r[3])
117
118 if user is None:
119 return redirect(url_for("login"))
120
121 if user.password == password:
122 login_user(user, remember=remember)
123 return redirect("/")
124 else:
125 return render_template("login.html", user=current_user, last_user=username)
126
127@app.route('/register', methods=['GET', 'POST'])
128def route_register():
129 if current_user.is_authenticated:
130 return redirect('/')
131
132 if request.method == "GET":
133 return render("register.html")
134
135 return render("register.html")
136
137@app.route('/logout')
138def route_logout():
139 logout_user()
140 return redirect("/")
141
142@app.route('/search', methods=['GET', 'POST'])
143def route_search():
144 if not current_user.is_authenticated:
145 return redirect('/login')
146 if request.method == 'GET':
147 return render("search.html")
148
149 query = request.form["query"]
150 search_response = search_game(query)
151
152 games = collapse_list_of_lists([ handle_game(x) for x in search_response ])
153 return render_template("search.html", user=current_user, games=games, query=query)
154
155@app.route('/upload', methods=['GET', 'POST'])
156def route_upload():
157 if not current_user.is_authenticated:
158 return redirect('/login')
159 if request.method == 'GET':
160 info = request.args.get("info")
161 if info is None:
162 return render_template("upload.html", user=current_user, game=c.NO_GAME)
163 # info = base64
164 base64_bytes = info.encode('utf-8')
165 message_bytes = base64.b64decode(base64_bytes)
166 message = message_bytes.decode('utf-8')
167 game = json.loads(message)
168 return render_template("upload.html", user=current_user, game=game)
169
170 f = request.files['file']
171 try:
172 game_id = int(request.form['game_id'])
173 platform_id = int(request.form['platform_id'])
174 except ValueError:
175 return redirect("/upload")
176
177 # TODO: use IGDB api to validate game_id, platform_id and title before adding
178 # TODO: save game in DB
179
180 save_folder = os.path.join(c.BASE_DIRECTORY, c.CONTENT_DIRECTORY, str(current_user.id), str(game_id), str(platform_id))
181 with suppress(FileExistsError):
182 os.makedirs(save_folder)
183 if f.filename is None:
184 return redirect("/upload")
185 save_file = os.path.join(save_folder, secure_filename(f.filename))
186 f.save(save_file)
187 return render("index.html")
188
189
190@app.route('/admin', methods=['GET', 'POST'])
191def route_admin():
192 if not current_user.is_authenticated:
193 return redirect('/')
194
195 if current_user.id != 1:
196 return redirect('/')
197
198 if request.method == "GET":
199 return render("admin.html")
200
201 return render("admin.html")
202
203@app.route('/about')
204def route_about():
205 return render("about.html")
206
207if __name__ == "__main__":
208 app.debug=c.DEBUG_SWITCH
209 app.secret_key = c.SECRET_KEY
210 app.config['SESSION_TYPE'] = 'filesystem'
211 init_db()
212 app.run(port=1111)