supernote 0.6.2__tar.gz → 0.7.0__tar.gz

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 (50) hide show
  1. {supernote-0.6.2 → supernote-0.7.0}/PKG-INFO +1 -1
  2. {supernote-0.6.2 → supernote-0.7.0}/pyproject.toml +1 -1
  3. {supernote-0.6.2 → supernote-0.7.0}/supernote/cli/server.py +23 -17
  4. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/app.py +11 -2
  5. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/config.py +24 -17
  6. {supernote-0.6.2 → supernote-0.7.0}/supernote.egg-info/PKG-INFO +1 -1
  7. {supernote-0.6.2 → supernote-0.7.0}/LICENSE +0 -0
  8. {supernote-0.6.2 → supernote-0.7.0}/README.md +0 -0
  9. {supernote-0.6.2 → supernote-0.7.0}/setup.cfg +0 -0
  10. {supernote-0.6.2 → supernote-0.7.0}/supernote/__init__.py +0 -0
  11. {supernote-0.6.2 → supernote-0.7.0}/supernote/cli/__init__.py +0 -0
  12. {supernote-0.6.2 → supernote-0.7.0}/supernote/cli/client.py +0 -0
  13. {supernote-0.6.2 → supernote-0.7.0}/supernote/cli/main.py +0 -0
  14. {supernote-0.6.2 → supernote-0.7.0}/supernote/cli/notebook.py +0 -0
  15. {supernote-0.6.2 → supernote-0.7.0}/supernote/client/__init__.py +0 -0
  16. {supernote-0.6.2 → supernote-0.7.0}/supernote/client/api_model.py +0 -0
  17. {supernote-0.6.2 → supernote-0.7.0}/supernote/client/auth.py +0 -0
  18. {supernote-0.6.2 → supernote-0.7.0}/supernote/client/client.py +0 -0
  19. {supernote-0.6.2 → supernote-0.7.0}/supernote/client/cloud_client.py +0 -0
  20. {supernote-0.6.2 → supernote-0.7.0}/supernote/client/exceptions.py +0 -0
  21. {supernote-0.6.2 → supernote-0.7.0}/supernote/client/login_client.py +0 -0
  22. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/__init__.py +0 -0
  23. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/color.py +0 -0
  24. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/converter.py +0 -0
  25. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/decoder.py +0 -0
  26. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/exceptions.py +0 -0
  27. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/fileformat.py +0 -0
  28. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/manipulator.py +0 -0
  29. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/parser.py +0 -0
  30. {supernote-0.6.2 → supernote-0.7.0}/supernote/notebook/utils.py +0 -0
  31. {supernote-0.6.2 → supernote-0.7.0}/supernote/py.typed +0 -0
  32. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/__init__.py +0 -0
  33. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/models/__init__.py +0 -0
  34. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/models/auth.py +0 -0
  35. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/models/base.py +0 -0
  36. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/models/file.py +0 -0
  37. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/routes/auth.py +0 -0
  38. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/routes/decorators.py +0 -0
  39. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/routes/file.py +0 -0
  40. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/routes/system.py +0 -0
  41. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/services/__init__.py +0 -0
  42. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/services/file.py +0 -0
  43. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/services/state.py +0 -0
  44. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/services/storage.py +0 -0
  45. {supernote-0.6.2 → supernote-0.7.0}/supernote/server/services/user.py +0 -0
  46. {supernote-0.6.2 → supernote-0.7.0}/supernote.egg-info/SOURCES.txt +0 -0
  47. {supernote-0.6.2 → supernote-0.7.0}/supernote.egg-info/dependency_links.txt +0 -0
  48. {supernote-0.6.2 → supernote-0.7.0}/supernote.egg-info/entry_points.txt +0 -0
  49. {supernote-0.6.2 → supernote-0.7.0}/supernote.egg-info/requires.txt +0 -0
  50. {supernote-0.6.2 → supernote-0.7.0}/supernote.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supernote
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: All-in-one toolkit for Supernote devices: parse notebooks, self-host services, access services
5
5
  Author-email: jya <jya@wizmy.net>, Allen Porter <allen.porter@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ requires = ["setuptools>=77.0"]
4
4
 
5
5
  [project]
6
6
  name = "supernote"
7
- version = "0.6.2"
7
+ version = "0.7.0"
8
8
  license = "Apache-2.0"
9
9
  license-files = ["LICENSE"]
10
10
  description = "All-in-one toolkit for Supernote devices: parse notebooks, self-host services, access services"
@@ -2,25 +2,17 @@
2
2
 
3
3
  import argparse
4
4
  import getpass
5
+ import hashlib
5
6
  import secrets
6
7
 
