initial commit

This commit is contained in:
Michał Rudowicz 2025-10-06 21:48:01 +02:00
commit 38e8972b99
8 changed files with 257 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__/

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# HSWro's NomadNet page
Made entirely for fun of making it.
Requires Redis/Valkey running on localhost on its default port.

55
pages/chat.mu Executable file
View File

@ -0,0 +1,55 @@
#!/usr/bin/env python3
import hswro
from os import environ
from redis import Redis
from datetime import datetime
import json
from uuid import uuid4
from typing import Optional
CHAT_NAME = "chat_test"
toast: Optional[str] = None
l = hswro.get_login_info()
r = Redis(host='localhost', port=6379, db=0)
msg_to_post = environ.get('field_message', None)
if msg_to_post is not None and msg_to_post.strip() != "" and l[0] is not None:
msg_to_post = hswro.sanitize_input(msg_to_post[0:255].strip())
r.rpush(CHAT_NAME, str(json.dumps({
"when": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
"who": l[0],
"msg": msg_to_post,
"id": uuid4().hex
})))
r.ltrim(CHAT_NAME, -20, -1)
#print(environ)
msg_to_del = environ.get('var_delete', None)
if msg_to_del is not None and hswro.is_admin():
for msg in r.lrange(CHAT_NAME, 0, -1):
m = json.loads(msg.decode('utf-8'))
if m['id'] == msg_to_del:
toast = f"\nDeleted a post by {m['who']}"
r.lrem(CHAT_NAME, 0, msg)
break
hswro.header("Shoutbox")
if toast is not None:
print(f"`c{toast}")
print("`l")
if l[0] is not None:
print(f"{l[0]}: `BFFF`F222`<message`>{hswro.BGCOLOR} [`[Send`:/page/chat.mu`*]]")
for msg in reversed(r.lrange(CHAT_NAME, 0, -1)):
m = json.loads(msg.decode('utf-8'))
delbtn = ""
if hswro.is_admin():
delbtn = f"`B400`FAAA`!`[DEL`:/page/chat.mu`delete={m['id']}]`!"
print(f"{delbtn}`BCCC`F333{m['when']}`BAAA`F444{'<'+m['who']+'>':>16}`B333`FCCC{m['msg']}")
hswro.reset_colors()
hswro.footer()

