Development

This commit is contained in:
Matthew Grotke 2026-06-10 14:23:47 -04:00
parent f5722f3c7b
commit d60bf15ce4
15 changed files with 367 additions and 285 deletions

View file

@ -1,13 +1,14 @@
import copy, json, subprocess, hashlib, os, uuid
import os as _os
import sqlite3 as _sqlite3
from datetime import datetime, timezone
from flask import session
APP_DIR = _os.path.dirname(_os.path.abspath(__file__))
CONFIGS_DIR = '/routlin_location'
DATA_DIR = '/data'
WWW_DIR = '/www'
ACCOUNTS_FILE = f'{CONFIGS_DIR}/.dashboard-accounts'
SESSIONS_DB = f'{CONFIGS_DIR}/.dashboard-sessions'
ACCOUNTS_DB = f'{DATA_DIR}/.dashboard-accounts'
CONFIG_FILE = f'{CONFIGS_DIR}/config.json'
DASHBOARD_QUEUE = f'{CONFIGS_DIR}/.dashboard-queue'
DASHBOARD_DONE = f'{CONFIGS_DIR}/.dashboard-done'
@ -28,6 +29,93 @@ DASHB_INTERVAL_SECS = 30
QUEUE_MAX_LINES = 50
# Accounts DB ========================================================
def open_accounts_db():
con = _sqlite3.connect(ACCOUNTS_DB, timeout=5)
con.execute('PRAGMA journal_mode=WAL')
con.row_factory = _sqlite3.Row
return con
def init_accounts_db():
con = open_accounts_db()
con.executescript('''
CREATE TABLE IF NOT EXISTS accounts (
account_id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
access_level INTEGER NOT NULL DEFAULT 1,
hashed_password TEXT NOT NULL DEFAULT '',
created_ts INTEGER NOT NULL DEFAULT 0,
created_by TEXT NOT NULL DEFAULT ''
);
CREATE TABLE IF NOT EXISTS sessions (
session_id TEXT PRIMARY KEY,
account_id TEXT NOT NULL,
tz_offset_seconds INTEGER NOT NULL DEFAULT 0,
apply_changes_immediately INTEGER NOT NULL DEFAULT 0,
session_started_ts INTEGER NOT NULL,
last_seen_ts INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS pending_verifications (
email TEXT PRIMARY KEY,
hashed_password TEXT NOT NULL,
tz_offset_seconds INTEGER NOT NULL DEFAULT 0,
code TEXT NOT NULL,
expires_ts INTEGER NOT NULL
);
''')
con.commit()
con.close()
_LEVEL_INT_TO_STR = {0: 'nothing', 1: 'viewer', 2: 'administrator', 3: 'manager'}
_LEVEL_STR_TO_INT = {'nothing': 0, 'viewer': 1, 'administrator': 2, 'manager': 3}
def _account_row_to_dict(row):
if row is None:
return None
import time as _t
from datetime import datetime as _dt
d = dict(row)
d['email_address'] = d.pop('email', d.get('email_address', ''))
d['access_level_int'] = d.get('access_level', 1)
d['access_level'] = _LEVEL_INT_TO_STR.get(d['access_level_int'], 'viewer')
d['account_status'] = 'active' if d.get('hashed_password') else 'pending'
d['account_created_by'] = d.get('created_by', '')
ts = d.get('created_ts', 0)
try:
d['account_created_ts'] = _dt.fromtimestamp(int(ts)).strftime('%Y-%m-%d %H:%M') if ts else '-'
except Exception:
d['account_created_ts'] = '-'
return d
def get_account_by_email(email):
try:
con = open_accounts_db()
row = con.execute('SELECT * FROM accounts WHERE lower(email)=?', (email.lower(),)).fetchone()
con.close()
return _account_row_to_dict(row)
except Exception:
return None
def get_account_by_id(account_id):
try:
con = open_accounts_db()
row = con.execute('SELECT * FROM accounts WHERE account_id=?', (account_id,)).fetchone()
con.close()
return _account_row_to_dict(row)
except Exception:
return None
def list_accounts():
try:
con = open_accounts_db()
rows = con.execute('SELECT * FROM accounts ORDER BY created_ts').fetchall()
con.close()
return [_account_row_to_dict(r) for r in rows]
except Exception:
return []
_config_cache = None
_config_mtime = None
@ -354,7 +442,6 @@ def queued_msg(cmd=None, description='', action_label='Configuration saved'):
# Snapshot system ===================================================
import re as _re
import sqlite3 as _sqlite3
def _db():
@ -845,17 +932,7 @@ def config_datasource(name):
return rows
if name == 'accounts':
try:
with open(ACCOUNTS_FILE) as f:
data = json.load(f)
except Exception:
data = {}
rows = []
for acct in data.get('accounts', []):
row = dict(acct)
row['account_status'] = 'active' if acct.get('hashed_password') else 'pending'
rows.append(row)
return rows
return list_accounts()
if name == 'vpn_peers':
rows = []