supernote 0.13.6__py3-none-any.whl

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 (138) hide show
  1. supernote/__init__.py +28 -0
  2. supernote/alembic/__init__.py +0 -0
  3. supernote/alembic/env.py +121 -0
  4. supernote/alembic/script.py.mako +28 -0
  5. supernote/alembic/versions/0543a383957b_initial_schema.py +312 -0
  6. supernote/alembic.ini +149 -0
  7. supernote/cli/__init__.py +1 -0
  8. supernote/cli/admin.py +178 -0
  9. supernote/cli/client.py +434 -0
  10. supernote/cli/main.py +61 -0
  11. supernote/cli/notebook.py +313 -0
  12. supernote/cli/server.py +89 -0
  13. supernote/client/__init__.py +28 -0
  14. supernote/client/admin.py +76 -0
  15. supernote/client/api.py +135 -0
  16. supernote/client/api_model.py +231 -0
  17. supernote/client/auth.py +84 -0
  18. supernote/client/client.py +365 -0
  19. supernote/client/device.py +361 -0
  20. supernote/client/exceptions.py +33 -0
  21. supernote/client/extended.py +20 -0
  22. supernote/client/hashing.py +50 -0
  23. supernote/client/login_client.py +154 -0
  24. supernote/client/schedule.py +149 -0
  25. supernote/client/summary.py +156 -0
  26. supernote/client/web.py +213 -0
  27. supernote/models/__init__.py +14 -0
  28. supernote/models/auth.py +380 -0
  29. supernote/models/base.py +266 -0
  30. supernote/models/equipment.py +276 -0
  31. supernote/models/extended.py +124 -0
  32. supernote/models/file_common.py +95 -0
  33. supernote/models/file_device.py +571 -0
  34. supernote/models/file_web.py +602 -0
  35. supernote/models/schedule.py +663 -0
  36. supernote/models/summary.py +830 -0
  37. supernote/models/system.py +431 -0
  38. supernote/models/user.py +638 -0
  39. supernote/notebook/__init__.py +67 -0
  40. supernote/notebook/color.py +93 -0
  41. supernote/notebook/converter.py +560 -0
  42. supernote/notebook/decoder.py +398 -0
  43. supernote/notebook/exceptions.py +43 -0
  44. supernote/notebook/fileformat.py +463 -0
  45. supernote/notebook/manipulator.py +420 -0
  46. supernote/notebook/parser.py +737 -0
  47. supernote/notebook/utils.py +49 -0
  48. supernote/py.typed +0 -0
  49. supernote/server/__init__.py +12 -0
  50. supernote/server/app.py +428 -0
  51. supernote/server/config.py +265 -0
  52. supernote/server/constants.py +26 -0
  53. supernote/server/db/__init__.py +9 -0
  54. supernote/server/db/base.py +8 -0
  55. supernote/server/db/migrations.py +44 -0
  56. supernote/server/db/models/__init__.py +23 -0
  57. supernote/server/db/models/device.py +17 -0
  58. supernote/server/db/models/file.py +107 -0
  59. supernote/server/db/models/kv.py +17 -0
  60. supernote/server/db/models/login_record.py +22 -0
  61. supernote/server/db/models/note_processing.py +100 -0
  62. supernote/server/db/models/schedule.py +83 -0
  63. supernote/server/db/models/summary.py +122 -0
  64. supernote/server/db/models/user.py +30 -0
  65. supernote/server/db/session.py +88 -0
  66. supernote/server/events.py +63 -0
  67. supernote/server/exceptions.py +153 -0
  68. supernote/server/mcp/__init__.py +1 -0
  69. supernote/server/mcp/models.py +109 -0
  70. supernote/server/mcp/server.py +176 -0
  71. supernote/server/resources/__init__.py +0 -0
  72. supernote/server/resources/prompts/__init__.py +0 -0
  73. supernote/server/resources/prompts/ocr/common/context.md +19 -0
  74. supernote/server/resources/prompts/ocr/common/legend.md +11 -0
  75. supernote/server/resources/prompts/ocr/daily/prompt.md +12 -0
  76. supernote/server/resources/prompts/ocr/default/system.md +14 -0
  77. supernote/server/resources/prompts/ocr/monthly/prompt.md +22 -0
  78. supernote/server/resources/prompts/ocr/weekly/prompt.md +16 -0
  79. supernote/server/resources/prompts/summary/common/instruction.md +8 -0
  80. supernote/server/resources/prompts/summary/daily/prompt.md +6 -0
  81. supernote/server/resources/prompts/summary/default/prompt.md +4 -0
  82. supernote/server/resources/prompts/summary/monthly/prompt.md +6 -0
  83. supernote/server/resources/prompts/summary/weekly/prompt.md +6 -0
  84. supernote/server/routes/admin.py +109 -0
  85. supernote/server/routes/auth.py +337 -0
  86. supernote/server/routes/decorators.py +13 -0
  87. supernote/server/routes/extended.py +139 -0
  88. supernote/server/routes/file_device.py +593 -0
  89. supernote/server/routes/file_web.py +597 -0
  90. supernote/server/routes/oss.py +317 -0
  91. supernote/server/routes/schedule.py +223 -0
  92. supernote/server/routes/summary.py +390 -0
  93. supernote/server/routes/system.py +57 -0
  94. supernote/server/services/__init__.py +15 -0
  95. supernote/server/services/blob.py +214 -0
  96. supernote/server/services/coordination.py +171 -0
  97. supernote/server/services/file.py +970 -0
  98. supernote/server/services/gemini.py +66 -0
  99. supernote/server/services/integrity.py +112 -0
  100. supernote/server/services/processor.py +331 -0
  101. supernote/server/services/processor_modules/__init__.py +145 -0
  102. supernote/server/services/processor_modules/gemini_embedding.py +112 -0
  103. supernote/server/services/processor_modules/gemini_ocr.py +149 -0
  104. supernote/server/services/processor_modules/page_hashing.py +220 -0
  105. supernote/server/services/processor_modules/png_conversion.py +107 -0
  106. supernote/server/services/processor_modules/summary.py +302 -0
  107. supernote/server/services/prompt_loader.py +133 -0
  108. supernote/server/services/schedule.py +161 -0
  109. supernote/server/services/search.py +250 -0
  110. supernote/server/services/summary.py +419 -0
  111. supernote/server/services/user.py +480 -0
  112. supernote/server/services/vfs.py +476 -0
  113. supernote/server/static/favicon.ico +0 -0
  114. supernote/server/static/index.html +186 -0
  115. supernote/server/static/js/api/client.js +463 -0
  116. supernote/server/static/js/components/FileCard.js +55 -0
  117. supernote/server/static/js/components/FileViewer.js +137 -0
  118. supernote/server/static/js/components/LoginCard.js +64 -0
  119. supernote/server/static/js/components/MoveModal.js +74 -0
  120. supernote/server/static/js/components/RenameModal.js +39 -0
  121. supernote/server/static/js/components/SummaryPanel.js +101 -0
  122. supernote/server/static/js/components/SystemPanel.js +158 -0
  123. supernote/server/static/js/composables/useFileSystem.js +116 -0
  124. supernote/server/static/js/main.js +241 -0
  125. supernote/server/static/style.css +14 -0
  126. supernote/server/utils/hashing.py +18 -0
  127. supernote/server/utils/note_content.py +76 -0
  128. supernote/server/utils/paths.py +58 -0
  129. supernote/server/utils/rate_limit.py +52 -0
  130. supernote/server/utils/tasks.py +71 -0
  131. supernote/server/utils/unique_id.py +48 -0
  132. supernote/server/utils/url_signer.py +218 -0
  133. supernote-0.13.6.dist-info/METADATA +183 -0
  134. supernote-0.13.6.dist-info/RECORD +138 -0
  135. supernote-0.13.6.dist-info/WHEEL +5 -0
  136. supernote-0.13.6.dist-info/entry_points.txt +3 -0
  137. supernote-0.13.6.dist-info/licenses/LICENSE +201 -0
  138. supernote-0.13.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,380 @@
