Beracles commited on
Commit
0f00fdc
·
1 Parent(s): e63b9e1

implement file service

Browse files
Files changed (3) hide show
  1. src/app/storage.py +134 -279
  2. src/app/utils.py +0 -280
  3. src/status.py +0 -10
src/app/storage.py CHANGED
@@ -1,285 +1,140 @@
1
- from fastapi import Response
2
- from .. import status
3
- from . import utils
4
- from fastapi.routing import APIRouter
5
  from pgsoft.pghash.md5 import md5
 
 
 
 
6
 
7
 
8
- router = APIRouter(
9
- prefix="/storage",
10
- tags=["storage"],
11
- )
12
  admin_token_md5 = "01112732d503cd3f0898d185a11c8b92"
13
-
14
-
15
- @router.post("/create_user")
16
- async def create_user(admin_token: str, email: str, response: Response) -> dict:
17
- """ Create a normal user with user's email
18
-
19
- Args:
20
- admin_token (str): a token string of administrator
21
- email (str): user's email
22
-
23
- Returns:
24
- dict: keys includes "status", "msg" and "data"
25
- """
26
- if md5(admin_token) != admin_token_md5:
27
- response.status_code = status.HTTP_401_UNAUTHORIZED
28
- return {
29
- "status": status.FAILURE,
30
- "data": {},
31
- "msg": "Unauthorized"
32
- }
33
- email = email.lower().strip()
34
- if utils.is_user_existed(email):
35
- response.status_code = status.HTTP_304_NOT_MODIFIED
36
- return {
37
- "status": status.FAILURE,
38
- "data": {},
39
- "msg": "User already exists"
40
- }
41
- secret = utils.create_account(email, status.USER)
42
- if secret is None:
43
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
44
- return {
45
- "status": status.FAILURE,
46
- "data": {},
47
- "msg": "creating normal user failed"
48
- }
49
- response.status_code = status.HTTP_201_CREATED
50
- return {
51
- "status": status.SUCCESS,
52
- "data": {
53
- "secret": secret,
54
- "email": email
55
- },
56
- "msg": "created normal user successfully."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
-
59
-
60
- @router.post("/register")
61
- async def register(secret: str, email: str, password: str, response: Response) -> dict:
62
- """ Register an account with user's secret and password
63
-
64
- Args:
65
- secret (str): a random md5code provided by administrator
66
- email (str): user's email
67
- password (str): user's password
68
-
69
- Returns:
70
- dict: keys includes "status", "msg" and "data"
71
- """
72
- email = email.lower().strip()
73
- if not utils.is_user_existed(email):
74
- response.status_code = status.HTTP_404_NOT_FOUND
75
- return {
76
- "status": status.FAILURE,
77
- "data": {},
78
- "msg": "user not found"
79
- }
80
- if utils.is_user_registered(email):
81
- response.status_code = status.HTTP_304_NOT_MODIFIED
82
- return {
83
- "status": status.FAILURE,
84
- "data": {},
85
- "msg": "you can't register repeatedly"
86
- }
87
- saved_secret = utils.get_secret(email)
88
- if saved_secret is None:
89
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
90
- return {
91
- "status": status.FAILURE,
92
- "data": {},
93
- "msg": "secret not existed"
94
- }
95
- if saved_secret != secret:
96
- response.status_code = status.HTTP_304_NOT_MODIFIED
97
- return {
98
- "status": status.FAILURE,
99
- "data": {},
100
- "msg": "incorrect secret or maybe your email is wrong",
101
- }
102
- if not utils.set_password(email, md5(password)):
103
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
104
- return {
105
- "status": status.FAILURE,
106
- "data": {},
107
- "msg": "failed to register"
108
- }
109
- if not utils.remove_user_secret(email):
110
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
111
- return {
112
- "status": status.FAILURE,
113
- "data": {},
114
- "msg": "failed to remove secret"
115
- }
116
- response.status_code = status.HTTP_200_OK
117
- return {
118
- "status": status.SUCCESS,
119
- "data": {},
120
- "msg": "Register Successfully, please login"
121
- }
122
-
123
-
124
- @router.post("/login")
125
- async def login(email: str, password: str, response: Response) -> dict:
126
- if email is None or email == "":
127
- response.status_code = status.HTTP_400_BAD_REQUEST
128
- return {
129
- "status": status.FAILURE,
130
- "data": {},
131
- "msg": "email is empty"
132
- }
133
- if password is None or password == "":
134
- response.status_code = status.HTTP_400_BAD_REQUEST
135
- return {
136
- "status": status.FAILURE,
137
- "data": {},
138
- "msg": "password is empty"
139
- }
140
- email = email.lower().strip()
141
- if not utils.is_user_existed(email):
142
- response.status_code = status.HTTP_404_NOT_FOUND
143
- return {
144
- "status": status.FAILURE,
145
- "data": {},
146
- "msg": "email does not exist"
147
- }
148
- password_digest = utils.get_password(email)
149
- if password_digest is None:
150
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
151
- return {
152
- "status": status.FAILURE,
153
- "data": {},
154
- "msg": "password not existed"
155
- }
156
- password = md5(password)
157
- if password_digest != password:
158
- response.status_code = status.HTTP_400_BAD_REQUEST
159
- return {
160
- "status": status.FAILURE,
161
- "data": {},
162
- "msg": "incorrect password"
163
- }
164
- response.status_code = status.HTTP_200_OK
165
- return {
166
- "status": status.SUCCESS,
167
- "data": {},
168
- "msg": "login succeeded"
169
- }
170
-
171
-
172
- @router.post("/delete_password")
173
- async def delete_password(admin_token: str, email: str, response: Response) -> dict:
174
- """ delete password with user's email
175
-
176
- Args:
177
- admin_token (str): a token string of administrator
178
- email (str): user's email
179
-
180
- Returns:
181
- dict: keys includes "status", "msg" and "data"
182
- """
183
- if md5(admin_token) != admin_token_md5:
184
- response.status_code = status.HTTP_401_UNAUTHORIZED
185
- return {
186
- "status": status.FAILURE,
187
- "data": {},
188
- "msg": "Unauthorized"
189
- }
190
- email = email.lower().strip()
191
- if not utils.is_user_existed(email):
192
- response.status_code = status.HTTP_304_NOT_MODIFIED
193
- return {
194
- "status": status.FAILURE,
195
- "data": {},
196
- "msg": "User does not exists"
197
- }
198
- if not utils.get_password(email):
199
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
200
- return {
201
- "status": status.FAILURE,
202
- "data": {},
203
- "msg": "password not existed"
204
- }
205
- if not utils.delete_password(email):
206
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
207
- return {
208
- "status": status.FAILURE,
209
- "data": {},
210
- "msg": "failed to delete password"
211
- }
212
- secret = utils.generate_secret(email)
213
- if secret is None:
214
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
215
- return {
216
- "status": status.FAILURE,
217
- "data": {},
218
- "msg": "creating secret failed"
219
- }
220
- response.status_code = status.HTTP_200_OK
221
- return {
222
- "status": status.SUCCESS,
223
- "data": {
224
- "secret": secret,
225
- "email": email
226
- },
227
- "msg": "successfully deleted password."
228
- }
229
-
230
-
231
- @router.post("/reset_password")
232
- async def reset_password(secret: str, email: str, password: str, response: Response) -> dict:
233
- """ Reset password with user's email
234
-
235
- Args:
236
- secret (str): a random md5code provided by administrator
237
- email (str): user's email
238
- password (str): user's password
239
-
240
- Returns:
241
- dict: keys includes "status", "msg" and "data"
242
- """
243
- email = email.lower().strip()
244
- if not utils.is_user_existed(email):
245
- response.status_code = status.HTTP_404_NOT_FOUND
246
- return {
247
- "status": status.FAILURE,
248
- "data": {},
249
- "msg": "user not found"
250
- }
251
- saved_secret = utils.get_secret(email)
252
- if saved_secret is None:
253
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
254
- return {
255
- "status": status.FAILURE,
256
- "data": {},
257
- "msg": "secret not existed"
258
- }
259
- if saved_secret != secret:
260
- response.status_code = status.HTTP_304_NOT_MODIFIED
261
- return {
262
- "status": status.FAILURE,
263
- "data": {},
264
- "msg": "incorrect secret or maybe your email is wrong",
265
- }
266
- if not utils.set_password(email, md5(password)):
267
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
268
- return {
269
- "status": status.FAILURE,
270
- "data": {},
271
- "msg": "failed to change password"
272
- }
273
- if not utils.remove_user_secret(email):
274
- response.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
275
- return {
276
- "status": status.FAILURE,
277
- "data": {},
278
- "msg": "failed to remove secret"
279
- }
280
- response.status_code = status.HTTP_200_OK
281
- return {
282
- "status": status.SUCCESS,
283
- "data": {},
284
- "msg": "Reset password successfully, please login"
285
  }
 
 
 
1
+ from fastapi import APIRouter
2
+ import pgsoft.pgfile as pgfile
 
 
3
  from pgsoft.pghash.md5 import md5
4
+ from pgsoft.pgdate.date_utils import beijing
5
+ from time import sleep
6
+ import json
7
+ import os
8
 
9
 
10
+ router = APIRouter(prefix="/file", tags=["File Service"])
 
 
 
11
  admin_token_md5 = "01112732d503cd3f0898d185a11c8b92"
12
+ dataset_id = "pgsoft/game"
13
+ tempdir = "game"
14
+ db_token = os.environ.get("db_token")
15
+ if db_token:
16
+ print(db_token[:5])
17
+
18
+
19
+ @router.get("/download")
20
+ def download_file(game: str, token: str, gamecode: str):
21
+ if md5(token) != admin_token_md5:
22
+ print(f"[{beijing()}][download file] failed")
23
+ return {"status": "Failure", "detail": "Invalid token"}
24
+ game = game.strip().lower()
25
+ filename = gamecode.strip() + ".json"
26
+ remotepath = "/".join([game, filename[:2], filename])
27
+ res = pgfile.download(
28
+ dataset_id,
29
+ remotepath=remotepath,
30
+ repo_type="dataset",
31
+ localdir=tempdir,
32
+ token=db_token,
33
+ )
34
+ if not res:
35
+ print(f"[{beijing()}][download file] failed")
36
+ return {"status": "Failure", "detail": "File not found or server error"}
37
+ with open(res, "r") as f:
38
+ outp = json.load(f)
39
+ print(f"[{beijing()}][download file] OK")
40
+ return {"status": "OK", "result": outp}
41
+
42
+
43
+ @router.post("/upload")
44
+ def upload_file(game: str, token: str, content: str):
45
+ if md5(token) != admin_token_md5:
46
+ print(f"[{beijing()}][upload file] failed")
47
+ return {"status": "Failure", "detail": "Invalid token"}
48
+ game = game.strip().lower()
49
+ try:
50
+ content_dict = json.loads(content)
51
+ except json.JSONDecodeError as e:
52
+ print(f"[{beijing()}][upload file] failed, {type(e)}: {e}")
53
+ return {"status": "Failure", "detail": "Invalid JSON"}
54
+
55
+ if not isinstance(content_dict, dict):
56
+ print(f"[{beijing()}][upload file] failed, not a dict")
57
+ return {"status": "Failure", "detail": "not a dict"}
58
+
59
+ needed_keys = ["game-file", "device-id"]
60
+ for key in needed_keys:
61
+ if key not in content_dict:
62
+ print(f'[{beijing()}][upload file] failed, missed "{key}"')
63
+ return {"status": "Failure", "detail": f'missed "{key}"'}
64
+
65
+ if not isinstance(content_dict["device-id"], str):
66
+ print(f'[{beijing()}][upload file] failed, "device-id" is not a str')
67
+ return {"status": "Failure", "detail": '"device-id" is not a str'}
68
+ if not isinstance(content_dict["game-file"], dict):
69
+ print(f'[{beijing()}][upload file] failed, "game-file" is not a dict')
70
+ return {"status": "Failure", "detail": '"game-file" is not a dict'}
71
+
72
+ obj = {
73
+ "upload-time": beijing().__str__(),
74
+ "game-file": content_dict["game-file"],
75
  }
76
+ maxtry = 5
77
+ for retry in range(maxtry):
78
+ md5code = md5(obj)
79
+ remotepath = "/".join([game, md5code[:2], md5code + ".json"])
80
+ if not pgfile.api.file_exists(
81
+ repo_id=dataset_id,
82
+ filename=remotepath,
83
+ repo_type="dataset",
84
+ token=db_token,
85
+ ):
86
+ break
87
+ sleep(0.1)
88
+ obj["upload-time"] = beijing().__str__()
89
+ maxtry -= 1
90
+ if not maxtry and pgfile.api.file_exists(
91
+ repo_id=dataset_id,
92
+ filename=remotepath,
93
+ repo_type="dataset",
94
+ token=db_token,
95
+ ):
96
+ print(f"[{beijing()}][upload file] failed, timeout, please retry")
97
+ return {"status": "Failure", "detail": "timeout, please retry"}
98
+ filedir = os.path.join(tempdir, game, md5code[:2])
99
+ if not os.path.exists(filedir):
100
+ os.makedirs(filedir)
101
+ filepath = os.path.join(filedir, md5code + ".json")
102
+ content_indented = json.dumps(content_dict, indent=4)
103
+ with open(filepath, "w") as f:
104
+ f.write(content_indented)
105
+ res = pgfile.upload(
106
+ filepath,
107
+ remotepath,
108
+ dataset_id,
109
+ "dataset",
110
+ db_token,
111
+ f"Updated at {beijing()}",
112
+ )
113
+ if not res:
114
+ print(f"[{beijing()}][upload file] failed")
115
+ return {"status": "Failure", "detail": "server error"}
116
+ print(f"[{beijing()}][upload file] OK")
117
+ return {"status": "OK", "result": md5code}
118
+
119
+
120
+ @router.get("/list")
121
+ def list_files(game: str, token: str):
122
+ if md5(token) != admin_token_md5:
123
+ print(f"[{beijing()}][list files] failed")
124
+ return {"status": "Failure", "detail": "Invalid token"}
125
+ game = game.strip().lower()
126
+ games = pgfile.list_files(
127
+ repo_id=dataset_id,
128
+ repo_type="dataset",
129
+ token=db_token,
130
+ )
131
+ if games is None:
132
+ print(f"[{beijing()}][list files] failed")
133
+ return {"status": "Failure", "detail": "server error"}
134
+ games = {
135
+ item.split(".")[0][-32:]: item
136
+ for item in games
137
+ if item.endswith(".json") and item.startswith(game)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
139
+ print(f"[{beijing()}][list files] OK")
140
+ return {"status": "OK", "result": games}
src/app/utils.py DELETED
@@ -1,280 +0,0 @@
1
- import huggingface_hub as hf
2
- import os
3
- from pgsoft import pgfile
4
- from pgsoft.pghash.md5 import md5
5
- from pgsoft.pgdate.date_utils import beijing
6
-
7
-
8
- hfapi = hf.HfApi()
9
- repo_id = "pgsoft/pguser"
10
- user_file_dir = "users"
11
- db_token = os.getenv("db_token")
12
-
13
-
14
- def file_exists(filename: str) -> bool:
15
- """ Check if a file exist in dataset pguser
16
-
17
- Args:
18
- filename (str): relative path of the file
19
-
20
- Returns:
21
- bool: True if exist, False otherwise
22
- """
23
- return hfapi.file_exists(repo_id=repo_id,
24
- filename=filename,
25
- repo_type="dataset",
26
- token=db_token)
27
-
28
-
29
- def upload_file(localpath: str, remotepath: str, commit_message: str = None) -> bool:
30
- """ upload a file to dataset pguser
31
-
32
- Args:
33
- localpath (str): the local path of the file
34
- remotepath (str): the remote path of the file
35
-
36
- Returns:
37
- bool: True if success, False if failed
38
- """
39
- return pgfile.upload(localpath=localpath,
40
- remotepath=remotepath,
41
- repo_id=repo_id,
42
- repo_type="dataset",
43
- token=db_token,
44
- commit_message=commit_message)
45
-
46
-
47
- def download_file(remotepath: str) -> str | None:
48
- """ download a file from dataset pguser
49
-
50
- Args:
51
- remotepath (str): the remote path of the file
52
-
53
- Returns:
54
- str | None: the local path of the file,
55
- return None if failed or file not exists
56
- """
57
- return pgfile.download(repo_id=repo_id,
58
- repo_type="dataset",
59
- remotepath=remotepath,
60
- token=db_token,
61
- localdir="./")
62
-
63
-
64
- def userdir(email: str) -> str:
65
- """ the path to store userdata
66
-
67
- Args:
68
- email (str): user's email
69
-
70
- Returns:
71
- str: the path to store userdata
72
- """
73
- subdir = md5(email)[:5]
74
- return f"{user_file_dir}/{subdir}/{email}"
75
-
76
-
77
- def is_user_existed(email: str) -> bool:
78
- """ Check if user exists in dataset according to if user's role file exists
79
-
80
- Args:
81
- email (str): user's email
82
-
83
- Returns:
84
- bool: True if exists, False if not
85
- """
86
- filepath = f"{userdir(email)}/role"
87
- return file_exists(filepath)
88
-
89
-
90
- def is_user_registered(email: str) -> bool:
91
- """ Check if a existed user registered according to if user's password file exists
92
-
93
- Args:
94
- email (str): user's email
95
-
96
- Returns:
97
- bool: True if exists, False if not
98
- """
99
- filepath = f"{userdir(email)}/password"
100
- return file_exists(filepath)
101
-
102
-
103
- def get_secret(email: str) -> str | None:
104
- """ Get secret of user
105
-
106
- Args:
107
- email (str): user's email
108
-
109
- Returns:
110
- str: a random md5code of user saved in dataset
111
- """
112
- filepath = f"{userdir(email)}/secret"
113
- localpath = download_file(filepath)
114
- if localpath is None:
115
- return None
116
- try:
117
- with open(localpath, "r") as f:
118
- secret = f.read()
119
- return secret
120
- except Exception as e:
121
- print(f"[get_secret]Failed to read secret, "
122
- + f"{type(e)}: {e}")
123
- return None
124
-
125
-
126
- def generate_user_secret() -> str:
127
- """ Generate a unique random md5code as user's secret
128
-
129
- Returns:
130
- str: a random md5code
131
- """
132
- secret = md5(beijing())
133
- return secret
134
-
135
-
136
- def remove_user_secret(email: str) -> bool:
137
- """ remove user's secret
138
-
139
- Args:
140
- email (str): user's email
141
-
142
- Returns:
143
- bool: True if succeed, False if failed
144
- """
145
- filepath = f"{userdir(email)}/secret"
146
- try:
147
- hfapi.delete_file(repo_id=repo_id,
148
- repo_type="dataset",
149
- path_in_repo=filepath,
150
- token=db_token,
151
- commit_message="remove secret")
152
- return True
153
- except Exception as e:
154
- print(f"[remove_user_secret]Failed to remove secret, "
155
- + f"{type(e)}: {e}")
156
- return False
157
-
158
-
159
- def create_account(email: str, role: str) -> str | None:
160
- """ Create a account of designated role, which could be ADMIN or USER
161
-
162
- Args:
163
- email (str): user's email
164
- role (str): the role of the account to be created
165
-
166
- Returns:
167
- str: the secret to be sent to user
168
- """
169
- prefix = userdir(email)
170
- if not os.path.exists(prefix):
171
- os.makedirs(prefix)
172
- role_path = f"{prefix}/role"
173
- secret_path = f"{prefix}/secret"
174
- secret = generate_user_secret()
175
- try:
176
- with open(role_path, "w") as f:
177
- f.write(role)
178
- with open(secret_path, "w") as f:
179
- f.write(secret)
180
- if not upload_file(role_path, role_path, "create account"):
181
- return None
182
- if not upload_file(secret_path, secret_path, "create account"):
183
- return None
184
- return secret
185
- except Exception as e:
186
- print(f"[create_account]{type(e)}: {e}")
187
- return None
188
-
189
-
190
- def set_password(email: str, password: str) -> bool:
191
- """ save user's password to user's directory
192
-
193
- Args:
194
- email (str): user's email
195
- password (str): password to save
196
-
197
- Returns:
198
- bool: True if succeed, False if failed
199
- """
200
- prefix = userdir(email)
201
- if not os.path.exists(prefix):
202
- os.makedirs(prefix)
203
- filepath = f"{prefix}/password"
204
- try:
205
- with open(filepath, "w") as f:
206
- f.write(password)
207
- return upload_file(filepath, filepath, "set password")
208
- except Exception as e:
209
- print(f"[set_password] {type(e)}: {e}")
210
- return False
211
-
212
-
213
- def get_password(email: str) -> str | None:
214
- """ Get password of user
215
-
216
- Args:
217
- email (str): user's email
218
-
219
- Returns:
220
- str: password hashed with MD5
221
- """
222
-
223
- filepath = f"{userdir(email)}/password"
224
- localpath = download_file(filepath)
225
- if localpath is None:
226
- return None
227
- try:
228
- with open(localpath, "r") as f:
229
- password = f.read()
230
- return password
231
- except Exception as e:
232
- print(f"[get_password]Failed to read password, "
233
- + f"{type(e)}: {e}")
234
- return None
235
-
236
-
237
- def delete_password(email: str) -> bool:
238
- """ delete user's password
239
-
240
- Args:
241
- email (str): user's email
242
-
243
- Returns:
244
- bool: True if succeed, False if failed
245
- """
246
- filepath = f"{userdir(email)}/password"
247
- try:
248
- hfapi.delete_file(repo_id=repo_id,
249
- repo_type="dataset",
250
- path_in_repo=filepath,
251
- token=db_token,
252
- commit_message="delete password")
253
- return True
254
- except Exception as e:
255
- print(f"[delete_password]Failed to delete password, "
256
- + f"{type(e)}: {e}")
257
- return False
258
-
259
-
260
- def generate_secret(email: str) -> str | None:
261
- """ Create a user secret
262
- Args:
263
- email (str): user's email
264
- Returns:
265
- str: the secret to be sent to user
266
- """
267
- prefix = userdir(email)
268
- if not os.path.exists(prefix):
269
- os.makedirs(prefix)
270
- secret_path = f"{prefix}/secret"
271
- secret = generate_user_secret()
272
- try:
273
- with open(secret_path, "w") as f:
274
- f.write(secret)
275
- if not upload_file(secret_path, secret_path, "create account"):
276
- return None
277
- return secret
278
- except Exception as e:
279
- print(f"[create_account]{type(e)}: {e}")
280
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/status.py DELETED
@@ -1,10 +0,0 @@
1
- from starlette.status import *
2
-
3
-
4
- SUCCESS = "OK"
5
- FAILURE = "Failure"
6
-
7
- # Roles
8
- ADMIN = "admin"
9
- USER = "user"
10
- GUEST = "guest"