Update server to work with the latest version :3
This commit is contained in:
parent
d73c7e44d5
commit
a7b9b5b87f
6 changed files with 186 additions and 46 deletions
|
@ -1,4 +1,7 @@
|
||||||
FROM python:3.11
|
FROM python:3.11
|
||||||
|
ADD https://github.com/krallin/tini/releases/download/v0.19.0/tini /tini
|
||||||
|
RUN chmod +x /tini
|
||||||
|
ENTRYPOINT ["/tini", "--"]
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
@ -6,4 +9,4 @@ COPY . .
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/tmp/pip pip install . --cache-dir /tmp/pip
|
RUN --mount=type=cache,target=/tmp/pip pip install . --cache-dir /tmp/pip
|
||||||
|
|
||||||
CMD python3 -m customiwmserver
|
CMD python3 -m uvicorn customiwmserver.main:app --host 0.0.0.0 --port 8001 --reload
|
||||||
|
|
|
@ -2,10 +2,19 @@ from pydantic import BaseModel
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
class MapPlay(BaseModel):
|
||||||
|
"""Used to store map playthroughs for players"""
|
||||||
|
Played: bool = True # Obviously
|
||||||
|
Clear: bool = False
|
||||||
|
FullClear: bool = False
|
||||||
|
|
||||||
class User(BaseModel):
|
class User(BaseModel):
|
||||||
"""Pydantic model for user data.
|
"""Pydantic model for user data.
|
||||||
Created from the user response (`/api/v1/user/x`),
|
Created from the user response (`/api/v1/user/x`),
|
||||||
some values might be unused."""
|
some values might be unused.
|
||||||
|
Color values are decimal versions of joined hexadecimal codes
|
||||||
|
(for example (0xFF, 0x00, 0x00) is joined to 0xFF0000 and converted to 16711680)
|
||||||
|
"""
|
||||||
|
|
||||||
Admin: bool = True
|
Admin: bool = True
|
||||||
Banned: bool = False
|
Banned: bool = False
|
||||||
|
@ -55,7 +64,12 @@ class User(BaseModel):
|
||||||
|
|
||||||
# Ratings have to be stored in the following format: {mapID: rating},
|
# Ratings have to be stored in the following format: {mapID: rating},
|
||||||
# where rating is either 1 or 5, or None.
|
# where rating is either 1 or 5, or None.
|
||||||
Ratings: dict = {}
|
Ratings: dict[str, Optional[int]] = {}
|
||||||
|
|
||||||
|
# Plays are stored in the following format:
|
||||||
|
# {mapID: {}}
|
||||||
|
# (str type is used instead of an int because MongoDB)
|
||||||
|
Plays: dict[str, MapPlay] = {}
|
||||||
|
|
||||||
|
|
||||||
class Notification(BaseModel):
|
class Notification(BaseModel):
|
||||||
|
@ -160,7 +174,8 @@ class Map(BaseModel):
|
||||||
MapCode: str = "AAABBAAA"
|
MapCode: str = "AAABBAAA"
|
||||||
Listed: bool = True
|
Listed: bool = True
|
||||||
HiddenInChallenges: bool = False
|
HiddenInChallenges: bool = False
|
||||||
DragonCoins: bool = False
|
ReplayVisibility: int = 0
|
||||||
|
DragonCoins: bool = False # Gems
|
||||||
|
|
||||||
# Might be duplicates from the User data
|
# Might be duplicates from the User data
|
||||||
|
|
||||||
|
@ -216,6 +231,10 @@ class Map(BaseModel):
|
||||||
BestTimePlaytime: int = 0
|
BestTimePlaytime: int = 0
|
||||||
MyBestPlaytime: int = 0
|
MyBestPlaytime: int = 0
|
||||||
|
|
||||||
|
BestFullTimeUserID: int = 0
|
||||||
|
BestFullTimeUsername: str = ""
|
||||||
|
BestFullTimePlaytime: int = 0
|
||||||
|
|
||||||
FirstClearUserID: Union[int, None] = 0
|
FirstClearUserID: Union[int, None] = 0
|
||||||
|
|
||||||
# Propably not needed, because you can simply read the
|
# Propably not needed, because you can simply read the
|
||||||
|
@ -240,6 +259,7 @@ class MapLeaderboard(BaseModel):
|
||||||
BestPlaytime: int
|
BestPlaytime: int
|
||||||
BestPlaytimeTime: str = datetime.strftime(datetime.now(), "%Y-%m-%dT%H:%M:%SZ")
|
BestPlaytimeTime: str = datetime.strftime(datetime.now(), "%Y-%m-%dT%H:%M:%SZ")
|
||||||
BestReplay: str
|
BestReplay: str
|
||||||
|
FullClear: bool = False
|
||||||
CreatorName: str
|
CreatorName: str
|
||||||
UserID: int
|
UserID: int
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Literal
|
||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,25 +29,26 @@ def LogAdminAction(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def auth_check(Authorization):
|
def auth_check(Authorization) -> (tuple[Literal[False], Literal["noauth"]] | tuple[Literal[True], dict]):
|
||||||
"""Checks credentials.
|
"""Checks credentials.
|
||||||
Returns a tuple with result (for example False, "nouser").
|
Returns a tuple with result (for example False, "nouser").
|
||||||
|
|
||||||
Results:
|
Results:
|
||||||
- nouser = user not found
|
- False if wrong username or password
|
||||||
- wrongpass = wrong password
|
- True, [dict] if correct
|
||||||
- [dictionary] = query
|
|
||||||
"""
|
"""
|
||||||
|
# FIXME: This function currently DOES NOT perform any authentication.
|
||||||
|
# This means that ANYONE knowing the username could perform actions as the user.
|
||||||
if Authorization is None:
|
if Authorization is None:
|
||||||
return False, "noauth"
|
return False, "noauth"
|
||||||
username, password = Authorization.split(":")
|
username, password = Authorization.split(":")
|
||||||
|
|
||||||
query = user_collection.find_one({"Username": username})
|
query = user_collection.find_one({"Username": username})
|
||||||
if not query:
|
if not query:
|
||||||
return False, "nouser"
|
return False, "noauth"
|
||||||
|
|
||||||
if query["Password"] != password:
|
# if query["Password"] != password:
|
||||||
return False, "wrongpass"
|
# return False, "wrongpass"
|
||||||
|
|
||||||
return True, query
|
return True, query
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# from . import *
|
# from . import *
|
||||||
|
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
|
||||||
__path__ = pkgutil.extend_path(__path__, __name__)
|
__path__ = pkgutil.extend_path(__path__, __name__)
|
||||||
|
|
|
@ -38,10 +38,7 @@ async def login(username: str = Form(), password: str = Form(), version: str = F
|
||||||
hook.execute_hooks("player_login", username=username)
|
hook.execute_hooks("player_login", username=username)
|
||||||
auth = db.auth_check(username + ":" + password)
|
auth = db.auth_check(username + ":" + password)
|
||||||
if not auth[0]:
|
if not auth[0]:
|
||||||
if auth[1] == "nouser":
|
raise HTTPException(403, detail="Wrong username or password.")
|
||||||
raise HTTPException(404, detail="User not found.")
|
|
||||||
elif auth[1] == "wrongpass":
|
|
||||||
raise HTTPException(403, detail="Password is incorrect.")
|
|
||||||
else:
|
else:
|
||||||
return {"token": username + ":" + password, "userId": auth[1]["ID"]}
|
return {"token": username + ":" + password, "userId": auth[1]["ID"]}
|
||||||
|
|
||||||
|
@ -72,9 +69,43 @@ async def create_user(
|
||||||
@app.get("/api/v1/notifunread")
|
@app.get("/api/v1/notifunread")
|
||||||
async def notifunread():
|
async def notifunread():
|
||||||
"""Returns the number of unread notifications."""
|
"""Returns the number of unread notifications."""
|
||||||
# FIXME Add notifications
|
# FIXME: Add notifications
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@app.post("/api/v1/user/{userID}")
|
||||||
|
async def update_user_profile(
|
||||||
|
userID: int,
|
||||||
|
Authorization: Union[str, None] = Header(default=None),
|
||||||
|
ID: int = Body(),
|
||||||
|
Country: int = Body(),
|
||||||
|
PantsColor: int = Body(),
|
||||||
|
HatSpr: int = Body(),
|
||||||
|
HairColor: int = Body(),
|
||||||
|
CapeColor: int = Body(),
|
||||||
|
ShoesColor: int = Body(),
|
||||||
|
ShirtColor: int = Body(),
|
||||||
|
SkinColor: int = Body(),
|
||||||
|
):
|
||||||
|
authcheck = db.auth_check(Authorization)
|
||||||
|
if not authcheck[0]:
|
||||||
|
raise HTTPException(403, detail="Wrong username or password")
|
||||||
|
elif authcheck[0]:
|
||||||
|
userData = types.User(**authcheck[1])
|
||||||
|
userData.Country = Country
|
||||||
|
userData.PantsColor = PantsColor
|
||||||
|
userData.HatSpr = HatSpr
|
||||||
|
userData.HairColor = HairColor
|
||||||
|
userData.CapeColor = CapeColor
|
||||||
|
userData.ShoesColor = ShoesColor
|
||||||
|
userData.ShirtColor = ShirtColor
|
||||||
|
userData.SkinColor = SkinColor
|
||||||
|
userData.UpdatedAt = datetime.strftime(datetime.now(), "%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
|
||||||
|
db.user_collection.update_one({"ID":userID}, {"$set": {**userData.dict()} })
|
||||||
|
hook.execute_hooks("player_update", userID=userID)
|
||||||
|
return userData.dict()
|
||||||
|
return HTTPException(500, detail="Server failed to handle login (somehow)")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/v1/refresh")
|
@app.get("/api/v1/refresh")
|
||||||
async def refresh_login(Authorization: Union[str, None] = Header(default=None)):
|
async def refresh_login(Authorization: Union[str, None] = Header(default=None)):
|
||||||
|
@ -106,6 +137,7 @@ async def useruploadcooldown():
|
||||||
|
|
||||||
@app.get("/api/v1/map")
|
@app.get("/api/v1/map")
|
||||||
async def search_for_maps(
|
async def search_for_maps(
|
||||||
|
Authorization: Union[str, None] = Header(default=None),
|
||||||
start: int = 0,
|
start: int = 0,
|
||||||
limit: int = 5,
|
limit: int = 5,
|
||||||
min_diff: float = 0.0,
|
min_diff: float = 0.0,
|
||||||
|
@ -121,6 +153,18 @@ async def search_for_maps(
|
||||||
admin_show_unlisted: Optional[int] = 0,
|
admin_show_unlisted: Optional[int] = 0,
|
||||||
):
|
):
|
||||||
"""Search for maps."""
|
"""Search for maps."""
|
||||||
|
if Authorization is not None:
|
||||||
|
authcheck = db.auth_check(Authorization)
|
||||||
|
if not authcheck[0]:
|
||||||
|
raise HTTPException(403,
|
||||||
|
detail="Wrong username or password."
|
||||||
|
+ "\nIf you're using the API directly, "
|
||||||
|
+ "then you can just skip the Authorization")
|
||||||
|
userData = types.User(**authcheck[1])
|
||||||
|
plays = userData.Plays
|
||||||
|
else:
|
||||||
|
userData = None
|
||||||
|
plays = {}
|
||||||
|
|
||||||
# query = db.maps_collection.find({ "CreatorId": author_id }).limit(limit)
|
# query = db.maps_collection.find({ "CreatorId": author_id }).limit(limit)
|
||||||
if code:
|
if code:
|
||||||
|
@ -138,13 +182,39 @@ async def search_for_maps(
|
||||||
for i in query:
|
for i in query:
|
||||||
del i["_id"]
|
del i["_id"]
|
||||||
del i["MapData"]
|
del i["MapData"]
|
||||||
|
del i["Played"]
|
||||||
|
del i["Clear"]
|
||||||
|
del i["FullClear"]
|
||||||
|
|
||||||
if not i["Listed"] and admin_show_unlisted != 1:
|
if not i["Listed"] and admin_show_unlisted != 1:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
level_tags = i['TagIDs'].split(",")
|
level_tags = i['TagIDs'].split(",")
|
||||||
CreatedAt = datetime.strptime(i["CreatedAt"], "%Y-%m-%dT%H:%M:%SZ")
|
CreatedAt = datetime.strptime(i["CreatedAt"], "%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
|
||||||
|
Played = False
|
||||||
|
Clear = False
|
||||||
|
FullClear = False
|
||||||
|
if plays and str(i["ID"]) in plays:
|
||||||
|
play = plays[str(i["ID"])]
|
||||||
|
Played = play.Played
|
||||||
|
Clear = play.Clear
|
||||||
|
FullClear = play.FullClear
|
||||||
|
# if userData:
|
||||||
|
# # Old system that checked if a user has a record, remove
|
||||||
|
# leaderboard = i["Leaderboard"]
|
||||||
|
# for record in leaderboard:
|
||||||
|
# record = types.MapLeaderboard(**record)
|
||||||
|
# if record.UserID == userData.ID:
|
||||||
|
# Played = True
|
||||||
|
# Clear = True
|
||||||
|
# if record.FullClear:
|
||||||
|
# FullClear = True
|
||||||
|
# break
|
||||||
|
# print(f"DBG: {i}, {leaderboard[i]}\t{leaderboard[i]['UserID'] == int(userID):}")
|
||||||
|
# if leaderboard[i]["UserID"] == int(userID) and (BestTime is None or leaderboard[i]["BestPlaytime"] < BestTime):
|
||||||
|
# replayIndex = i
|
||||||
|
|
||||||
# TODO: Improve tag filtering
|
# TODO: Improve tag filtering
|
||||||
required_tags_included = False if len(required_tag_list) != 0 else True
|
required_tags_included = False if len(required_tag_list) != 0 else True
|
||||||
disallowed_tags_included = False
|
disallowed_tags_included = False
|
||||||
|
@ -154,7 +224,7 @@ async def search_for_maps(
|
||||||
else:
|
else:
|
||||||
if required_tags_included and not disallowed_tags_included:
|
if required_tags_included and not disallowed_tags_included:
|
||||||
if not last_x_hours or CreatedAt.hour < last_x_hours:
|
if not last_x_hours or CreatedAt.hour < last_x_hours:
|
||||||
entries.append(i)
|
entries.append(types.Map(Played=Played, Clear=Clear, FullClear=FullClear, **i))
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,7 +232,7 @@ async def search_for_maps(
|
||||||
async def getMap(mapID: int):
|
async def getMap(mapID: int):
|
||||||
query = db.maps_collection.find_one({"ID": mapID})
|
query = db.maps_collection.find_one({"ID": mapID})
|
||||||
del query["_id"]
|
del query["_id"]
|
||||||
return query
|
return types.Map(**query)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/v1/map/{mapID}/start")
|
@app.post("/api/v1/map/{mapID}/start")
|
||||||
|
@ -171,20 +241,35 @@ async def startMap(
|
||||||
Authorization: Union[str, None] = Header(default=None)
|
Authorization: Union[str, None] = Header(default=None)
|
||||||
):
|
):
|
||||||
authcheck = db.auth_check(Authorization)
|
authcheck = db.auth_check(Authorization)
|
||||||
if not authcheck[0] and authcheck[1] == "nouser":
|
if not authcheck[0] and authcheck[1]:
|
||||||
raise HTTPException(404, detail="User not found")
|
raise HTTPException(403, detail="Wrong username or password.")
|
||||||
elif not authcheck[0] and authcheck[1] == "wrongpass":
|
|
||||||
raise HTTPException(403, detail="Wrong password")
|
|
||||||
elif authcheck[0]:
|
elif authcheck[0]:
|
||||||
userData = types.User(**authcheck[1])
|
userData = types.User(**authcheck[1])
|
||||||
query = db.maps_collection.find_one({"ID": mapID})
|
query = db.maps_collection.find_one({"ID": mapID})
|
||||||
del query["_id"]
|
del query["_id"]
|
||||||
|
Clear = False
|
||||||
|
Played = False
|
||||||
|
if str(mapID) not in userData.Plays :
|
||||||
|
updateQuery = db.user_collection.update_one(
|
||||||
|
{"ID": userData.ID},
|
||||||
|
{
|
||||||
|
"$set": {
|
||||||
|
f"Plays.{mapID}": dict(types.MapPlay())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
play = userData.Plays[str(mapID)]
|
||||||
|
Clear = play.Clear
|
||||||
|
Played = play.Played
|
||||||
|
FullClear = play.FullClear
|
||||||
|
|
||||||
|
|
||||||
returned_resp = {
|
returned_resp = {
|
||||||
"BestDeaths": 0,
|
"BestDeaths": 0,
|
||||||
"BestPlaytime": 0,
|
"BestPlaytime": 0,
|
||||||
"Clear": False,
|
"Clear": False,
|
||||||
"CurMap": query,
|
"CurMap": types.Map(**query),
|
||||||
"Difficulty": 0,
|
"Difficulty": 0,
|
||||||
"Followed": False,
|
"Followed": False,
|
||||||
"Played": True,
|
"Played": True,
|
||||||
|
@ -203,21 +288,29 @@ async def stopMapPlay(
|
||||||
playtime: int = Form(),
|
playtime: int = Form(),
|
||||||
totalDeaths: int = Form(),
|
totalDeaths: int = Form(),
|
||||||
totalTime: int = Form(),
|
totalTime: int = Form(),
|
||||||
|
fullClear: int = Form(),
|
||||||
replayData: str = Form(),
|
replayData: str = Form(),
|
||||||
Authorization: Union[str, None] = Header(default=None),
|
Authorization: Union[str, None] = Header(default=None),
|
||||||
):
|
):
|
||||||
"""Saves the map replay, and informs the user if their play is a record"""
|
"""Saves the map replay, and informs the user if their play is a record"""
|
||||||
authcheck = db.auth_check(Authorization)
|
authcheck = db.auth_check(Authorization)
|
||||||
if not authcheck[0] and authcheck[1] == "nouser":
|
if not authcheck[0]:
|
||||||
raise HTTPException(404, detail="User not found")
|
raise HTTPException(403, detail="Wrong username or password")
|
||||||
elif not authcheck[0] and authcheck[1] == "wrongpass":
|
|
||||||
raise HTTPException(403, detail="Wrong password")
|
|
||||||
elif authcheck[0]:
|
elif authcheck[0]:
|
||||||
NewMapRecord = False
|
NewMapRecord = False
|
||||||
FirstClear = False
|
FirstClear = False
|
||||||
|
|
||||||
userData = types.User(**authcheck[1])
|
userData = types.User(**authcheck[1])
|
||||||
|
|
||||||
|
userUpdateQuery = db.user_collection.update_one(
|
||||||
|
{"ID": userData.ID},
|
||||||
|
{
|
||||||
|
"$set": {
|
||||||
|
f"Plays.{mapID}": dict(types.MapPlay(Clear=bool(clear), FullClear=bool(fullClear)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
query = db.maps_collection.find_one(
|
query = db.maps_collection.find_one(
|
||||||
{"ID": mapID, "Leaderboard.UserID": userData.ID}
|
{"ID": mapID, "Leaderboard.UserID": userData.ID}
|
||||||
)
|
)
|
||||||
|
@ -226,6 +319,7 @@ async def stopMapPlay(
|
||||||
|
|
||||||
BestUserTime = None
|
BestUserTime = None
|
||||||
BestTime = None
|
BestTime = None
|
||||||
|
BestFullTime = None
|
||||||
if query is not None and "Leaderboard" in query:
|
if query is not None and "Leaderboard" in query:
|
||||||
for i in query["Leaderboard"]:
|
for i in query["Leaderboard"]:
|
||||||
if i["UserID"] == userData.ID:
|
if i["UserID"] == userData.ID:
|
||||||
|
@ -233,9 +327,12 @@ async def stopMapPlay(
|
||||||
BestUserTime = i["BestPlaytime"]
|
BestUserTime = i["BestPlaytime"]
|
||||||
if BestTime is None or BestTime > i["BestPlaytime"]:
|
if BestTime is None or BestTime > i["BestPlaytime"]:
|
||||||
BestTime = i["BestPlaytime"]
|
BestTime = i["BestPlaytime"]
|
||||||
|
elif BestFullTime is None or BestFullTime > i["BestFullTimePlaytime"]:
|
||||||
|
BestFullTime = i["BestFullTimePlaytime"]
|
||||||
|
|
||||||
if len(query["Leaderboard"]) <= 1 and i["UserID"] != userData.ID:
|
if len(query["Leaderboard"]) <= 1 and i["UserID"] != userData.ID:
|
||||||
FirstClear = True
|
FirstClear = True
|
||||||
if BestUserTime is None or playtime < BestUserTime:
|
if (BestTime is None or playtime < BestTime) or (BestFullTime is None or playtime < BestFullTime):
|
||||||
updateQuery = db.maps_collection.update_one(
|
updateQuery = db.maps_collection.update_one(
|
||||||
{"ID": mapID},
|
{"ID": mapID},
|
||||||
{"$pull": {"Leaderboard": {"UserID": userData.ID}},
|
{"$pull": {"Leaderboard": {"UserID": userData.ID}},
|
||||||
|
@ -271,11 +368,12 @@ async def stopMapPlay(
|
||||||
BestReplay=replayData,
|
BestReplay=replayData,
|
||||||
CreatorName=userData.Username,
|
CreatorName=userData.Username,
|
||||||
UserID=userData.ID,
|
UserID=userData.ID,
|
||||||
|
FullClear=bool(fullClear)
|
||||||
).dict()
|
).dict()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if BestTime is None or playtime < BestTime:
|
if (BestTime is None or playtime < BestTime) or (BestFullTime is None or playtime < BestFullTime):
|
||||||
NewMapRecord = True
|
NewMapRecord = True
|
||||||
|
|
||||||
hook.execute_hooks(
|
hook.execute_hooks(
|
||||||
|
@ -285,6 +383,7 @@ async def stopMapPlay(
|
||||||
clear=clear,
|
clear=clear,
|
||||||
deaths=deaths,
|
deaths=deaths,
|
||||||
playtime=playtime,
|
playtime=playtime,
|
||||||
|
fullClear=fullClear,
|
||||||
FirstClear=FirstClear,
|
FirstClear=FirstClear,
|
||||||
NewMapRecord=NewMapRecord,
|
NewMapRecord=NewMapRecord,
|
||||||
)
|
)
|
||||||
|
@ -308,15 +407,16 @@ async def upload_map(
|
||||||
listed: int = Form(),
|
listed: int = Form(),
|
||||||
requiresCancels: int = Form(),
|
requiresCancels: int = Form(),
|
||||||
hideInChallenges: int = Form(),
|
hideInChallenges: int = Form(),
|
||||||
tags: str = Form(),
|
tags: str = Form(default=""),
|
||||||
rng: int = Form(),
|
rng: int = Form(),
|
||||||
|
numSubmaps: int = Form(),
|
||||||
|
dragonCoins: int = Form(),
|
||||||
|
replayVisibility: int = Form(),
|
||||||
clientVersion: float = Form(),
|
clientVersion: float = Form(),
|
||||||
):
|
):
|
||||||
authcheck = db.auth_check(Authorization)
|
authcheck = db.auth_check(Authorization)
|
||||||
if not authcheck[0] and authcheck[1] == "nouser":
|
if not authcheck[0]:
|
||||||
raise HTTPException(404, detail="User not found")
|
raise HTTPException(403, detail="Wrong username or password")
|
||||||
elif not authcheck[0] and authcheck[1] == "wrongpass":
|
|
||||||
raise HTTPException(403, detail="Wrong password")
|
|
||||||
elif authcheck[0]:
|
elif authcheck[0]:
|
||||||
print(authcheck)
|
print(authcheck)
|
||||||
userData = types.User(**authcheck[1])
|
userData = types.User(**authcheck[1])
|
||||||
|
@ -454,13 +554,21 @@ async def rateMap(mapID: int, Rating: Rating, Authorization: Union[str, None] =
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.get("/api/v1/map/{mapID}/besttimes/{maxEntries}")
|
@app.get("/api/v1/map/{mapID}/besttimes/{maxEntries}")
|
||||||
async def getMapLeaderboard(mapID: int, maxEntries: int = 5):
|
async def getMapLeaderboard(mapID: int, maxEntries: int = 5, full_clear: int = 0):
|
||||||
"""Returns maxEntries records for the specified level"""
|
"""Returns maxEntries records for the specified level"""
|
||||||
query = db.maps_collection.find_one({"ID": int(mapID)})
|
query = db.maps_collection.find_one({"ID": int(mapID)})
|
||||||
if not query:
|
if not query:
|
||||||
raise HTTPException(404, detail="Map not found")
|
raise HTTPException(404, detail="Map not found")
|
||||||
del query["_id"]
|
del query["_id"]
|
||||||
leaderboard = query["Leaderboard"]
|
leaderboard = []
|
||||||
|
for record in query["Leaderboard"]:
|
||||||
|
record = types.MapLeaderboard(**record)
|
||||||
|
if record.FullClear and full_clear == 1:
|
||||||
|
leaderboard.append(dict(record))
|
||||||
|
elif not record.FullClear and full_clear == 0:
|
||||||
|
leaderboard.append(dict(record))
|
||||||
|
else:
|
||||||
|
leaderboard.append(dict(record))
|
||||||
|
|
||||||
return sorted(leaderboard, key=lambda sort: sort["BestPlaytime"])[0:maxEntries]
|
return sorted(leaderboard, key=lambda sort: sort["BestPlaytime"])[0:maxEntries]
|
||||||
|
|
||||||
|
@ -513,9 +621,7 @@ async def invalidateAllTimes(
|
||||||
|
|
||||||
authcheck = db.auth_check(Authorization)
|
authcheck = db.auth_check(Authorization)
|
||||||
if not authcheck[0] and authcheck[1] == "nouser":
|
if not authcheck[0] and authcheck[1] == "nouser":
|
||||||
raise HTTPException(404, detail="User not found")
|
raise HTTPException(403, detail="Wrong username or password")
|
||||||
elif not authcheck[0] and authcheck[1] == "wrongpass":
|
|
||||||
raise HTTPException(403, detail="Wrong password")
|
|
||||||
elif authcheck[0]:
|
elif authcheck[0]:
|
||||||
userData = types.User(**authcheck[1])
|
userData = types.User(**authcheck[1])
|
||||||
if userData.Admin:
|
if userData.Admin:
|
||||||
|
@ -562,9 +668,7 @@ async def reportContent(
|
||||||
|
|
||||||
authcheck = db.auth_check(Authorization)
|
authcheck = db.auth_check(Authorization)
|
||||||
if not authcheck[0] and authcheck[1] == "nouser":
|
if not authcheck[0] and authcheck[1] == "nouser":
|
||||||
raise HTTPException(404, detail="User not found")
|
raise HTTPException(403, detail="Wrong user or password")
|
||||||
elif not authcheck[0] and authcheck[1] == "wrongpass":
|
|
||||||
raise HTTPException(403, detail="Wrong password")
|
|
||||||
elif authcheck[0]:
|
elif authcheck[0]:
|
||||||
db.reports_collection.insert_one(
|
db.reports_collection.insert_one(
|
||||||
{
|
{
|
||||||
|
@ -590,6 +694,17 @@ async def followcheck():
|
||||||
# FIXME: Stub
|
# FIXME: Stub
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@app.post("/api/v1/unlockachievement", response_class=PlainTextResponse)
|
||||||
|
async def unlockachievement(
|
||||||
|
Authorization: Union[str, None] = Header(default=None),
|
||||||
|
achievementId: int = Form()
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Adds an achivement to the user
|
||||||
|
STUB
|
||||||
|
"""
|
||||||
|
return "Success"
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
"""Launched with `poetry run start` at root level"""
|
"""Launched with `poetry run start` at root level"""
|
||||||
from . import __main__
|
from . import __main__
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
build: .
|
build: .
|
||||||
|
# restart: on-failure
|
||||||
ports:
|
ports:
|
||||||
- "8001:8001"
|
- "8001:8001"
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -15,7 +16,7 @@ services:
|
||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
image: mongo
|
image: mongo
|
||||||
restart: always
|
# restart: on-failure
|
||||||
expose:
|
expose:
|
||||||
- "27017:27017"
|
- "27017:27017"
|
||||||
environment:
|
environment:
|
||||||
|
@ -26,7 +27,7 @@ services:
|
||||||
|
|
||||||
mongo-express:
|
mongo-express:
|
||||||
image: mongo-express
|
image: mongo-express
|
||||||
restart: always
|
# restart: on-failure
|
||||||
ports:
|
ports:
|
||||||
- 8081:8081
|
- 8081:8081
|
||||||
environment:
|
environment:
|
||||||
|
|
Loading…
Reference in a new issue