supernote 0.22.0__tar.gz → 0.23.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 (165) hide show
  1. {supernote-0.22.0 → supernote-0.23.0}/PKG-INFO +1 -1
  2. {supernote-0.22.0 → supernote-0.23.0}/pyproject.toml +1 -1
  3. supernote-0.23.0/supernote/alembic/versions/68964804740d_prevent_user_id_reuse.py +74 -0
  4. supernote-0.23.0/supernote/server/db/migrations.py +109 -0
  5. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/user.py +2 -0
  6. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/index.html +1 -1
  7. {supernote-0.22.0 → supernote-0.23.0}/supernote.egg-info/PKG-INFO +1 -1
  8. {supernote-0.22.0 → supernote-0.23.0}/supernote.egg-info/SOURCES.txt +1 -0
  9. supernote-0.22.0/supernote/server/db/migrations.py +0 -44
  10. {supernote-0.22.0 → supernote-0.23.0}/LICENSE +0 -0
  11. {supernote-0.22.0 → supernote-0.23.0}/README.md +0 -0
  12. {supernote-0.22.0 → supernote-0.23.0}/setup.cfg +0 -0
  13. {supernote-0.22.0 → supernote-0.23.0}/supernote/__init__.py +0 -0
  14. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic/__init__.py +0 -0
  15. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic/env.py +0 -0
  16. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic/script.py.mako +0 -0
  17. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic/versions/0543a383957b_initial_schema.py +0 -0
  18. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic/versions/7a8291f043bc_add_links_and_sort_fields_to_schedule_task.py +0 -0
  19. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic/versions/b8e9c0d1e2f3_backfill_summary_md5_and_timestamps.py +0 -0
  20. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic/versions/d1e2f3a4b5c6_add_client_task_ids.py +0 -0
  21. {supernote-0.22.0 → supernote-0.23.0}/supernote/alembic.ini +0 -0
  22. {supernote-0.22.0 → supernote-0.23.0}/supernote/cli/__init__.py +0 -0
  23. {supernote-0.22.0 → supernote-0.23.0}/supernote/cli/admin.py +0 -0
  24. {supernote-0.22.0 → supernote-0.23.0}/supernote/cli/client.py +0 -0
  25. {supernote-0.22.0 → supernote-0.23.0}/supernote/cli/main.py +0 -0
  26. {supernote-0.22.0 → supernote-0.23.0}/supernote/cli/notebook.py +0 -0
  27. {supernote-0.22.0 → supernote-0.23.0}/supernote/cli/server.py +0 -0
  28. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/__init__.py +0 -0
  29. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/admin.py +0 -0
  30. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/api.py +0 -0
  31. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/auth.py +0 -0
  32. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/client.py +0 -0
  33. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/device.py +0 -0
  34. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/exceptions.py +0 -0
  35. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/extended.py +0 -0
  36. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/hashing.py +0 -0
  37. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/login_client.py +0 -0
  38. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/schedule.py +0 -0
  39. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/socket.py +0 -0
  40. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/summary.py +0 -0
  41. {supernote-0.22.0 → supernote-0.23.0}/supernote/client/web.py +0 -0
  42. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/__init__.py +0 -0
  43. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/auth.py +0 -0
  44. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/base.py +0 -0
  45. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/equipment.py +0 -0
  46. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/extended.py +0 -0
  47. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/file_common.py +0 -0
  48. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/file_device.py +0 -0
  49. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/file_web.py +0 -0
  50. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/schedule.py +0 -0
  51. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/socket.py +0 -0
  52. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/summary.py +0 -0
  53. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/system.py +0 -0
  54. {supernote-0.22.0 → supernote-0.23.0}/supernote/models/user.py +0 -0
  55. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/__init__.py +0 -0
  56. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/color.py +0 -0
  57. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/converter.py +0 -0
  58. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/decoder.py +0 -0
  59. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/exceptions.py +0 -0
  60. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/fileformat.py +0 -0
  61. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/manipulator.py +0 -0
  62. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/parser.py +0 -0
  63. {supernote-0.22.0 → supernote-0.23.0}/supernote/notebook/utils.py +0 -0
  64. {supernote-0.22.0 → supernote-0.23.0}/supernote/py.typed +0 -0
  65. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/__init__.py +0 -0
  66. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/app.py +0 -0
  67. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/config.py +0 -0
  68. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/constants.py +0 -0
  69. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/__init__.py +0 -0
  70. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/base.py +0 -0
  71. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/__init__.py +0 -0
  72. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/device.py +0 -0
  73. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/file.py +0 -0
  74. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/kv.py +0 -0
  75. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/login_record.py +0 -0
  76. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/note_processing.py +0 -0
  77. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/schedule.py +0 -0
  78. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/models/summary.py +0 -0
  79. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/db/session.py +0 -0
  80. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/events.py +0 -0
  81. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/exceptions.py +0 -0
  82. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/mcp/__init__.py +0 -0
  83. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/mcp/auth.py +0 -0
  84. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/mcp/models.py +0 -0
  85. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/mcp/server.py +0 -0
  86. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/metrics.py +0 -0
  87. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/__init__.py +0 -0
  88. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/__init__.py +0 -0
  89. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/ocr/common/context.md +0 -0
  90. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/ocr/common/legend.md +0 -0
  91. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/ocr/daily/prompt.md +0 -0
  92. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/ocr/default/system.md +0 -0
  93. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/ocr/monthly/prompt.md +0 -0
  94. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/ocr/weekly/prompt.md +0 -0
  95. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/summary/common/instruction.md +0 -0
  96. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/summary/daily/prompt.md +0 -0
  97. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/summary/default/prompt.md +0 -0
  98. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/summary/monthly/prompt.md +0 -0
  99. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/resources/prompts/summary/weekly/prompt.md +0 -0
  100. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/admin.py +0 -0
  101. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/auth.py +0 -0
  102. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/decorators.py +0 -0
  103. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/extended.py +0 -0
  104. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/file_device.py +0 -0
  105. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/file_web.py +0 -0
  106. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/oss.py +0 -0
  107. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/schedule.py +0 -0
  108. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/summary.py +0 -0
  109. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/routes/system.py +0 -0
  110. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/__init__.py +0 -0
  111. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/blob.py +0 -0
  112. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/coordination.py +0 -0
  113. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/file.py +0 -0
  114. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/gemini.py +0 -0
  115. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/ical_export.py +0 -0
  116. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/integrity.py +0 -0
  117. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/processor.py +0 -0
  118. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/processor_modules/__init__.py +0 -0
  119. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/processor_modules/gemini_embedding.py +0 -0
  120. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/processor_modules/gemini_ocr.py +0 -0
  121. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/processor_modules/page_hashing.py +0 -0
  122. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/processor_modules/png_conversion.py +0 -0
  123. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/processor_modules/summary.py +0 -0
  124. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/schedule.py +0 -0
  125. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/search.py +0 -0
  126. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/summary.py +0 -0
  127. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/user.py +0 -0
  128. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/services/vfs.py +0 -0
  129. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/socket.py +0 -0
  130. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/socket_auth.py +0 -0
  131. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/favicon.ico +0 -0
  132. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/api/client.js +0 -0
  133. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/api/schedule.js +0 -0
  134. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/DashboardView.js +0 -0
  135. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/FileCard.js +0 -0
  136. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/FileViewer.js +0 -0
  137. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/LoginCard.js +0 -0
  138. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/MoveModal.js +0 -0
  139. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/RenameModal.js +0 -0
  140. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/SearchModal.js +0 -0
  141. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/Sidebar.js +0 -0
  142. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/SummaryPanel.js +0 -0
  143. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/SystemPanel.js +0 -0
  144. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/components/TaskPanel.js +0 -0
  145. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/composables/useFileSystem.js +0 -0
  146. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/composables/useSchedule.js +0 -0
  147. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/js/main.js +0 -0
  148. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/static/style.css +0 -0
  149. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/auth_utils.py +0 -0
  150. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/gemini_content.py +0 -0
  151. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/hashing.py +0 -0
  152. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/note_content.py +0 -0
  153. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/paths.py +0 -0
  154. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/prompt_loader.py +0 -0
  155. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/rate_limit.py +0 -0
  156. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/tasks.py +0 -0
  157. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/unique_id.py +0 -0
  158. {supernote-0.22.0 → supernote-0.23.0}/supernote/server/utils/url_signer.py +0 -0
  159. {supernote-0.22.0 → supernote-0.23.0}/supernote/testing/__init__.py +0 -0
  160. {supernote-0.22.0 → supernote-0.23.0}/supernote/testing/server_runner.py +0 -0
  161. {supernote-0.22.0 → supernote-0.23.0}/supernote.egg-info/dependency_links.txt +0 -0
  162. {supernote-0.22.0 → supernote-0.23.0}/supernote.egg-info/entry_points.txt +0 -0
  163. {supernote-0.22.0 → supernote-0.23.0}/supernote.egg-info/requires.txt +0 -0
  164. {supernote-0.22.0 → supernote-0.23.0}/supernote.egg-info/top_level.txt +0 -0
  165. {supernote-0.22.0 → supernote-0.23.0}/tests/test_init.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supernote