117
pages/hswro.py Executable file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env python3
from os import environ
from typing import Optional, Tuple
from datetime import datetime
from redis import Redis
from hashlib import sha256
from functools import reduce
s = datetime.now()
NO_REMOTE_IDENTITY_MSG = """User identity hash not available.
In MeshChat: use the fingerprint button in the top right to send the identification information.
In NomadNet: Save node, click on it, go to <Info> and select "Identify when connecting"."""
FORBIDDEN_INPUT = [
"[",
"`",
"\n"
]
ADMIN_IDENTITIES = [
"e6c72573bb91d48338dbcc57d0223b81",
"70c9608c9a0f4ae895f7fab406554e1c"
]
BGCOLOR = "`F333`BDDD"
def is_admin() -> bool:
remote_identity = environ.get("remote_identity", None)
if remote_identity is None:
return False
return remote_identity in ADMIN_IDENTITIES
def sanitize_input(text: str) -> str:
return reduce(lambda a, b: a.replace(b, ""), FORBIDDEN_INPUT, text)
def check_password(password: str) -> bool:
expected = '34a77e61000b2ba1f56201332ef64d93f9cdc60e63ab48bc255102586ef7e592'
gotten = sha256(f"hswrosalt{password}".encode('utf-8'), usedforsecurity=True).hexdigest()
return expected == gotten
def check_name(username: str) -> bool:
if len(username) >= 16:
return False
return all(map(lambda c: c.isalnum(), username))
def get_login_info() -> Tuple[Optional[str], Optional[str]]:
"""Returns [login_name, error]"""
remote_identity = environ.get("remote_identity", None)
if remote_identity is None:
return (None, NO_REMOTE_IDENTITY_MSG)
r = Redis(host='localhost', port=6379, db=0)
login = r.get(f'login_info_{remote_identity}')
if login is None:
return (None, None)
return (login.decode('utf-8'), None)
def login(username: str) -> Optional[str]:
"""Returns err"""
remote_identity = environ.get("remote_identity", None)
if remote_identity is None:
return NO_REMOTE_IDENTITY_MSG
if not check_name(username):
return "Username should only contain alphanumeric characters and have at most 16 characters."
r = Redis(host='localhost', port=6379, db=0)
r.set(f'login_info_{remote_identity}', username)
return None
def logout():
remote_identity = environ.get("remote_identity", None)
if remote_identity is None:
return
r = Redis(host='localhost', port=6379, db=0)
r.delete(f'login_info_{remote_identity}')
def login_button() -> str:
l = get_login_info()
if l[0] is None:
return "[`[Identify`:/page/login.mu`]]"
else:
return f"Welcome, {l[0]} | [`[Forget`:/page/logout.mu`]]"
def header(title: Optional[str] = None):
if title is None:
title = ""
else:
title = ": `i" + title
print(f"""`F000`BFB1
`c
-*abc
`r {login_button()}
`l `!Hackerspace`!Wrocław{title}
`a
-_
`a
``
`FEEE`B333
`c
`l `[Home`:/page/index.mu]` | `[Status`:/page/status.mu]` | `[Shoutbox`:/page/chat.mu]`
`a
``
{BGCOLOR}
""")
def reset_colors() -> str:
print(BGCOLOR)
def footer():
print(f"{BGCOLOR}`r Rendered in: {(datetime.now()-s).total_seconds()}s")
print(f"{datetime.now()}")
if __name__ == "__main__":
pass

7
pages/index.mu Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env python3
import hswro
hswro.header()
print("Nothing to see here... yet")
hswro.footer()

31
pages/login.mu Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
import hswro
from os import environ
def login_form():
print("`c `!Login:`")
print("Username: `<16|username`>")
print("Password: `<!16|pass`>")
print("[`[Submit`:/page/login.mu`*]]")
l = hswro.get_login_info()
form_login = environ.get('field_username', None)
form_pass = environ.get('field_pass', None)
hswro.header()
if l[1] is not None:
print(f"`c`!Warning:`! {l[1]}")
if form_pass is not None and not hswro.check_password(form_pass):
print(f"`c`!Incorrect password.`!")
if hswro.check_password(form_pass) and form_login is not None:
e = hswro.login(form_login)
if e is not None:
print(f"`c`!Error:`! {e}")
if l[0] is None:
login_form()
else:
print(f"Welcome, {l[0]}.")
#print("`l\n\n\n\n\n")
#print(environ)
hswro.footer()

10
pages/logout.mu Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python3
import hswro
from os import environ
hswro.logout()
hswro.header()
print("`cYou have been logged out.")
hswro.footer()

31
pages/status.mu Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
import os
import time
import subprocess
import json
import hswro
def sizeof_fmt(num, suffix="B"):
for unit in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"):
if abs(num) < 1024.0:
return f"{num:3.1f}{unit}{suffix}"
num /= 1024.0
return f"{num:.1f}Yi{suffix}"
status_raw = subprocess.run(["/home/reticulum/venv/bin/rnstatus", "-j"], capture_output=True)
status = json.loads(status_raw.stdout)
hswro.header("Node Status")
print("> Interfaces")
for i in status['interfaces']:
print(">> ", i['short_name'])
print("`!Clients:`! ", i['clients'])
print("`!Current RX:`! ", i['rxs'])
print("`!Current TX:`! ", i['txs'])
print("`!Total RX:`! ", sizeof_fmt(i['rxb']))
print("`!Total TX:`! ", sizeof_fmt(i['txb']))
hswro.footer()