Nexom 1.0.4__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nexom/app/__init__.py +1 -1
- nexom/app/auth.py +184 -71
- nexom/app/db.py +34 -6
- nexom/app/path.py +1 -1
- nexom/app/response.py +12 -3
- nexom/app/template.py +21 -17
- nexom/assets/app/__pycache__/__init__.cpython-313.pyc +0 -0
- nexom/assets/app/pages/__pycache__/__init__.cpython-313.pyc +0 -0
- nexom/assets/app/pages/_templates.py +2 -2
- nexom/assets/app/router.py +2 -2
- nexom/assets/app/static/github.png +0 -0
- nexom/assets/app/static/style.css +626 -29
- nexom/assets/app/templates/default.html +7 -3
- nexom/assets/app/templates/document.html +122 -166
- nexom/assets/app/templates/footer.html +3 -3
- nexom/assets/app/templates/header.html +9 -3
- nexom/assets/auth/__pycache__/__init__.cpython-313.pyc +0 -0
- nexom/assets/auth_page/login.html +180 -40
- nexom/assets/auth_page/signup.html +259 -44
- nexom/buildTools/build.py +1 -1
- nexom/core/error.py +125 -32
- nexom/templates/auth.py +89 -23
- {nexom-1.0.4.dist-info → nexom-1.0.5.dist-info}/METADATA +2 -2
- {nexom-1.0.4.dist-info → nexom-1.0.5.dist-info}/RECORD +28 -27
- {nexom-1.0.4.dist-info → nexom-1.0.5.dist-info}/WHEEL +0 -0
- {nexom-1.0.4.dist-info → nexom-1.0.5.dist-info}/entry_points.txt +0 -0
- {nexom-1.0.4.dist-info → nexom-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {nexom-1.0.4.dist-info → nexom-1.0.5.dist-info}/top_level.txt +0 -0
|
@@ -5,64 +5,260 @@
|
|
|
5
5
|
<meta charset="utf-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
7
7
|
<title>Nexom Signup</title>
|
|
8
|
+
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Noto+Sans+JP:wght@400;600;700&display=swap" rel="stylesheet">
|
|
12
|
+
|
|
8
13
|
<style>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
:root{
|
|
15
|
+
--bg:#0b0d12;
|
|
16
|
+
--card:#0f141c;
|
|
17
|
+
--line:rgba(255,255,255,.10);
|
|
18
|
+
--text:rgba(255,255,255,.92);
|
|
19
|
+
--muted:rgba(255,255,255,.62);
|
|
20
|
+
--danger:#ff5a6a;
|
|
21
|
+
--ok:#27d39b;
|
|
22
|
+
--btn:#ffffff;
|
|
23
|
+
--btnText:#0b0d12;
|
|
24
|
+
--shadow:0 20px 60px rgba(0,0,0,.45);
|
|
25
|
+
}
|
|
26
|
+
*{ box-sizing:border-box; }
|
|
27
|
+
html,body{ height:100%; }
|
|
28
|
+
body{
|
|
29
|
+
margin:0;
|
|
30
|
+
background: radial-gradient(1200px 600px at 20% 10%, rgba(106,140,255,.18), transparent 60%),
|
|
31
|
+
radial-gradient(900px 500px at 85% 30%, rgba(39,211,155,.12), transparent 55%),
|
|
32
|
+
var(--bg);
|
|
33
|
+
color:var(--text);
|
|
34
|
+
font-family: Inter, "Noto Sans JP", "Hiragino Sans", "ヒラギノ角ゴシック", "Yu Gothic", "游ゴシック", system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
35
|
+
overflow:hidden; /* スクロール禁止 */
|
|
36
|
+
}
|
|
37
|
+
.wrap{
|
|
38
|
+
height:100%;
|
|
39
|
+
display:grid;
|
|
40
|
+
place-items:center;
|
|
41
|
+
padding:24px;
|
|
42
|
+
}
|
|
43
|
+
.card{
|
|
44
|
+
width:min(560px, 100%);
|
|
45
|
+
background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03));
|
|
46
|
+
border:1px solid var(--line);
|
|
47
|
+
border-radius:18px;
|
|
48
|
+
box-shadow: var(--shadow);
|
|
49
|
+
padding:18px;
|
|
50
|
+
backdrop-filter: blur(10px);
|
|
51
|
+
}
|
|
52
|
+
h1{
|
|
53
|
+
margin:0 0 10px;
|
|
54
|
+
font-size:18px;
|
|
55
|
+
letter-spacing:.2px;
|
|
56
|
+
}
|
|
57
|
+
.sub{
|
|
58
|
+
margin:0 0 14px;
|
|
59
|
+
color:var(--muted);
|
|
60
|
+
font-size:12px;
|
|
61
|
+
line-height:1.5;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.grid{
|
|
65
|
+
display:grid;
|
|
66
|
+
grid-template-columns: 1fr 1fr;
|
|
67
|
+
gap:10px;
|
|
68
|
+
}
|
|
69
|
+
.grid .full{ grid-column:1 / -1; }
|
|
70
|
+
@media (max-width: 480px){
|
|
71
|
+
.grid .hurf{ grid-column:1 / -1; }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
label{
|
|
75
|
+
display:block;
|
|
76
|
+
font-size:11px;
|
|
77
|
+
color:var(--muted);
|
|
78
|
+
margin:0 0 6px;
|
|
79
|
+
}
|
|
80
|
+
input{
|
|
81
|
+
width:100%;
|
|
82
|
+
height:40px;
|
|
83
|
+
padding:0 12px;
|
|
84
|
+
border-radius:12px;
|
|
85
|
+
border:1px solid var(--line);
|
|
86
|
+
background: rgba(0,0,0,.25);
|
|
87
|
+
color:var(--text);
|
|
88
|
+
outline:none;
|
|
89
|
+
font-size:13px;
|
|
90
|
+
}
|
|
91
|
+
input:focus{
|
|
92
|
+
border-color: rgba(255,255,255,.18);
|
|
93
|
+
box-shadow: 0 0 0 3px rgba(106,140,255,.12);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.row{
|
|
97
|
+
display:flex;
|
|
98
|
+
gap:10px;
|
|
99
|
+
align-items:center;
|
|
100
|
+
justify-content:space-between;
|
|
101
|
+
margin-top:12px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
button{
|
|
105
|
+
height:40px;
|
|
106
|
+
padding:0 14px;
|
|
107
|
+
border-radius:12px;
|
|
108
|
+
border:1px solid rgba(255,255,255,.08);
|
|
109
|
+
background: var(--btn);
|
|
110
|
+
color: var(--btnText);
|
|
111
|
+
font-weight:700;
|
|
112
|
+
cursor:pointer;
|
|
113
|
+
white-space:nowrap;
|
|
114
|
+
}
|
|
115
|
+
button:disabled{
|
|
116
|
+
opacity:.55;
|
|
117
|
+
cursor:not-allowed;
|
|
118
|
+
}
|
|
119
|
+
.link{
|
|
120
|
+
color:var(--muted);
|
|
121
|
+
font-size:12px;
|
|
122
|
+
text-decoration:none;
|
|
123
|
+
border-bottom:1px dotted rgba(255,255,255,.25);
|
|
124
|
+
}
|
|
125
|
+
.link:hover{ color:var(--text); }
|
|
126
|
+
|
|
127
|
+
.msg{
|
|
128
|
+
display:none;
|
|
129
|
+
margin-top:10px;
|
|
130
|
+
padding:10px 12px;
|
|
131
|
+
border-radius:12px;
|
|
132
|
+
border:1px solid var(--line);
|
|
133
|
+
font-size:12px;
|
|
134
|
+
line-height:1.5;
|
|
135
|
+
white-space:pre-wrap;
|
|
136
|
+
background: rgba(0,0,0,.22);
|
|
137
|
+
}
|
|
138
|
+
.msg.is-error{ display:block; border-color: rgba(255,90,106,.35); }
|
|
139
|
+
.msg.is-ok{ display:block; border-color: rgba(39,211,155,.35); }
|
|
140
|
+
|
|
141
|
+
.okPanel{
|
|
142
|
+
display:none;
|
|
143
|
+
margin-top:10px;
|
|
144
|
+
padding:12px;
|
|
145
|
+
border-radius:14px;
|
|
146
|
+
border:1px solid rgba(39,211,155,.35);
|
|
147
|
+
background: rgba(39,211,155,.10);
|
|
148
|
+
}
|
|
149
|
+
.okPanel.show{ display:block; }
|
|
150
|
+
.okTitle{
|
|
151
|
+
font-weight:700;
|
|
152
|
+
font-size:13px;
|
|
153
|
+
margin:0 0 6px;
|
|
154
|
+
}
|
|
155
|
+
.okText{
|
|
156
|
+
margin:0 0 10px;
|
|
157
|
+
font-size:12px;
|
|
158
|
+
color:var(--muted);
|
|
159
|
+
line-height:1.5;
|
|
160
|
+
}
|
|
161
|
+
.okBtn{
|
|
162
|
+
width:100%;
|
|
163
|
+
height:40px;
|
|
164
|
+
border-radius:12px;
|
|
165
|
+
border:1px solid rgba(255,255,255,.08);
|
|
166
|
+
background: rgba(255,255,255,.92);
|
|
167
|
+
color:#0b0d12;
|
|
168
|
+
font-weight:800;
|
|
169
|
+
cursor:pointer;
|
|
170
|
+
}
|
|
22
171
|
</style>
|
|
23
172
|
</head>
|
|
173
|
+
|
|
24
174
|
<body>
|
|
25
175
|
<div class="wrap">
|
|
26
|
-
<h1 style="margin:0 0 12px;font-size:20px;">Signup</h1>
|
|
27
|
-
|
|
28
176
|
<section class="card">
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
<input id="user_id" name="user_id" autocomplete="username" required />
|
|
177
|
+
<h1>Signup</h1>
|
|
178
|
+
<p class="sub">アカウントを作成</p>
|
|
32
179
|
|
|
33
|
-
|
|
34
|
-
<
|
|
180
|
+
<form id="form">
|
|
181
|
+
<div class="grid">
|
|
182
|
+
<div class="full">
|
|
183
|
+
<label for="user_id">ユーザーID</label>
|
|
184
|
+
<input id="user_id" name="user_id" autocomplete="username" required />
|
|
185
|
+
</div>
|
|
35
186
|
|
|
36
|
-
|
|
37
|
-
|
|
187
|
+
<div class="full">
|
|
188
|
+
<label for="public_name">公開名</label>
|
|
189
|
+
<input id="public_name" name="public_name" autocomplete="nickname" required />
|
|
190
|
+
</div>
|
|
38
191
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
192
|
+
<div class="hurf">
|
|
193
|
+
<label for="password">パスワード</label>
|
|
194
|
+
<input id="password" name="password" type="password" autocomplete="new-password" required />
|
|
195
|
+
</div>
|
|
196
|
+
<div class="hurf">
|
|
197
|
+
<label for="password2">パスワード (確認)</label>
|
|
198
|
+
<input id="password2" name="password2" type="password" autocomplete="new-password" required />
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
43
201
|
|
|
44
202
|
<div class="row">
|
|
45
|
-
<
|
|
46
|
-
<
|
|
203
|
+
<a class="link" href="./login">アカウントがある場合</a>
|
|
204
|
+
<button id="btn" type="submit">作成</button>
|
|
47
205
|
</div>
|
|
48
|
-
</form>
|
|
49
206
|
|
|
50
|
-
|
|
207
|
+
<div id="msg" class="msg"></div>
|
|
208
|
+
|
|
209
|
+
<div id="okPanel" class="okPanel">
|
|
210
|
+
<div class="okTitle">アカウントが作成されました</div>
|
|
211
|
+
<p class="okText">ログインしてください</p>
|
|
212
|
+
<button id="toLogin" class="okBtn" type="button">ログイン画面へ</button>
|
|
213
|
+
</div>
|
|
214
|
+
</form>
|
|
51
215
|
</section>
|
|
52
216
|
</div>
|
|
53
217
|
|
|
54
218
|
<script>
|
|
55
|
-
const
|
|
219
|
+
const AUTH_BASE = ""
|
|
220
|
+
const PAGE_PATH = "/{{ page_path }}"
|
|
56
221
|
|
|
57
|
-
|
|
58
|
-
|
|
222
|
+
const error_code_for_message = {
|
|
223
|
+
// ===== Auth errors =====
|
|
224
|
+
"A01": "フォームの入力内容に不足があります。",
|
|
225
|
+
"A02": "このユーザーIDはすでに使用されています。",
|
|
226
|
+
"A03": "ユーザーIDまたはパスワードが正しくありません。",
|
|
227
|
+
"A04": "このユーザーは無効化されています。",
|
|
228
|
+
"A05": "認証情報が見つかりません。再度ログインしてください。",
|
|
229
|
+
"A06": "認証情報が不正です。再度ログインしてください。",
|
|
230
|
+
"A07": "セッションの有効期限が切れています。再度ログインしてください。",
|
|
231
|
+
"A08": "このセッションは無効化されています。再度ログインしてください。",
|
|
232
|
+
"A09": "認証サービスに接続できません。しばらく時間をおいて再度お試しください。",
|
|
233
|
+
|
|
234
|
+
// fallback
|
|
235
|
+
"DEFAULT": "エラーが発生しました。もう一度お試しください。"
|
|
236
|
+
};
|
|
237
|
+
function getErrorMessage(code) {
|
|
238
|
+
if (typeof code !== "string") {
|
|
239
|
+
return error_code_for_message.DEFAULT;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return error_code_for_message[code] ?? error_code_for_message.DEFAULT;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const form = document.getElementById("form");
|
|
246
|
+
const btn = document.getElementById("btn");
|
|
247
|
+
const msg = document.getElementById("msg");
|
|
248
|
+
const okPanel = document.getElementById("okPanel");
|
|
249
|
+
const toLogin = document.getElementById("toLogin");
|
|
250
|
+
|
|
251
|
+
function clearMsg(){
|
|
252
|
+
msg.className = "msg";
|
|
253
|
+
msg.textContent = "";
|
|
59
254
|
}
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
255
|
+
function setError(text){
|
|
256
|
+
msg.className = "msg is-error";
|
|
257
|
+
msg.textContent = getErrorMessage(text);
|
|
63
258
|
}
|
|
64
259
|
|
|
65
|
-
async function postJSON(
|
|
260
|
+
async function postJSON(path, bodyObj){
|
|
261
|
+
const url = (AUTH_BASE || "") + path;
|
|
66
262
|
const res = await fetch(url, {
|
|
67
263
|
method: "POST",
|
|
68
264
|
headers: {
|
|
@@ -75,12 +271,13 @@
|
|
|
75
271
|
const text = await res.text();
|
|
76
272
|
let data = {};
|
|
77
273
|
try { data = text ? JSON.parse(text) : {}; } catch { data = { raw: text }; }
|
|
78
|
-
|
|
79
274
|
return { ok: res.ok, status: res.status, data };
|
|
80
275
|
}
|
|
81
276
|
|
|
82
|
-
|
|
277
|
+
form.addEventListener("submit", async (e) => {
|
|
83
278
|
e.preventDefault();
|
|
279
|
+
clearMsg();
|
|
280
|
+
okPanel.classList.remove("show");
|
|
84
281
|
|
|
85
282
|
const user_id = document.getElementById("user_id").value.trim();
|
|
86
283
|
const public_name = document.getElementById("public_name").value.trim();
|
|
@@ -88,18 +285,36 @@
|
|
|
88
285
|
const password2 = document.getElementById("password2").value;
|
|
89
286
|
|
|
90
287
|
if (!user_id || !public_name || !password) {
|
|
91
|
-
|
|
288
|
+
setError("T: missing fields");
|
|
92
289
|
return;
|
|
93
290
|
}
|
|
94
291
|
if (password !== password2) {
|
|
95
|
-
|
|
292
|
+
setError("T: password mismatch");
|
|
96
293
|
return;
|
|
97
294
|
}
|
|
98
295
|
|
|
99
|
-
|
|
100
|
-
|
|
296
|
+
btn.disabled = true;
|
|
297
|
+
try{
|
|
298
|
+
const r = await postJSON(PAGE_PATH, { user_id, public_name, password });
|
|
299
|
+
|
|
300
|
+
if (!r.ok || !r.data || r.data.ok !== true) {
|
|
301
|
+
|
|
302
|
+
const code = (r.data && r.data.error) ? String(r.data.error) : "UnknownError";
|
|
303
|
+
setError(code);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
okPanel.classList.add("show");
|
|
308
|
+
}catch(e){
|
|
309
|
+
console.error(e)
|
|
310
|
+
setError("NetworkError");
|
|
311
|
+
}finally{
|
|
312
|
+
btn.disabled = false;
|
|
313
|
+
}
|
|
314
|
+
});
|
|
101
315
|
|
|
102
|
-
|
|
316
|
+
toLogin.addEventListener("click", () => {
|
|
317
|
+
location.href = "./login";
|
|
103
318
|
});
|
|
104
319
|
</script>
|
|
105
320
|
</body>
|
nexom/buildTools/build.py
CHANGED
nexom/core/error.py
CHANGED
|
@@ -160,68 +160,161 @@ class ObjectHTMLTypeError(NexomError):
|
|
|
160
160
|
# =========================
|
|
161
161
|
# DatabaseManager
|
|
162
162
|
# =========================
|
|
163
|
-
class
|
|
164
|
-
"""
|
|
163
|
+
class DBError(NexomError):
|
|
164
|
+
"""
|
|
165
|
+
Base class for database-related errors.
|
|
166
|
+
|
|
167
|
+
This error represents a generic database failure that does not fall into
|
|
168
|
+
a more specific category such as connection, integrity, operational, or
|
|
169
|
+
programming errors.
|
|
170
|
+
|
|
171
|
+
It is typically used as a catch-all or wrapper error when the underlying
|
|
172
|
+
database exception cannot be safely or clearly classified.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
class DBMConnectionInvalidError(DBError):
|
|
176
|
+
"""
|
|
177
|
+
Raised when the database manager connection is invalid or not initialized.
|
|
165
178
|
|
|
166
|
-
|
|
179
|
+
This error indicates that the database manager (DBM) is in an unusable state,
|
|
180
|
+
such as:
|
|
181
|
+
- the database connection has not been established yet
|
|
182
|
+
- the connection was already closed
|
|
183
|
+
- the DBM was accessed before proper initialization
|
|
184
|
+
|
|
185
|
+
This is typically a lifecycle or configuration error.
|
|
186
|
+
"""
|
|
187
|
+
def __init__(self, message: str = "Not started") -> None:
|
|
167
188
|
super().__init__(
|
|
168
189
|
"DBM01",
|
|
169
190
|
f"DBM connection is invalid. -> {message}",
|
|
170
191
|
)
|
|
171
192
|
|
|
172
|
-
class DBError(NexomError):
|
|
173
|
-
"""Raised when an udbm connection is invalid."""
|
|
174
193
|
|
|
175
|
-
|
|
194
|
+
class DBOperationalError(DBError):
|
|
195
|
+
"""
|
|
196
|
+
Raised when a database operational error occurs.
|
|
197
|
+
|
|
198
|
+
This error represents failures related to the database runtime environment,
|
|
199
|
+
such as:
|
|
200
|
+
- inability to open or connect to the database file
|
|
201
|
+
- database being locked
|
|
202
|
+
- I/O errors during a query
|
|
203
|
+
- transaction failures caused by the database state
|
|
204
|
+
|
|
205
|
+
Typically maps to sqlite3.OperationalError.
|
|
206
|
+
"""
|
|
207
|
+
def __init__(self, message: str) -> None:
|
|
176
208
|
super().__init__(
|
|
177
209
|
"DBM02",
|
|
178
|
-
f"
|
|
210
|
+
f"Database operational error. -> {message}",
|
|
179
211
|
)
|
|
180
212
|
|
|
213
|
+
|
|
214
|
+
class DBIntegrityError(DBError):
|
|
215
|
+
"""
|
|
216
|
+
Raised when a database integrity constraint is violated.
|
|
217
|
+
|
|
218
|
+
This error indicates that a database constraint has been broken, such as:
|
|
219
|
+
- UNIQUE constraint violations
|
|
220
|
+
- FOREIGN KEY constraint failures
|
|
221
|
+
- NOT NULL constraint violations
|
|
222
|
+
- CHECK constraint failures
|
|
223
|
+
|
|
224
|
+
Typically maps to sqlite3.IntegrityError.
|
|
225
|
+
"""
|
|
226
|
+
def __init__(self, message: str) -> None:
|
|
227
|
+
super().__init__(
|
|
228
|
+
"DBM03",
|
|
229
|
+
f"Database integrity constraint violated. -> {message}",
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class DBProgrammingError(DBError):
|
|
234
|
+
"""
|
|
235
|
+
Raised when a database programming or SQL syntax error occurs.
|
|
236
|
+
|
|
237
|
+
This error indicates a bug in application code or query construction, such as:
|
|
238
|
+
- malformed SQL statements
|
|
239
|
+
- referencing non-existent tables or columns
|
|
240
|
+
- incorrect parameter binding
|
|
241
|
+
- misuse of the database API
|
|
242
|
+
|
|
243
|
+
Typically maps to sqlite3.ProgrammingError.
|
|
244
|
+
"""
|
|
245
|
+
def __init__(self, message: str) -> None:
|
|
246
|
+
super().__init__(
|
|
247
|
+
"DBM04",
|
|
248
|
+
f"Database programming error. -> {message}",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
181
252
|
# =========================
|
|
182
253
|
# Auth
|
|
183
254
|
# =========================
|
|
184
255
|
|
|
185
256
|
class AuthMissingFieldError(NexomError):
|
|
186
|
-
"""
|
|
257
|
+
"""Required auth fields are missing."""
|
|
187
258
|
def __init__(self, key: str) -> None:
|
|
188
|
-
super().__init__(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
259
|
+
super().__init__("A01", f"Missing field. '{key}'")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class AuthUserIdAlreadyExistsError(NexomError):
|
|
263
|
+
"""user_id already exists (signup conflict)."""
|
|
264
|
+
def __init__(self) -> None:
|
|
265
|
+
super().__init__("A02", "This user_id is already in use.")
|
|
192
266
|
|
|
193
267
|
|
|
194
268
|
class AuthInvalidCredentialsError(NexomError):
|
|
195
|
-
"""
|
|
269
|
+
"""user_id or password is invalid (login)."""
|
|
196
270
|
def __init__(self) -> None:
|
|
197
|
-
super().__init__(
|
|
198
|
-
"A02",
|
|
199
|
-
"Invalid credentials."
|
|
200
|
-
)
|
|
271
|
+
super().__init__("A03", "Invalid credentials.")
|
|
201
272
|
|
|
202
273
|
|
|
203
274
|
class AuthUserDisabledError(NexomError):
|
|
204
|
-
"""
|
|
275
|
+
"""User is inactive/disabled."""
|
|
205
276
|
def __init__(self) -> None:
|
|
206
|
-
super().__init__(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
277
|
+
super().__init__("A04", "This user is disabled.")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class AuthTokenMissingError(NexomError):
|
|
281
|
+
"""Token is missing."""
|
|
282
|
+
def __init__(self) -> None:
|
|
283
|
+
super().__init__("A05", "Token is missing.")
|
|
210
284
|
|
|
211
285
|
|
|
212
286
|
class AuthTokenInvalidError(NexomError):
|
|
213
|
-
"""
|
|
287
|
+
"""Token is invalid (malformed / not found)."""
|
|
214
288
|
def __init__(self) -> None:
|
|
215
|
-
super().__init__(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
289
|
+
super().__init__("A06", "This token is invalid.")
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class AuthTokenExpiredError(NexomError):
|
|
293
|
+
"""Token is expired."""
|
|
294
|
+
def __init__(self) -> None:
|
|
295
|
+
super().__init__("A07", "This token has expired.")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class AuthTokenRevokedError(NexomError):
|
|
299
|
+
"""Token is revoked (logout etc)."""
|
|
300
|
+
def __init__(self) -> None:
|
|
301
|
+
super().__init__("A08", "This token is revoked.")
|
|
219
302
|
|
|
220
303
|
|
|
221
304
|
class AuthServiceUnavailableError(NexomError):
|
|
222
|
-
"""
|
|
305
|
+
"""AuthService is unreachable / timed out / invalid response."""
|
|
223
306
|
def __init__(self) -> None:
|
|
224
|
-
super().__init__(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
307
|
+
super().__init__("A09", "Authentication service is currently unavailable.")
|
|
308
|
+
|
|
309
|
+
def _status_for_auth_error(code: str) -> int:
|
|
310
|
+
return {
|
|
311
|
+
"A01": 400, # missing field
|
|
312
|
+
"A02": 409, # user_id already exists
|
|
313
|
+
"A03": 401, # invalid credentials
|
|
314
|
+
"A04": 403, # user disabled
|
|
315
|
+
"A05": 401, # token missing
|
|
316
|
+
"A06": 401, # token invalid
|
|
317
|
+
"A07": 401, # token expired
|
|
318
|
+
"A08": 401, # token revoked
|
|
319
|
+
"A09": 503, # auth service unavailable
|
|
320
|
+
}.get(code, 400)
|