3
- Version: 0.22.0
3
+ Version: 0.23.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.22.0"
7
+ version = "0.23.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"
@@ -0,0 +1,74 @@
1
+ """Prevent reuse of deleted SQLite user IDs.
2
+
3
+ Revision ID: 68964804740d
4
+ Revises: d1e2f3a4b5c6
5
+ Create Date: 2026-09-03 00:00:00.000000
6
+ """
7
+
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ from sqlalchemy import text
12
+
13
+ revision: str = "68964804740d"
14
+ down_revision: Union[str, None] = "d1e2f3a4b5c6"
15
+ branch_labels: Union[str, Sequence[str], None] = None
16
+ depends_on: Union[str, Sequence[str], None] = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ bind = op.get_bind()
21
+ if bind.dialect.name != "sqlite":
22
+ return
23
+
24
+ # Old deletions may have left owner rows behind. Preserve their highest user
25
+ # ID as part of the sequence floor so none can become visible after upgrade.
26
+ tables = (
27
+ "users",
28
+ "devices",
29
+ "login_records",
30
+ "f_user_file",
31
+ "f_capacity",
32
+ "f_recycle_file",
33
+ "f_summary",
34
+ "f_summary_tag",
35
+ "t_schedule_task",
36
+ "t_schedule_task_group",
37
+ )
38
+
39
+ def max_owner_id(table: str) -> int:
40
+ column = "id" if table == "users" else "user_id"
41
+ query = text(f'SELECT COALESCE(MAX({column}), 0) FROM "{table}"')
42
+ return int(bind.execute(query).scalar_one())
43
+
44
+ floor = max(max_owner_id(table) for table in tables)
45
+
46
+ with op.batch_alter_table(
47
+ "users", recreate="always", table_kwargs={"sqlite_autoincrement": True}
48
+ ):
49
+ pass
50
+
51
+ current = int(
52
+ bind.execute(
53
+ text(
54
+ "SELECT COALESCE(MAX(seq), 0) FROM sqlite_sequence WHERE name = 'users'"
55
+ )
56
+ ).scalar_one()
57
+ )
58
+ sequence = max(floor, current)
59
+ result = bind.execute(
60
+ text("UPDATE sqlite_sequence SET seq = :seq WHERE name = 'users'"),
61
+ {"seq": sequence},
62
+ )
63
+ if result.rowcount == 0:
64
+ bind.execute(
65
+ text("INSERT INTO sqlite_sequence(name, seq) VALUES ('users', :seq)"),
66
+ {"seq": sequence},
67
+ )
68
+
69
+
70
+ def downgrade() -> None:
71
+ if op.get_bind().dialect.name != "sqlite":
72
+ return
73
+ with op.batch_alter_table("users", recreate="always"):
74
+ pass
@@ -0,0 +1,109 @@
1
+ import importlib.resources
2
+ import logging
3
+ from pathlib import Path
4
+
5
+ import alembic.command
6
+ import alembic.config
7
+ import sqlalchemy as sa
8
+ from alembic.migration import MigrationContext
9
+ from alembic.operations import Operations
10
+ from sqlalchemy.exc import OperationalError, ProgrammingError
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def _heal_orphaned_v20_revision(db_url: str) -> None:
16
+ """Detect and reconcile databases stamped with orphaned v0.20.0 revision c9a8b7c6d5e4.
17
+
18
+ In v0.20.0, migration c9a8b7c6d5e4 altered task_id and task_list_id to String,
19
+ but was superseded by d1e2f3a4b5c6 in subsequent releases. If detected, revert
20
+ the column types back to BigInteger and reset alembic_version to b8e9c0d1e2f3
21
+ so Alembic can cleanly upgrade forward to head.
22
+ """
23
+ sync_url = db_url.replace("+aiosqlite", "").replace("+asyncpg", "")
24
+ engine = sa.create_engine(sync_url)
25
+ try:
26
+ with engine.connect() as conn:
27
+ inspector = sa.inspect(conn)
28
+ if "alembic_version" not in inspector.get_table_names():
29
+ return
30
+
31
+ current_revision = conn.execute(
32
+ sa.text("SELECT version_num FROM alembic_version")
33
+ ).scalar()
34
+ if current_revision != "c9a8b7c6d5e4":
35
+ return
36
+
37
+ logger.warning(
38
+ "Detected orphaned v0.20.0 Alembic revision 'c9a8b7c6d5e4'. "
39
+ "Reconciling database schema back to 'b8e9c0d1e2f3'..."
40
+ )
41
+ migration_context = MigrationContext.configure(conn)
42
+ op = Operations(migration_context)
43
+
44
+ with op.batch_alter_table("t_schedule_task") as batch_op:
45
+ batch_op.alter_column(
46
+ "task_list_id", type_=sa.BigInteger(), existing_type=sa.String()
47
+ )
48
+ batch_op.alter_column(
49
+ "task_id", type_=sa.BigInteger(), existing_type=sa.String()
50
+ )
51
+
52
+ with op.batch_alter_table("t_schedule_task_group") as batch_op:
53
+ batch_op.alter_column(
54
+ "task_list_id", type_=sa.BigInteger(), existing_type=sa.String()
55
+ )
56
+
57
+ conn.execute(
58
+ sa.text(
59
+ "UPDATE alembic_version SET version_num = 'b8e9c0d1e2f3' WHERE version_num = 'c9a8b7c6d5e4'"
60
+ )
61
+ )
62
+ conn.commit()
63
+ logger.info(
64
+ "Successfully reconciled schema to 'b8e9c0d1e2f3'; proceeding with migration to head."
65
+ )
66
+ except (OperationalError, ProgrammingError) as exc:
67
+ logger.debug(
68
+ "Skipping v0.20.0 migration healing due to database check error: %s", exc
69
+ )
70
+ finally:
71
+ engine.dispose()
72
+
73
+
74
+ def run_migrations(db_url: str, target_revision: str = "head") -> None:
75
+ """Run pending database migrations using Alembic.
76
+
77
+ Args:
78
+ db_url: The database connection URL to use for migrations.
79
+ This overrides the url in alembic.ini to ensure we target
80
+ the correct environment (prod, test, etc).
81
+ target_revision: Revision to migrate to (default: "head").
82
+ """
83
+ # Locate alembic.ini inside the supernote package
84
+ traversable = importlib.resources.files("supernote") / "alembic.ini"
85
+
86
+ with importlib.resources.as_file(traversable) as ini_path:
87
+ if not ini_path.exists():
88
+ raise FileNotFoundError(f"Could not find alembic.ini at {ini_path}")
89
+
90
+ alembic_cfg = alembic.config.Config(str(ini_path))
91
+
92
+ # IMPORTANT: Override the URL with the one from the running application.
93
+ alembic_cfg.set_main_option("sqlalchemy.url", db_url)
94
+ # Don't let alembic reset our carefully configured logging
95
+ alembic_cfg.set_main_option("skip_logging_config", "true")
96
+
97
+ # Ensure the database directory exists because sqlite won't create it
98
+ if db_url.startswith("sqlite"):
99
+ # Extract path from sqlite+aiosqlite:///path or sqlite:///path
100
+ path_str = db_url.split(":///")[-1]
101
+ if path_str != ":memory:":
102
+ db_path = Path(path_str)
103
+ db_path.parent.mkdir(parents=True, exist_ok=True)
104
+
105
+ _heal_orphaned_v20_revision(db_url)
106
+
107
+ logger.info("Running database migrations to %s...", target_revision)
108
+ alembic.command.upgrade(alembic_cfg, target_revision)
109
+ logger.info("Database migrations complete.")
@@ -11,6 +11,8 @@ class UserDO(Base):
11
11
  """User database model."""