1
+ """Module for authentication API data models.
2
+
3
+ The following endpoints are supported:
4
+ - /api/official/user/account/login/equipment
5
+ - /api/official/user/account/login/new
6
+ - /api/official/user/query/random/code
7
+ - /api/official/user/sms/login
8
+ - /api/user/sms/validcode/send
9
+ - /api/user/query/token
10
+ - /api/user/validcode/pre-auth
11
+ - /api/user/logout (empty body)
12
+ """
13
+
14
+ from dataclasses import dataclass, field
15
+
16
+ from mashumaro import field_options
17
+ from mashumaro.config import BaseConfig
18
+ from mashumaro.mixins.json import DataClassJSONMixin
19
+
20
+ from .base import BaseEnum, BaseResponse
21
+
22
+ COUNTRY_CODE = 1
23
+ BROWSER = "Chrome142"
24
+ LANGUAGE = "en"
25
+
26
+
27
+ class Equipment(BaseEnum):
28
+ """Device type."""
29
+
30
+ WEB = 1
31
+ APP = 2
32
+ TERMINAL = 3
33
+ USER_PLATFORM = 4
34
+
35
+
36
+ def _deserialize_int_equipment(value: str) -> Equipment:
37
+ return Equipment(int(value))
38
+
39
+
40
+ @dataclass
41
+ class UserCheckDTO(DataClassJSONMixin):
42
+ """Request to check user existence."""
43
+
44
+ email: str
45
+ country_code: str = field(metadata=field_options(alias="countryCode"), default="")
46
+ telephone: str = ""
47
+ user_name: str = field(metadata=field_options(alias="userName"), default="")
48
+ domain: str = ""
49
+
50
+ class Config(BaseConfig):
51
+ serialize_by_alias = True
52
+
53
+
54
+ class LoginMethod(str, BaseEnum):
55
+ """Method for logging in to account."""
56
+
57
+ PHONE = "1"
58
+ EMAIL = "2"
59
+ WECHAT = "3"
60
+
61
+
62
+ @dataclass
63
+ class LoginDTO(DataClassJSONMixin):
64
+ """Request to login.
65
+
66
+ This is used by the following POST endpoints:
67
+ /api/official/user/account/login/equipment
68
+ /api/official/user/account/login/new
69
+ """
70
+
71
+ account: str
72
+ """User account (must be an email address)."""
73
+
74
+ password: str
75
+ """Hashed password. Schema: SHA256(MD5(pwd) + randomCode)) or MD5(pwd + randomCode)."""
76
+
77
+ timestamp: str
78
+ """Client timestamp in milliseconds."""
79
+
80
+ login_method: LoginMethod = field(metadata=field_options(alias="loginMethod"))
81
+ """Login method."""
82
+
83
+ language: str = LANGUAGE
84
+ """Language code."""
85
+
86
+ country_code: int | None = field(
87
+ metadata=field_options(alias="countryCode"), default=None
88
+ )
89
+ """Country code."""
90
+
91
+ browser: str = BROWSER
92
+ """Browser name (user agent info)."""
93
+
94
+ equipment: Equipment = field(
95
+ metadata=field_options(deserialize=_deserialize_int_equipment),
96
+ default=Equipment.WEB,
97
+ )
98
+ """Device type."""
99
+
100
+ equipment_no: str | None = field(
101
+ metadata=field_options(alias="equipmentNo"), default=None
102
+ )
103
+ """Device serial number (SN12345678) or other client identifier (WEB)."""
104
+
105
+ class Config(BaseConfig):
106
+ serialize_by_alias = True
107
+
108
+
109
+ @dataclass(kw_only=True)
110
+ class LoginVO(BaseResponse):
111
+ """Response from login endpoints."""
112
+
113
+ token: str
114
+ """JWT Access Token."""
115
+
116
+ counts: str
117
+ """Error count."""
118
+
119
+ user_name: str | None = field(
120
+ metadata=field_options(alias="userName"), default=None
121
+ )
122
+ """User nickname."""
123
+
124
+ is_bind: str = field(metadata=field_options(alias="isBind"), default="N")
125
+ """Is account bound."""
126
+
127
+ is_bind_equipment: str = field(
128
+ metadata=field_options(alias="isBindEquipment"), default="N"
129
+ )
130
+ """Is device bound (Terminal only)."""
131
+
132
+ sold_out_count: int = field(metadata=field_options(alias="soldOutCount"), default=0)
133
+ """Logout count."""
134
+
135
+
136
+ @dataclass
137
+ class RandomCodeDTO(DataClassJSONMixin):
138
+ """Request to get a random code.
139
+
140
+ This is used by the following POST endpoint:
141
+ /api/official/user/query/random/code
142
+ """
143
+
144
+ account: str
145
+ """User account (must be an email address)."""
146
+
147
+ country_code: int | None = field(
148
+ metadata=field_options(alias="countryCode"), default=None
149
+ )
150
+ """Country code."""
151
+
152
+ version: str | None = None
153
+ """Version."""
154
+
155
+ class Config(BaseConfig):
156
+ serialize_by_alias = True
157
+
158
+
159
+ @dataclass(kw_only=True)
160
+ class RandomCodeVO(BaseResponse):
161
+ """Response from random code endpoint."""
162
+
163
+ random_code: str = field(metadata=field_options(alias="randomCode"), default="")
164
+ """Server-side nonce (salt) used for password hashing."""
165
+
166
+ timestamp: str = ""
167
+ """Client timestamp in milliseconds."""
168
+
169
+
170
+ @dataclass
171
+ class QueryTokenDTO(DataClassJSONMixin):
172
+ """Request to token endpoint.
173
+
174
+ This is used by the following POST endpoint:
175
+ /api/user/query/token
176
+ """
177
+
178
+
179
+ @dataclass(kw_only=True)
180
+ class QueryTokenVO(BaseResponse):
181
+ """Response from token endpoint."""
182
+
183
+ token: str | None = None
184
+ """JWT Access Token."""
185
+
186
+ class Config(BaseConfig):
187
+ serialize_by_alias = True
188
+ omit_none = False
189
+ code_generation_options = ["TO_DICT_ADD_OMIT_NONE_FLAG"]
190
+
191
+
192
+ class OSType(str, BaseEnum):
193
+ """OS Type."""
194
+
195
+ WINDOWS = "WINDOWS"
196
+ MACOS = "MACOS"
197
+ LINUX = "LINUX"
198
+ ANDROID = "ANDROID"
199
+ IOS = "IOS"
200
+
201
+
202
+ @dataclass
203
+ class UserVO(DataClassJSONMixin):
204
+ """User profile VO."""
205
+
206
+ user_name: str | None = field(
207
+ metadata=field_options(alias="userName"), default=None
208
+ )
209
+ email: str | None = None
210
+ phone: str | None = None
211
+ country_code: str | None = field(
212
+ metadata=field_options(alias="countryCode"), default=None
213
+ )
214
+ total_capacity: str = field(
215
+ metadata=field_options(alias="totalCapacity"), default="0"
216
+ )
217
+ file_server: str = field(metadata=field_options(alias="fileServer"), default="0")
218
+ avatars_url: str | None = field(
219
+ metadata=field_options(alias="avatarsUrl"), default=None
220
+ )
221
+ birthday: str | None = None
222
+ sex: str | None = None
223
+
224
+ class Config(BaseConfig):
225
+ serialize_by_alias = True
226
+ omit_none = True
227
+ code_generation_options = ["TO_DICT_ADD_OMIT_NONE_FLAG"]
228
+
229
+
230
+ @dataclass
231
+ class UserQueryByIdVO(BaseResponse):
232
+ """User query response."""
233
+
234
+ user: UserVO | None = None
235
+ is_user: bool = field(metadata=field_options(alias="isUser"), default=False)
236
+ equipment_no: str | None = field(
237
+ metadata=field_options(alias="equipmentNo"), default=None
238
+ )
239
+
240
+
241
+ @dataclass
242
+ class SmsLoginDTO(DataClassJSONMixin):
243
+ """Request to login via sms.
244
+
245
+ This is used by the following POST endpoint:
246
+ /api/user/sms/login
247
+ """
248
+
249
+ valid_code: str = field(metadata=field_options(alias="validCode"))
250
+ """SMS/Email verification code."""
251
+
252
+ valid_code_key: str = field(metadata=field_options(alias="validCodeKey"))
253
+ """Redis session key for the code (e.g., '{email}_validCode')."""
254
+
255
+ country_code: int = field(
256
+ metadata=field_options(alias="countryCode"), default=COUNTRY_CODE
257
+ )
258
+
259
+ telephone: str | None = None
260
+ """User phone number."""
261
+
262
+ timestamp: str | None = None
263
+ """Client timestamp in milliseconds."""
264
+
265
+ email: str | None = None
266
+ """User email."""
267
+
268
+ browser: str = BROWSER
269
+ """Browser name (user agent info)."""
270
+
271
+ equipment: Equipment = Equipment.WEB
272
+ """Device type."""
273
+
274
+ devices: OSType | None = OSType.WINDOWS
275
+ """OS Type: WINDOWS, MACOS, LINUX, ANDROID, IOS."""
276
+
277
+ class Config(BaseConfig):
278
+ serialize_by_alias = True
279
+
280
+
281
+ @dataclass(kw_only=True)
282
+ class SmsLoginVO(BaseResponse):
283
+ """Response from access token call."""
284
+
285
+ token: str
286
+
287
+
288
+ @dataclass
289
+ class UserPreAuthRequest(DataClassJSONMixin):
290
+ """Request for pre-auth.
291
+
292
+ This is used by the following POST endpoint:
293
+ /api/user/validcode/pre-auth
294
+ """
295
+
296
+ account: str
297
+
298
+
299
+ @dataclass
300
+ class UserPreAuthResponse(BaseResponse):
301
+ """Response from pre-auth."""
302
+
303
+ token: str = ""
304
+
305
+
306
+ @dataclass
307
+ class SendSmsDTO(DataClassJSONMixin):
308
+ """Request to send SMS code.
309
+
310
+ This is used by the following POST endpoint:
311
+ /api/user/sms/validcode/send
312
+ """
313
+
314
+ telephone: str
315
+ """User phone number."""
316
+
317
+ timestamp: str
318
+ """Client timestamp in milliseconds."""
319
+
320
+ token: str
321
+ """JWT Access Token."""
322
+
323
+ sign: str
324
+ """JWT Signature."""
325
+
326
+ extend: str | None = None
327
+ """JWT Extension."""
328
+
329
+ nationcode: int = field(
330
+ metadata=field_options(alias="nationcode"), default=COUNTRY_CODE
331
+ )
332
+ """Country code."""
333
+
334
+ class Config(BaseConfig):
335
+ serialize_by_alias = True
336
+
337
+
338
+ @dataclass
339
+ class SendSmsVO(BaseResponse):
340
+ """Response from send SMS."""
341
+
342
+ valid_code_key: str = field(
343
+ metadata=field_options(alias="validCodeKey"), default=""
344
+ )
345
+
346
+
347
+ @dataclass
348
+ class EmailDTO(DataClassJSONMixin):
349
+ """Request to send email code.
350
+
351
+ Used by:
352
+ /api/user/mail/validcode (POST)
353
+ """
354
+
355
+ email: str
356
+ """User email."""
357
+
358
+ language: str | None = None
359
+ """Language code."""
360
+
361
+ class Config(BaseConfig):
362
+ serialize_by_alias = True
363
+
364
+
365
+ @dataclass
366
+ class ValidCodeDTO(DataClassJSONMixin):
367
+ """Request to check validation code.
368
+
369
+ Used by:
370
+ /api/user/check/validcode (POST)
371
+ """
372
+
373
+ valid_code_key: str = field(metadata=field_options(alias="validCodeKey"))
374
+ """Key for the validation code."""
375
+
376
+ valid_code: str = field(metadata=field_options(alias="validCode"))
377
+ """The validation code."""
378
+
379
+ class Config(BaseConfig):
380
+ serialize_by_alias = True
@@ -0,0 +1,266 @@
1
+ """Module for API base classes."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from enum import Enum
5
+ from typing import Any, Self
6
+
7
+ from mashumaro import field_options
8
+ from mashumaro.config import BaseConfig
9
+ from mashumaro.mixins.json import DataClassJSONMixin
10
+
11
+
12
+ @dataclass
13
+ class BaseResponse(DataClassJSONMixin):
14
+ """Base response class."""
15
+
16
+ success: bool = True
17
+ """Whether the request was successful."""
18
+
19
+ error_code: str | None = field(
20
+ metadata=field_options(alias="errorCode"), default=None
21
+ )
22
+ """Error code."""
23
+
24
+ error_msg: str | None = field(
25
+ metadata=field_options(alias="errorMsg"), default=None
26
+ )
27
+ """Error message."""
28
+
29
+ class Config(BaseConfig):
30
+ serialize_by_alias = True
31
+ omit_none = True
32
+
33
+
34
+ class BaseEnum(Enum):
35
+ """Base enum class."""
36
+
37
+ @classmethod
38
+ def from_value(cls, value: int) -> Self:
39
+ for member in cls:
40
+ if member.value == value:
41
+ return member
42
+ raise ValueError(f"Invalid {cls.__name__} value: {value}")
43
+
44
+
45
+ class BooleanEnum(str, BaseEnum):
46
+ """Boolean enum."""
47
+
48
+ YES = "Y"
49
+ NO = "N"
50
+
51
+ @classmethod
52
+ def of(cls, value: bool) -> Self:
53
+ return cls.YES if value else cls.NO
54
+
55
+
56
+ class ProcessingStatus(str, BaseEnum):
57
+ """Processing status for system tasks."""
58
+
59
+ PENDING = "PENDING"
60
+ PROCESSING = "PROCESSING"
61
+ COMPLETED = "COMPLETED"
62
+ FAILED = "FAILED"
63
+ NONE = "NONE" # Used in view aggregation
64
+
65
+
66
+ @dataclass
67
+ class CommonList(BaseResponse):
68
+ """Common list response class."""
69
+
70
+ total: int = 0
71
+ """Total count of items."""
72
+
73
+ pages: int = 0
74
+ """Total pages."""
75
+
76
+ size: int = field(metadata=field_options(alias="size"), default=20)
77
+ """Current page size."""
78
+
79
+ vo_list: list[Any] = field(
80
+ metadata=field_options(alias="voList"), default_factory=list
81
+ )
82
+ """List of items."""
83
+
84
+ class Config(BaseConfig):
85
+ serialize_by_alias = True
86
+ omit_none = True
87
+
88
+
89
+ class ErrorCode(str, Enum):
90
+ """API Error Codes."""
91
+
92
+ description: str
93
+
94
+ def __new__(cls, code: str, message: str) -> "ErrorCode":
95
+ obj = str.__new__(cls, code)
96
+ obj._value_ = code
97
+ setattr(obj, "description", message)
98
+ return obj
99
+
100
+ SUCCESS = ("200", "Success")
101
+ BAD_REQUEST = ("400", "Bad Request")
102
+ UNAUTHORIZED = ("401", "Unauthorized")
103
+ FORBIDDEN = ("403", "Forbidden")
104
+ NOT_FOUND = ("404", "Not Found")
105
+ CONFLICT = ("409", "Conflict")
106
+ INTERNAL_ERROR = ("500", "Internal Server Error")
107
+
108
+ # Specific Error Codes from legacy system
109
+ OPERATION_FAILED = ("E0701", "Operation failed!")
110
+ DELETION_FAILED = ("E0702", "Deletion failed!")
111
+ DELETE_CHILDREN_FIRST = (
112
+ "E0703",
113
+ "Please delete the child nodes first before deleting the current node!",
114
+ )
115
+ ID_EMPTY = ("E0704", "ID cannot be empty!")
116
+ MODIFICATION_FAILED = ("E0705", "Modification failed!")
117
+ SYSTEM_ERROR = ("E0706", "System error!")
118
+ ROLE_DELETION_FAILED_USERS_EXIST = (
119
+ "E0707",
120
+ "There are still users under this role, deletion is not allowed!",
121
+ )
122
+ INVALID_CREDENTIALS = ("E0708", "Incorrect username or password!")
123
+ USER_DISABLED = (
124
+ "E0709",
125
+ "The current user is in a disabled state. Please contact the administrator!",
126
+ )
127
+ USER_LOCKED = (
128
+ "E0710",
129
+ "The user is locked. Please contact the administrator or try logging in later!",
130
+ )
131
+ LOGIN_ATTEMPTS_REMAINING = (
132
+ "E0711",
133
+ "Incorrect username or password. Remaining login attempts",
134
+ )
135
+ LOGIN_EXPIRED = (
136
+ "E0712",
137
+ "You are not logged in or your login has expired. Please log in again!",
138
+ )
139
+ PASSWORD_RECENTLY_USED = (
140
+ "E0713",
141
+ "The password cannot be the same as the recent ones!",
142
+ )
143
+ ORIGINAL_PASSWORD_INCORRECT = (
144
+ "E0714",
145
+ "The original password entered is incorrect!",
146
+ )
147
+ ENABLEMENT_FAILED = ("E0715", "Enablement failed!")
148
+ CANNOT_ENABLE_SELF = ("E0716", "A user cannot enable themselves!")
149
+ CANNOT_DISABLE_SELF = ("E0717", "A user cannot disable themselves!")
150
+ DISABLE_FAILED = ("E0718", "Disabling failed!")
151
+ USER_HAS_RECORDS_CANNOT_DELETE = (
152
+ "E0719",
153
+ "This user already has operation records and cannot be deleted!",
154
+ )
155
+ CANNOT_DELETE_SELF = ("E0720", "A user cannot delete themselves!")
156
+ ONLY_LOCKED_CAN_UNLOCK = ("E0721", "Only locked users can be unlocked!")
157
+ USER_INFO_NOT_FOUND = ("E0722", "No information found for this user!")
158
+ CANNOT_AUTHORIZE_SELF = ("E0723", "A user cannot authorize themselves!")
159
+ AUTHORIZATION_FAILED = ("E0724", "Authorization failed!")
160
+ TASK_DISABLE_FAILED = ("E0725", "Scheduled task disabling failed!")
161
+ TASK_ENABLE_FAILED = ("E0726", "Scheduled task enabling failed!")
162
+ TASK_RUNNING = ("E0727", "The task is running!")
163
+ QUOTA_EXCEEDED = ("E0728", "Data cleanup exception!")
164
+ TASK_EXECUTION_EXCEPTION = (
165
+ "E0729",
166
+ "Scheduled task execution exception. Please stop the task and restart it!",
167
+ )
168
+ DUPLICATE_BUSINESS_CODE = (
169
+ "E0730",
170
+ "Identical codes are not allowed under the same business code!",
171
+ )
172
+ PARAMETER_EXISTS = ("E0731", "The parameter already exists!")
173
+ ALREADY_ENABLED = ("E0732", "Normal users are not allowed to be enabled again!")
174
+ USER_ALREADY_EXISTS = ("E0733", "The user already exists!")
175
+ ALREADY_DISABLED = ("E0734", "Disabled users are not allowed to be disabled again!")
176
+ CANNOT_UNLOCK_SELF = ("E0735", "A user cannot unlock themselves!")
177
+ ALL_ENABLED = (
178
+ "E0736",
179
+ "All data is in the enabled state. Please select a task that is not enabled!",
180
+ )
181
+ ALL_DISABLED = (
182
+ "E0737",
183
+ "All data is in the disabled state. Please select a task that is not disabled!",
184
+ )
185
+ ENABLE_TASK_FIRST = ("E0738", "Please enable the task first!")
186
+ REQUEST_DATA_EMPTY = ("E0739", "The request data is empty!")
187
+ ROLE_DELETION_FAILED_CHILDREN_EXIST = (
188
+ "E0740",
189
+ "Please delete all child roles under this role first!",
190
+ )
191
+ USER_DELETION_FAILED_CHILDREN_EXIST = (
192
+ "E0741",
193
+ "Please delete all child users under this user first!",
194
+ )
195
+ SUPERIOR_RESOURCES_MISMATCH = (
196
+ "E0742",
197
+ "The system does not match the superior resources!",
198
+ )
199
+ ACCOUNT_CANCELLED = ("E0061", "The account has been cancelled!")
200
+ PHONE_EMPTY = ("E0062", "The phone number is empty!")
201
+ TOO_MANY_SMS = ("E0064", "Too many SMS messages have been sent!")
202
+ FAILED_TO_SEND_SMS = ("E0065", "Failed to send SMS!")
203
+ INVALID_PHONE_FORMAT = ("E0066", "The phone number format is incorrect!")
204
+ AVATAR_UPLOAD_FAILED = ("E0067", "Failed to upload the avatar!")
205
+ COPY_LIMIT_EXCEEDED = ("E0068", "The number of copied files exceeds the limit!")
206
+ INVALID_DEVICE = ("E0069", "The device is invalid!")
207
+ NO_NEED_TO_UPDATE = ("E0070", "No need to update!")
208
+ NO_COMPRESSED_PACKAGE = ("E0071", "There is no compressed package!")
209
+ ACCESS_DENIED_SYSTEM = (
210
+ "E0072",
211
+ "No operations are allowed under the supernote directory!",
212
+ )
213
+ NICKNAME_EMPTY = ("E0073", "The nickname cannot be empty!")
214
+ NICKNAME_EXISTS = ("E0074", "The nickname already exists. Please choose a new one!")
215
+ DEVICE_ALREADY_BOUND = (
216
+ "E0075",
217
+ "The device is already bound to this account. No need to bind again!",
218
+ )
219
+ BINDING_ACCOUNT_MISMATCH = (
220
+ "E0077",
221
+ "The logged-in account is not the same as the one bound to the device!",
222
+ )
223
+ ANOTHER_DEVICE_SYNCING = (
224
+ "E0078",
225
+ "A device is currently synchronizing. Please wait until it's finished before synchronizing again!",
226
+ )
227
+ SYNC_IN_PROGRESS = (
228
+ "E0079",
229
+ "Synchronization is in progress. Please wait until it's finished before performing other operations!",
230
+ )
231
+ PATH_NOT_FOUND = ("E0081", "The path does not exist!")
232
+ HASH_EXITS = (
233
+ "E0082",
234
+ "There is a file with the same MD5 value. No need to upload!",
235
+ )
236
+ DEVICE_BOUND_OTHER_ACCOUNT = (
237
+ "E0083",
238
+ "The device is already bound to another account. It cannot be bound to a new account!",
239
+ )
240
+ INVALID_VERSION_NUMBER = ("E0084", "The published version number is incorrect!")
241
+ INVALID_TOKEN = ("E0085", "The token is invalid!")
242
+ COUNTRY_CODE_EMPTY = ("E0086", "The country code is empty!")
243
+ NO_LATEST_VERSION = ("E0087", "There is no latest version. No need to update.")
244
+ RESOURCE_IN_USE_BY_ROLE = (
245
+ "E0088",
246
+ "This resource is already in use by a role and cannot be deleted",
247
+ )
248
+ CONFLICT_EXISTS = ("E0322", "File or directory already exists!")
249
+ TIMEZONE_NOT_OBTAINED = (
250
+ "E0844",
251
+ "The time zone information for this area was not obtained",
252
+ )
253
+
254
+ @property
255
+ def message(self) -> str:
256
+ """Get the default message for the error code."""
257
+ return str(getattr(self, "description", "An error occurred"))
258
+
259
+
260
+ def create_error_response(
261
+ error_msg: str, error_code: str | ErrorCode | None = None
262
+ ) -> BaseResponse:
263
+ """Create an error response."""
264
+ if isinstance(error_code, ErrorCode):
265
+ error_code = error_code.value
266
+ return BaseResponse(success=False, error_code=error_code, error_msg=error_msg)