7
- import yaml
8
-
9
8
  from supernote.server import app as server_app
10
- from supernote.server.config import AuthConfig, ServerConfig, UsersConfig
11
- from supernote.server.services.user import UserService
9
+ from supernote.server.config import ServerConfig, UserEntry
12
10
 
13
11
 
14
- def get_auth_config(config_dir: str | None) -> AuthConfig:
12
+ def list_users(config_dir: str | None):
15
13
  # Load base config
16
14
  server_config = ServerConfig.load(config_dir)
17
- return server_config.auth
18
-
19
-
20
- def list_users(config_dir: str | None):
21
- config = get_auth_config(config_dir)
22
- service = UserService(config)
23
- for user in service.list_users():
15
+ for user in server_config.auth.users:
24
16
  status = "active" if user.is_active else "inactive"
25
17
  print(f"{user.username} ({status})")
26
18
 
@@ -30,13 +22,17 @@ def add_user(username: str, password: str | None = None):
30
22
  password = getpass.getpass(f"Password for {username}: ")
31
23
 
32
24
  # Generate the YAML snippet for a single user
33
- user_entry = UserService.create_user_entry(username, password)
34
- users_config = UsersConfig(users=[user_entry])
25
+ password_md5 = hashlib.md5(password.encode()).hexdigest()
26
+ user_entry = UserEntry(
27
+ username=username,
28
+ password_md5=password_md5,
29
+ display_name=username,
30
+ )
35
31
 
36
32
  # Print YAML to stdout
37
33
  print("# Generated Supernote User Entry")
38
- print("# Append this to your users.yaml file.")
39
- print(yaml.safe_dump(users_config.to_dict(), default_flow_style=False))
34
+ print("# Add this entry to the 'users' list in your config.yaml file.")
35
+ print("- " + user_entry.to_yaml(omit_none=True).strip().replace("\n", "\n "))
40
36
 
41
37
 
42
38
  def config_init():
@@ -44,10 +40,20 @@ def config_init():
44
40
  secret = secrets.token_hex(32)
45
41
  config = ServerConfig()
46
42
  config.auth.secret_key = secret
43
+ config.trace_log_file = None # Use system storage default
44
+
45
+ # Add a sample user entry to the output
46
+ sample_user = UserEntry(
47
+ username="user@example.com",
48
+ password_md5=hashlib.md5(b"password").hexdigest(),
49
+ display_name="Sample User",
50
+ )
51
+ config.auth.users = [sample_user]
47
52
 
48
53
  print("# Generated Supernote Server Configuration")
49
54
  print("# Save this as config.yaml.")
50
- print(yaml.safe_dump(config.to_dict(), default_flow_style=False))
55
+ print("# You should change the sample user entry below.")
56
+ print(config.to_yaml(omit_none=True))
51
57
 
52
58
 
53
59
  def generate_secret():