12
12
 
13
13
  __tablename__ = "users"
14
+ # User ids should not be recycled
15
+ __table_args__ = {"sqlite_autoincrement": True}
14
16
 
15
17
  id: Mapped[int] = mapped_column(Integer, primary_key=True)
16
18
  email: Mapped[str] = mapped_column(String, unique=True, index=True)
@@ -7,7 +7,7 @@
7
7
  <title>Supernote Cloud - Web Viewer</title>
8
8
  <script src="https://cdn.tailwindcss.com"></script>
9
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.2/spark-md5.min.js"></script>
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/js-sha256/0.12.0/sha256.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/js-sha256/1.0.0/sha256.min.js"></script>
11
11
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
12
12
  <link rel="stylesheet" href="/static/style.css">
13
13
  <link rel="icon" type="image/x-icon" href="/static/favicon.ico">
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supernote
3
- Version: 0.22.0
3
+ Version: 0.23.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
@@ -14,6 +14,7 @@ supernote/alembic/__init__.py
14
14
  supernote/alembic/env.py
15
15
  supernote/alembic/script.py.mako
16
16
  supernote/alembic/versions/0543a383957b_initial_schema.py
17
+ supernote/alembic/versions/68964804740d_prevent_user_id_reuse.py
17
18
  supernote/alembic/versions/7a8291f043bc_add_links_and_sort_fields_to_schedule_task.py
