storage: JSON files -> SQLite (users/history/searches/events)
- one-time import from the legacy JSON (left in place as snapshot); ghost rows (int vs str ep_id) deduped on import, unrepresentable after - history POST is now an upsert: the (user, slug_id, ep_id) PK is the merge key, so the unguarded read-modify-write (lost updates) and the corruption-swallow (JSONDecodeError -> [] -> fresh overwrite) are gone - searches: NOCASE PK upsert + cap 8 - events table + position/search logging (watch-time trajectory), 90-day retention pruned at boot - account delete cascades all four tables - 22/22 tests green; old JSON files untouched
This commit is contained in:
1 parent
2c0ee8eae3
commit
202bfa41c3
5 files changed
+222
-105
No files matched your search
+17
-6
@@ -96,7 +96,7 @@ def test_no_self_signup(base=BASE):
|
||||
jar = login("admin", "adminpw12345", base)
|
||||
assert req("POST", "/signup", {"username": "zed", "password": "whatever123"},
|
||||
jar=jar, base=base) == 405
|
||||
assert "zed" not in json.load(open(_users_file()))
|
||||
assert "zed" not in _users()
|
||||
|
||||
|
||||
def test_history_requires_ep_id(base=BASE):
|
||||
@@ -172,8 +172,8 @@ def test_self_delete(base=BASE):
|
||||
# give dave a searches file too: account delete must remove it
|
||||
assert req("POST", "/api/searches", {"q": "dave-search"}, jar=jar, base=base) == 200
|
||||
assert req("POST", "/account/delete", jar=jar, base=base) == 200
|
||||
assert "dave" not in json.load(open(_users_file()))
|
||||
assert not os.path.exists(os.path.join(os.environ["ANIWEB_DATA"], "searches", "dave.json"))
|
||||
assert "dave" not in _users()
|
||||
assert _search_count("dave") == 0 # account delete cascades the rows
|
||||
# old session cookie is dead immediately (middleware re-checks existence)
|
||||
assert req("GET", "/api/me", jar=jar, base=base) == 401
|
||||
jar2 = CJ()
|
||||
@@ -230,6 +230,17 @@ def _json(method, path, jar=None, base=BASE):
|
||||
return json.loads(resp.read().decode())
|
||||
|
||||
|
||||
def _users_file():
|
||||
import os
|
||||
return os.path.join(os.environ["ANIWEB_DATA"], "users.json")
|
||||
def _users():
|
||||
import sqlite3
|
||||
c = sqlite3.connect(os.path.join(os.environ["ANIWEB_DATA"], "aniweb.db"))
|
||||
names = [r[0] for r in c.execute("SELECT name FROM users")]
|
||||
c.close()
|
||||
return names
|
||||
|
||||
|
||||
def _search_count(user):
|
||||
import sqlite3
|
||||
c = sqlite3.connect(os.path.join(os.environ["ANIWEB_DATA"], "aniweb.db"))
|
||||
n = c.execute("SELECT COUNT(*) FROM searches WHERE user=?", (user,)).fetchone()[0]
|
||||
c.close()
|
||||
return n
|
||||
Reference in new issue
Block a user