@@ -57,13 +57,18 @@ async def trace_middleware(
57
57
 
58
58
  # Get config from app
59
59
  server_config: ServerConfig = request.app["config"]
60
+ if not server_config.trace_log_file:
61
+ return await handler(request)
62
+
63
+ trace_log_path = Path(server_config.trace_log_file)
60
64
 
61
65
  try:
62
- with open(server_config.trace_log_file, "a") as f:
66
+ trace_log_path.parent.mkdir(parents=True, exist_ok=True)
67
+ with open(trace_log_path, "a") as f:
63
68
  f.write(json.dumps(log_entry) + "\n")
64
69
  f.flush()
65
70
  except Exception as e:
66
- logger.error(f"Failed to write to trace log: {e}")
71
+ logger.error(f"Failed to write to trace log at {trace_log_path}: {e}")
67
72
 
68
73
  logger.info(
69
74
  f"Trace: {request.method} {request.path} (Body: {len(body_bytes) if body_bytes else 0} bytes)"
@@ -125,6 +130,10 @@ def create_app(
125
130
  app["file_service"] = FileService(storage_service)
126
131
  app["sync_locks"] = {} # user -> (equipment_no, expiry_time)
127
132
 
133
+ # Resolve trace log path if not set
134
+ if not config.trace_log_file:
135
+ config.trace_log_file = str(storage_service.system_dir / "trace.log")
136
+
128
137
  # Register routes
129
138
  app.add_routes(system.routes)
130
139
  app.add_routes(auth.routes)
@@ -3,23 +3,26 @@ import os
3
3
  import secrets
4
4
  from dataclasses import dataclass, field
5
5
  from pathlib import Path
6
- from typing import Any
7
6
 
8
- import yaml
9
- from mashumaro.mixins.dict import DataClassDictMixin
7
+ from mashumaro.config import TO_DICT_ADD_OMIT_NONE_FLAG, BaseConfig
8
+ from mashumaro.mixins.yaml import DataClassYAMLMixin
10
9
 
11
10
  logger = logging.getLogger(__name__)
12
11
 
13
12
 
14
13
  @dataclass
15
- class AuthConfig(DataClassDictMixin):
14
+ class AuthConfig(DataClassYAMLMixin):
16
15
  secret_key: str = ""
17
16
  expiration_hours: int = 24
18
17
  users: list["UserEntry"] = field(default_factory=list)
19
18
 
19
+ class Config(BaseConfig):
20
+ omit_none = True
21
+ code_generation_options = [TO_DICT_ADD_OMIT_NONE_FLAG] # type: ignore[list-item]
22
+
20
23
 
21
24
  @dataclass
22
- class UserEntry(DataClassDictMixin):
25
+ class UserEntry(DataClassYAMLMixin):
23
26
  username: str
24
27
  password_md5: str
25
28
  is_active: bool = True
@@ -29,12 +32,16 @@ class UserEntry(DataClassDictMixin):
29
32
  avatar: str | None = None
30
33
  total_capacity: str = "25485312"
31
34
 
35
+ class Config(BaseConfig):
36
+ omit_none = True
37
+ code_generation_options = [TO_DICT_ADD_OMIT_NONE_FLAG] # type: ignore[list-item]
38
+
32
39
 
33
40
  @dataclass
34
- class ServerConfig(DataClassDictMixin):
41
+ class ServerConfig(DataClassYAMLMixin):
35
42
  host: str = "0.0.0.0"
36
43
  port: int = 8080
37
- trace_log_file: str = "data/server_trace.log"
44
+ trace_log_file: str | None = None
38
45
  storage_dir: str = "storage"
39
46
  auth: AuthConfig = field(default_factory=AuthConfig)
40
47
 
@@ -47,25 +54,18 @@ class ServerConfig(DataClassDictMixin):
47
54
  config_dir_path = Path(config_dir)
48
55
  config_file = config_dir_path / "config.yaml"
49
56
 
50
- file_data: dict[str, Any] = {}
57
+ config = cls()
51
58
  if config_file.exists():
52
59
  try:
53
60
  with open(config_file, "r") as f:
54
- file_data = yaml.safe_load(f) or {}
61
+ config = cls.from_yaml(f.read())
55
62
  except Exception as e:
56
63
  logger.warning(f"Failed to load config file {config_file}: {e}")
57
64
 
58
- try:
59
- config = cls.from_dict(file_data)
60
- except Exception as e:
61
- logger.warning(
62
- f"Failed to parse config file {config_file}, using defaults: {e}"
63
- )
64
- config = cls()
65
-
66
65
  # 4. JWT Secret priority: Env > Config > Random(in-memory only)
67
66
  env_secret = os.getenv("SUPERNOTE_JWT_SECRET")
68
67
  if env_secret:
68
+ logger.info("Using SUPERNOTE_JWT_SECRET")
69
69
  config.auth.secret_key = env_secret
70
70
 
71
71
  if not config.auth.secret_key:
@@ -77,14 +77,21 @@ class ServerConfig(DataClassDictMixin):
77
77
  # Apply other env var overrides
78
78
  if os.getenv("SUPERNOTE_HOST"):
79
79
  config.host = os.getenv("SUPERNOTE_HOST", config.host)
80
+ logger.info(f"Using SUPERNOTE_HOST: {config.host}")
80
81
 
81
82
  if os.getenv("SUPERNOTE_PORT"):
82
83
  try:
83
84
  config.port = int(os.getenv("SUPERNOTE_PORT", str(config.port)))
85
+ logger.info(f"Using SUPERNOTE_PORT: {config.port}")
84
86
  except ValueError:
85
87
  pass
86
88
 
87
89
  if os.getenv("SUPERNOTE_STORAGE_DIR"):
88
90
  config.storage_dir = os.getenv("SUPERNOTE_STORAGE_DIR", config.storage_dir)
91
+ logger.info(f"Using SUPERNOTE_STORAGE_DIR: {config.storage_dir}")
89
92
 
90
93
  return config
94
+
95
+ class Config(BaseConfig):
96
+ omit_none = True
97
+ code_generation_options = [TO_DICT_ADD_OMIT_NONE_FLAG] # type: ignore[list-item]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supernote
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: All-in-one toolkit for Supernote devices: parse notebooks, self-host services, access services
5
5
  Author-email: jya <jya@wizmy.net>, Allen Porter <allen.porter@gmail.com>
6
6
  License-Expression: Apache-2.0
File without changes
File without changes
File without changes
File without changes