Nexom 0.1.3__py3-none-any.whl → 1.0.1__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.
Files changed (67) hide show
  1. nexom/__init__.py +2 -2
  2. nexom/__main__.py +111 -17
  3. nexom/app/__init__.py +62 -0
  4. nexom/app/auth.py +322 -0
  5. nexom/{web → app}/cookie.py +4 -2
  6. nexom/app/db.py +88 -0
  7. nexom/app/path.py +195 -0
  8. nexom/app/request.py +267 -0
  9. nexom/{web → app}/response.py +13 -3
  10. nexom/{web → app}/template.py +1 -1
  11. nexom/app/user.py +31 -0
  12. nexom/assets/app/__init__.py +0 -0
  13. nexom/assets/app/__pycache__/__init__.cpython-313.pyc +0 -0
  14. nexom/assets/app/config.py +28 -0
  15. nexom/assets/app/gunicorn.conf.py +5 -0
  16. nexom/assets/app/pages/__pycache__/__init__.cpython-313.pyc +0 -0
  17. nexom/assets/app/pages/_templates.py +7 -0
  18. nexom/assets/{server → app}/pages/default.py +2 -2
  19. nexom/assets/{server → app}/pages/document.py +2 -2
  20. nexom/assets/app/router.py +12 -0
  21. nexom/assets/app/wsgi.py +64 -0
  22. nexom/assets/auth/__init__.py +0 -0
  23. nexom/assets/auth/__pycache__/__init__.cpython-313.pyc +0 -0
  24. nexom/assets/auth/config.py +27 -0
  25. nexom/assets/auth/gunicorn.conf.py +5 -0
  26. nexom/assets/auth/wsgi.py +62 -0
  27. nexom/assets/auth_page/login.html +95 -0
  28. nexom/assets/auth_page/signup.html +106 -0
  29. nexom/assets/error_page/error.html +3 -3
  30. nexom/assets/gateway/apache_app.conf +16 -0
  31. nexom/assets/gateway/nginx_app.conf +21 -0
  32. nexom/buildTools/__init__.py +1 -0
  33. nexom/buildTools/build.py +274 -54
  34. nexom/buildTools/run.py +185 -0
  35. nexom/core/__init__.py +2 -1
  36. nexom/core/error.py +81 -3
  37. nexom/core/log.py +111 -0
  38. nexom/{engine → core}/object_html_render.py +4 -1
  39. nexom/templates/__init__.py +0 -0
  40. nexom/templates/auth.py +72 -0
  41. {nexom-0.1.3.dist-info → nexom-1.0.1.dist-info}/METADATA +75 -50
  42. nexom-1.0.1.dist-info/RECORD +56 -0
  43. {nexom-0.1.3.dist-info → nexom-1.0.1.dist-info}/WHEEL +1 -1
  44. nexom/assets/server/config.py +0 -27
  45. nexom/assets/server/gunicorn.conf.py +0 -16
  46. nexom/assets/server/pages/__pycache__/__init__.cpython-313.pyc +0 -0
  47. nexom/assets/server/pages/_templates.py +0 -11
  48. nexom/assets/server/router.py +0 -18
  49. nexom/assets/server/wsgi.py +0 -30
  50. nexom/engine/__init__.py +0 -1
  51. nexom/web/__init__.py +0 -5
  52. nexom/web/path.py +0 -125
  53. nexom/web/request.py +0 -62
  54. nexom-0.1.3.dist-info/RECORD +0 -39
  55. /nexom/{web → app}/http_status_codes.py +0 -0
  56. /nexom/{web → app}/middleware.py +0 -0
  57. /nexom/assets/{server → app}/pages/__init__.py +0 -0
  58. /nexom/assets/{server → app}/static/dog.jpeg +0 -0
  59. /nexom/assets/{server → app}/static/style.css +0 -0
  60. /nexom/assets/{server → app}/templates/base.html +0 -0
  61. /nexom/assets/{server → app}/templates/default.html +0 -0
  62. /nexom/assets/{server → app}/templates/document.html +0 -0
  63. /nexom/assets/{server → app}/templates/footer.html +0 -0
  64. /nexom/assets/{server → app}/templates/header.html +0 -0
  65. {nexom-0.1.3.dist-info → nexom-1.0.1.dist-info}/entry_points.txt +0 -0
  66. {nexom-0.1.3.dist-info → nexom-1.0.1.dist-info}/licenses/LICENSE +0 -0
  67. {nexom-0.1.3.dist-info → nexom-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,27 @@
1
+ """
2
+ Nexom auth server settings.
3
+
4
+ This file is generated by Nexom buildTools.
5
+ """
6
+ # ======== project ========
7
+ PROJECT_DIR: str = "__prj_dir__"
8
+ APP_DIR: str = "__app_dir__"
9
+ TEMPLATES_DIR: str = "__app_dir__/templates"
10
+
11
+ DATA_DIR: str = "__prj_dir__/data"
12
+
13
+ # ======== gunicorn ========
14
+ ADDRESS: str = "__g_address__"
15
+ PORT: int = __g_port__
16
+ WORKERS: int = __g_workers__
17
+ RELOAD: bool = __g_reload__
18
+
19
+ # ======== auth service ========
20
+ AUTH_DB: str = DATA_DIR + "/db/auth.db"
21
+
22
+ # ======== logger ========
23
+ INFO_LOG: str = "__prj_dir__/data/log/__app_name__/info.log"
24
+ WARN_LOG: str = "__prj_dir__/data/log/__app_name__/warning.log"
25
+ ERR_LOG: str = "__prj_dir__/data/log/__app_name__/error.log"
26
+ ACES_LOG: str = "__prj_dir__/data/log/__app_name__/access.log"
27
+ AUTH_LOG: str = "__prj_dir__/data/log/__app_name__/auth.log"
@@ -0,0 +1,5 @@
1
+ from auth.config import ADDRESS, PORT, WORKERS, RELOAD # noqa: E402
2
+
3
+ bind = f"{ADDRESS}:{PORT}"
4
+ workers = int(WORKERS)
5
+ reload = bool(RELOAD)
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable, Iterable
4
+ import time
5
+
6
+ from nexom.app.request import Request
7
+ from nexom.app.response import JsonResponse
8
+ from nexom.core.error import NexomError
9
+ from nexom.core.log import AppLogger, AuthLogger
10
+ from nexom.app.auth import AuthService
11
+
12
+ from auth.config import AUTH_DB, INFO_LOG, WARN_LOG, ERR_LOG, ACES_LOG, AUTH_LOG
13
+
14
+
15
+ # Logger
16
+ logger = AppLogger(
17
+ info=INFO_LOG,
18
+ warn=WARN_LOG,
19
+ error=ERR_LOG,
20
+ access=ACES_LOG,
21
+ )
22
+
23
+ # AuthService
24
+ service = AuthService(AUTH_DB, AUTH_LOG)
25
+
26
+
27
+ def _ip(environ: dict) -> str:
28
+ xff = environ.get("HTTP_X_FORWARDED_FOR")
29
+ if isinstance(xff, str) and xff.strip():
30
+ return xff.split(",")[0].strip()
31
+ return str(environ.get("REMOTE_ADDR") or "-")
32
+
33
+
34
+ def app(environ: dict, start_response: Callable) -> Iterable[bytes]:
35
+ """
36
+ WSGI application entrypoint.
37
+ """
38
+ try:
39
+ t0 = time.time()
40
+
41
+ res = service.handler(environ)
42
+
43
+ except NexomError as e:
44
+ logger.error(e)
45
+ return JsonResponse({"error": e.code})
46
+ except Exception:
47
+ logger.error(e)
48
+ return JsonResponse({"error": "Internal Server Error"})
49
+
50
+ try:
51
+ req = Request(environ)
52
+ dt_ms = int((time.time() - t0) * 1000)
53
+ method = req.method if req else str(environ.get("REQUEST_METHOD") or "-")
54
+ path = (req.path if req else str(environ.get("PATH_INFO") or "")).lstrip("/")
55
+ ip = _ip(environ)
56
+ ua = str(environ.get("HTTP_USER_AGENT") or "-")
57
+ logger.access(f'{ip} "{method} /{path}" {res.status_code} {dt_ms}ms "{ua}"')
58
+ except Exception:
59
+ pass
60
+
61
+ start_response(res.status_text, res.headers)
62
+ return [res.body]
@@ -0,0 +1,95 @@
1
+ <!-- assets/auth_page/login.html -->
2
+ <!doctype html>
3
+ <html lang="ja">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
7
+ <title>Nexom Login</title>
8
+ <style>
9
+ body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 24px; }
10
+ .wrap { max-width: 720px; margin: 0 auto; }
11
+ .card { border: 1px solid #eee; border-radius: 14px; padding: 16px; background: #fff; }
12
+ label { display:block; font-size:12px; margin: 12px 0 6px; color:#333; }
13
+ input { width:100%; padding:10px 12px; border:1px solid #ddd; border-radius:10px; font-size:14px; }
14
+ button { margin-top: 14px; padding:10px 12px; border:1px solid #333; border-radius:10px; background:#333; color:#fff; cursor:pointer; }
15
+ .row { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top: 10px; }
16
+ a { color:#333; }
17
+ .help { color:#666; font-size:12px; }
18
+ .log { margin-top: 14px; padding: 12px; border-radius: 12px; background:#f7f7f7; border:1px solid #eee; white-space: pre-wrap;
19
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
20
+ .ok { color:#0a7; }
21
+ .ng { color:#c33; }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <div class="wrap">
26
+ <h1 style="margin:0 0 12px;font-size:20px;">Login</h1>
27
+
28
+ <section class="card">
29
+ <form id="formLogin">
30
+ <label for="user_id">user_id</label>
31
+ <input id="user_id" name="user_id" autocomplete="username" required />
32
+
33
+ <label for="password">password</label>
34
+ <input id="password" name="password" type="password" autocomplete="current-password" required />
35
+
36
+ <button type="submit">login</button>
37
+
38
+ <div class="row">
39
+ <span class="help">送信先はこのアプリの <code>/login/</code>(アプリ → AuthClient → AuthService)</span>
40
+ <a class="help" href="/signup/">signupへ</a>
41
+ </div>
42
+ </form>
43
+
44
+ <div id="log" class="log">ready</div>
45
+ </section>
46
+ </div>
47
+
48
+ <script>
49
+ const logEl = document.getElementById("log");
50
+
51
+ function pretty(v) {
52
+ try { return JSON.stringify(v, null, 2); } catch { return String(v); }
53
+ }
54
+ function writeLog(ok, title, data) {
55
+ const cls = ok ? "ok" : "ng";
56
+ logEl.innerHTML = `<span class="${cls}">${title}</span>\n${pretty(data)}`;
57
+ }
58
+
59
+ async function postJSON(url, bodyObj) {
60
+ const res = await fetch(url, {
61
+ method: "POST",
62
+ headers: {
63
+ "Content-Type": "application/json; charset=utf-8",
64
+ "Accept": "application/json",
65
+ },
66
+ body: JSON.stringify(bodyObj ?? {}),
67
+ });
68
+
69
+ const text = await res.text();
70
+ let data = {};
71
+ try { data = text ? JSON.parse(text) : {}; } catch { data = { raw: text }; }
72
+
73
+ return { ok: res.ok, status: res.status, data };
74
+ }
75
+
76
+ document.getElementById("formLogin").addEventListener("submit", async (e) => {
77
+ e.preventDefault();
78
+
79
+ const user_id = document.getElementById("user_id").value.trim();
80
+ const password = document.getElementById("password").value;
81
+
82
+ if (!user_id || !password) {
83
+ writeLog(false, "login: missing fields", { user_id });
84
+ return;
85
+ }
86
+
87
+ const r = await postJSON("/login/", { user_id, password });
88
+ writeLog(r.ok, `login: ${r.status}`, r.data);
89
+
90
+ // 例: token を localStorage に保存したいならここ(仕様次第)
91
+ // if (r.ok && r.data && r.data.token) localStorage.setItem("nexom_token", r.data.token);
92
+ });
93
+ </script>
94
+ </body>
95
+ </html>
@@ -0,0 +1,106 @@
1
+ <!-- assets/auth_page/signup.html -->
2
+ <!doctype html>
3
+ <html lang="ja">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
7
+ <title>Nexom Signup</title>
8
+ <style>
9
+ body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 24px; }
10
+ .wrap { max-width: 720px; margin: 0 auto; }
11
+ .card { border: 1px solid #eee; border-radius: 14px; padding: 16px; background: #fff; }
12
+ label { display:block; font-size:12px; margin: 12px 0 6px; color:#333; }
13
+ input { width:100%; padding:10px 12px; border:1px solid #ddd; border-radius:10px; font-size:14px; }
14
+ button { margin-top: 14px; padding:10px 12px; border:1px solid #333; border-radius:10px; background:#333; color:#fff; cursor:pointer; }
15
+ .row { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top: 10px; }
16
+ a { color:#333; }
17
+ .help { color:#666; font-size:12px; }
18
+ .log { margin-top: 14px; padding: 12px; border-radius: 12px; background:#f7f7f7; border:1px solid #eee; white-space: pre-wrap;
19
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
20
+ .ok { color:#0a7; }
21
+ .ng { color:#c33; }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <div class="wrap">
26
+ <h1 style="margin:0 0 12px;font-size:20px;">Signup</h1>
27
+
28
+ <section class="card">
29
+ <form id="formSignup">
30
+ <label for="user_id">user_id</label>
31
+ <input id="user_id" name="user_id" autocomplete="username" required />
32
+
33
+ <label for="public_name">public_name</label>
34
+ <input id="public_name" name="public_name" autocomplete="nickname" required />
35
+
36
+ <label for="password">password</label>
37
+ <input id="password" name="password" type="password" autocomplete="new-password" required />
38
+
39
+ <label for="password2">password (confirm)</label>
40
+ <input id="password2" name="password2" type="password" autocomplete="new-password" required />
41
+
42
+ <button type="submit">signup</button>
43
+
44
+ <div class="row">
45
+ <span class="help">送信先はこのアプリの <code>/signup/</code>(アプリ → AuthClient → AuthService)</span>
46
+ <a class="help" href="/login/">loginへ</a>
47
+ </div>
48
+ </form>
49
+
50
+ <div id="log" class="log">ready</div>
51
+ </section>
52
+ </div>
53
+
54
+ <script>
55
+ const logEl = document.getElementById("log");
56
+
57
+ function pretty(v) {
58
+ try { return JSON.stringify(v, null, 2); } catch { return String(v); }
59
+ }
60
+ function writeLog(ok, title, data) {
61
+ const cls = ok ? "ok" : "ng";
62
+ logEl.innerHTML = `<span class="${cls}">${title}</span>\n${pretty(data)}`;
63
+ }
64
+
65
+ async function postJSON(url, bodyObj) {
66
+ const res = await fetch(url, {
67
+ method: "POST",
68
+ headers: {
69
+ "Content-Type": "application/json; charset=utf-8",
70
+ "Accept": "application/json",
71
+ },
72
+ body: JSON.stringify(bodyObj ?? {}),
73
+ });
74
+
75
+ const text = await res.text();
76
+ let data = {};
77
+ try { data = text ? JSON.parse(text) : {}; } catch { data = { raw: text }; }
78
+
79
+ return { ok: res.ok, status: res.status, data };
80
+ }
81
+
82
+ document.getElementById("formSignup").addEventListener("submit", async (e) => {
83
+ e.preventDefault();
84
+
85
+ const user_id = document.getElementById("user_id").value.trim();
86
+ const public_name = document.getElementById("public_name").value.trim();
87
+ const password = document.getElementById("password").value;
88
+ const password2 = document.getElementById("password2").value;
89
+
90
+ if (!user_id || !public_name || !password) {
91
+ writeLog(false, "signup: missing fields", { user_id, public_name });
92
+ return;
93
+ }
94
+ if (password !== password2) {
95
+ writeLog(false, "signup: password mismatch", {});
96
+ return;
97
+ }
98
+
99
+ const r = await postJSON("/signup/", { user_id, public_name, password });
100
+ writeLog(r.ok, `signup: ${r.status}`, r.data);
101
+
102
+ // if (r.ok) location.href = "/login/";
103
+ });
104
+ </script>
105
+ </body>
106
+ </html>
@@ -3,12 +3,12 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>__STATUS__</title>
6
+ <title>{{ status }}</title>
7
7
  </head>
8
8
  <body>
9
9
  <div class="error_container">
10
- <div class="error_name">__STATUS__</div>
11
- <div class="error_message">__MESSAGE__</div>
10
+ <div class="error_name">{{ status }}</div>
11
+ <div class="error_message">{{ message }}</div>
12
12
  </div>
13
13
  </body>
14
14
  </html>
@@ -0,0 +1,16 @@
1
+ # __APP_NAME__ gateway (apache)
2
+ # Domain: __DOMAIN__
3
+ #
4
+ # NOTE:
5
+ # - Replace __DOMAIN__ with your real domain (or pass --domain).
6
+ # - This file is a template.
7
+
8
+ <VirtualHost *:80>
9
+ ServerName __DOMAIN__
10
+
11
+ ProxyPreserveHost On
12
+ ProxyPass / http://127.0.0.1:__APP_PORT__/
13
+ ProxyPassReverse / http://127.0.0.1:__APP_PORT__/
14
+
15
+ # WSGI: __APP_WSGI__
16
+ </VirtualHost>
@@ -0,0 +1,21 @@
1
+ # __APP_NAME__ gateway (nginx)
2
+ # Domain: __DOMAIN__
3
+ #
4
+ # NOTE:
5
+ # - Replace __DOMAIN__ with your real domain (or pass --domain).
6
+ # - This file is a template.
7
+
8
+ server {
9
+ listen 80;
10
+ server_name __DOMAIN__;
11
+
12
+ location / {
13
+ proxy_set_header Host $host;
14
+ proxy_set_header X-Real-IP $remote_addr;
15
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
16
+ proxy_set_header X-Forwarded-Proto $scheme;
17
+
18
+ # WSGI: __APP_WSGI__
19
+ proxy_pass http://127.0.0.1:__APP_PORT__;
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ from . import build