18
19
  supernote/alembic/versions/b8e9c0d1e2f3_backfill_summary_md5_and_timestamps.py
19
20
  supernote/alembic/versions/d1e2f3a4b5c6_add_client_task_ids.py
@@ -1,44 +0,0 @@
1
- import importlib.resources
2
- import logging
3
- from pathlib import Path
4
-
5
- import alembic.command
6
- import alembic.config
7
-
8
- logger = logging.getLogger(__name__)
9
-
10
-
11
- def run_migrations(db_url: str, target_revision: str = "head") -> None:
12
- """Run pending database migrations using Alembic.
13
-
14
- Args:
15
- db_url: The database connection URL to use for migrations.
16
- This overrides the url in alembic.ini to ensure we target
17
- the correct environment (prod, test, etc).
18
- target_revision: Revision to migrate to (default: "head").
19
- """
20
- # Locate alembic.ini inside the supernote package
21
- traversable = importlib.resources.files("supernote") / "alembic.ini"
22
-
23
- with importlib.resources.as_file(traversable) as ini_path:
24
- if not ini_path.exists():
25
- raise FileNotFoundError(f"Could not find alembic.ini at {ini_path}")
26
-
27
- alembic_cfg = alembic.config.Config(str(ini_path))
28
-
29
- # IMPORTANT: Override the URL with the one from the running application.
30
- alembic_cfg.set_main_option("sqlalchemy.url", db_url)
31
- # Don't let alembic reset our carefully configured logging
32
- alembic_cfg.set_main_option("skip_logging_config", "true")
33
-
34
- # Ensure the database directory exists because sqlite won't create it
35
- if db_url.startswith("sqlite"):
36
- # Extract path from sqlite+aiosqlite:///path or sqlite:///path
37
- path_str = db_url.split(":///")[-1]
38
- if path_str != ":memory:":
39
- db_path = Path(path_str)
40
- db_path.parent.mkdir(parents=True, exist_ok=True)
41
-
42
- logger.info("Running database migrations to %s...", target_revision)
43
- alembic.command.upgrade(alembic_cfg, target_revision)
44
- logger.info("Database migrations complete.")
File without changes
File without changes
File without changes