MetaList 0.3.12__tar.gz → 0.3.13__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.
- {metalist-0.3.12/metalist.egg-info → metalist-0.3.13}/PKG-INFO +15 -2
- {metalist-0.3.12 → metalist-0.3.13}/README.md +13 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/api/middleware/auth.py +0 -4
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/auth.py +47 -198
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/notes.py +109 -44
- {metalist-0.3.12 → metalist-0.3.13}/app/config.py +1 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/db/file_session.py +2 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/migrations.py +99 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/schema.py +4 -20
- metalist-0.3.13/app/db/search_history_sql.py +77 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/version.py +1 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/encryption_audit.py +81 -17
- {metalist-0.3.12 → metalist-0.3.13}/app/main.py +20 -3
- {metalist-0.3.12 → metalist-0.3.13}/app/models/database.py +3 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/presentation/render/note_renderer.py +5 -1
- metalist-0.3.13/app/presentation/templates.py +13 -0
- metalist-0.3.13/app/security/authenticated_logging.py +179 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/auth_service.py +35 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/backup_service.py +87 -20
- {metalist-0.3.12 → metalist-0.3.13}/app/services/client_state_service.py +27 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/content_cache.py +14 -6
- {metalist-0.3.12 → metalist-0.3.13}/app/services/diagnostics.py +137 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/embedded_references.py +78 -84
- {metalist-0.3.12 → metalist-0.3.13}/app/services/encryption.py +52 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/html_export.py +0 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/services/html_unformatting.py +73 -22
- {metalist-0.3.12 → metalist-0.3.13}/app/services/markdown_rendering.py +7 -0
- metalist-0.3.13/app/services/namespace_runtime_guard.py +143 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/namespace_switcher.py +20 -15
- metalist-0.3.13/app/services/note_fullscreen.py +107 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/note_image_tags.py +8 -21
- {metalist-0.3.12 → metalist-0.3.13}/app/services/note_store.py +2 -2
- {metalist-0.3.12 → metalist-0.3.13}/app/services/root_sorting.py +36 -1
- metalist-0.3.13/app/services/search_history.py +829 -0
- metalist-0.3.13/app/services/search_history_storage.py +153 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/search_index.py +128 -123
- {metalist-0.3.12 → metalist-0.3.13}/app/services/search_query.py +70 -12
- {metalist-0.3.12 → metalist-0.3.13}/app/services/selected_text_tag.py +2 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/self_update.py +91 -8
- {metalist-0.3.12 → metalist-0.3.13}/app/services/snapshot.py +224 -117
- {metalist-0.3.12 → metalist-0.3.13}/app/services/tag_ontology.py +2 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/tag_suggestions.py +1 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/services/undo_state.py +2 -277
- {metalist-0.3.12 → metalist-0.3.13}/app/services/view_cache.py +19 -5
- {metalist-0.3.12 → metalist-0.3.13}/app/services/view_diff.py +3 -0
- metalist-0.3.13/app/startup_environment.py +55 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/startup_js_sanity.py +22 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/startup_sanity.py +253 -2
- {metalist-0.3.12 → metalist-0.3.13}/app/static/config/command_palette_tags.json +25 -5
- {metalist-0.3.12 → metalist-0.3.13}/app/static/css/main.css +623 -276
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/main.js +8 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/api-client.js +64 -27
- metalist-0.3.13/app/static/js/modules/api-failure-classification-service.js +82 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/auth.js +43 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/command-palette-controller.js +246 -16
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/endpoint-registry.js +48 -7
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/config.js +4 -2
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/context-menu/context-menu-registry.js +88 -10
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/context-menu/context-menu-service.js +44 -0
- metalist-0.3.13/app/static/js/modules/disconnect-editing-state-service.js +30 -0
- metalist-0.3.13/app/static/js/modules/elapsed-time.js +18 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/error-handler.js +32 -44
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/error-overlay.js +13 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/backup-restore-modal.js +12 -33
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/backup-result-modal.js +0 -10
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/backup-settings-modal.js +4 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/base-modal.js +58 -3
- metalist-0.3.13/app/static/js/modules/modals/confirmation-modal.js +145 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/delete-namespace-modal.js +10 -46
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/delete-namespace-validation.js +0 -15
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/help-modal.js +0 -7
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/ontology-modal.js +3 -6
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/password-modal.js +341 -79
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/random-password-modal.js +3 -9
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/reminder-modal.js +1 -5
- metalist-0.3.13/app/static/js/modules/modals/search-suggestion-statistics-modal.js +455 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/session-timeout-modal.js +1 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/sound-manager-modal.js +1 -5
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/version-info-modal.js +0 -9
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/actions/history-actions.js +32 -89
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/actions/note-actions.js +23 -9
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/actions/selection-actions.js +11 -42
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/actions/ui-actions.js +4 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/events/context-menu-events.js +80 -7
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/events/keyboard-events.js +88 -129
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/events/mouse-events.js +26 -125
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/events/search-events.js +36 -8
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/mode-context.js +15 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -7
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +16 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +27 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/differential-view-service.js +35 -2
- metalist-0.3.13/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +25 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +23 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +11 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +10 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +41 -3
- metalist-0.3.13/app/static/js/modules/mode-manager/services/mermaid-render-service.js +223 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +95 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +61 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +83 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +17 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/root-sort-service.js +5 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/search-input-service.js +13 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/search-interaction-service.js +125 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +78 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +72 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +80 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +41 -13
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/search-syntax-service.js +54 -0
- metalist-0.3.13/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +599 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +2 -1
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -2
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +12 -1
- metalist-0.3.13/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +13 -0
- metalist-0.3.13/app/static/js/modules/password-operation-response.js +27 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/password-policy.js +14 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/tag-token.js +3 -0
- metalist-0.3.13/app/static/js/vendor/mermaid-11.9.0.LICENSE.txt +21 -0
- metalist-0.3.13/app/static/js/vendor/mermaid-11.9.0.min.js +2659 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/templates/index.html +60 -29
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/add_selected_text_tag.py +8 -7
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/rename_tag.py +2 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/search_comment_autofill.py +4 -3
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/search_context_tags.py +5 -4
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/set_collapse_in_context.py +42 -65
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/split_note.py +0 -7
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/update_content.py +7 -24
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/view.py +1 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/version.py +1 -1
- {metalist-0.3.12 → metalist-0.3.13}/main.py +12 -0
- {metalist-0.3.12 → metalist-0.3.13/metalist.egg-info}/PKG-INFO +15 -2
- {metalist-0.3.12 → metalist-0.3.13}/metalist.egg-info/SOURCES.txt +24 -1
- {metalist-0.3.12 → metalist-0.3.13}/metalist.egg-info/requires.txt +1 -0
- {metalist-0.3.12 → metalist-0.3.13}/pyproject.toml +1 -0
- metalist-0.3.12/app/db/search_history_sql.py +0 -213
- metalist-0.3.12/app/presentation/templates.py +0 -22
- metalist-0.3.12/app/services/search_history.py +0 -987
- metalist-0.3.12/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -181
- metalist-0.3.12/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -207
- metalist-0.3.12/app/usecases/record_edit_mode.py +0 -45
- {metalist-0.3.12 → metalist-0.3.13}/MANIFEST.in +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/deps.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/dev.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/request_auth.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/backups.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/files.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/ontology.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/reminders.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/remote_images.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/sounds.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/routes/test.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/api/transactions.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/backup_settings_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/engine.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/file_schema.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/files_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/link_titles_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/notes_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/ontology_rules_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/reminders_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/session.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/settings_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/sounds_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/db/tab_state_sql.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/commands.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/entities.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/enums.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/linked_list.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/list_operations.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/list_traversal.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/note_crud.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/models/utils.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/presentation/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/presentation/render/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/security/encryption.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/security/http_headers.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/security/note_html.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/security/request_boundary.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/security/sensitive_logging.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/security/shell_execution.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/security/validation_errors.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/server_runtime.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/auth.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/auth_cache_state.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/backlinks.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/backup_settings_service.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/base_service.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/content_formatting.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/date_filtering.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/dependencies.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/exception_capture.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/file_registry.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/file_storage.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/hydration_state.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/image_size_formatting.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/inline_image_occurrences.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/integrity.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/latex_rendering.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/link_titles.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/login_rate_limit.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/maintenance_mode.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/namespace_deletion_jobs.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/namespace_deletion_worker.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/namespace_rename_jobs.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/namespace_rename_worker.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/note_service.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/ontology_rules_store.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/query_service.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/reminders.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/remote_image_proxy.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/runtime_hardening.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/runtime_lock.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/search_text.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/session_timeout_service.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/shell_session_service.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/sound_storage.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/store.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/sync.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/sync_state.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/tab_state.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/tag_case.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/tag_rename.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/tag_term_matching.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/test_reset.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/tokens.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/transaction_manager.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/undo_service.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/view_state.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/services/windows_process_control.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/startup_sanity_config.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/css/forms.css +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/img/favicon.ico +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/locked.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/activity-tracker.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/async-result.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/client-state-api.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/client-state-migration.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/preferences-store.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/command-palette/usage-store.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/connectivity-monitor.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/dom-utils-fixed.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/dom-utils.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/editor-commands.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/editor-selection.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/editor-toolbar.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/location-flags.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/login-namespace-picker.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/namespace-modals.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/prioritize-modal.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/date-filter-indicator-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/date-filter-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/rhs-panel-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/note-html-sanitizer.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/password-generator.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/password-strength.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/reminder-store.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/reminder-surface-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/selection-markers.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/session-auth.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/sound-service.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/modules/uuid.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/namespace-deleted.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/namespace-renamed.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/vendor/dompurify-3.4.12.LICENSE.txt +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/vendor/dompurify-3.4.12.min.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/vendor/markdown-it.min.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/note-html-policy.json +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/static/video/login-startup.mp4 +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/templates/base.html +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/templates/locked.html +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/templates/maintenance.html +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/templates/namespace_deleted.html +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/templates/namespace_renamed.html +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/templates/notes_list.html +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/alphabetize_root_notes.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/base.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/collapse.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/copy_note.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/create_child.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/create_note.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/create_sibling.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/delete_subtree.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/expand.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/indent.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/lock.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/move.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/move_to_top.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/outdent.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/paste_child.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/paste_sibling.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/prioritize.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/redo.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/reset_updated_at.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/resize_image.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/run_shell.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/set_collapse_bulk.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/toggle_reference_mode.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/toggle_todo_done.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/undo.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/usecases/unformat_content.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/utils/__init__.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/utils/encryption.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/app/utils/text_utils.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/convert-from-legacy.py +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/metalist.egg-info/dependency_links.txt +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/metalist.egg-info/entry_points.txt +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/metalist.egg-info/top_level.txt +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/scripts/generate-lan-cert.sh +0 -0
- {metalist-0.3.12 → metalist-0.3.13}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metalist
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
4
4
|
Summary: A minimalist single-user note-taking app with SSR, fast in-memory tree operations, and efficient sync.
|
|
5
5
|
Project-URL: Homepage, https://github.com/evolvingstuff/metalist
|
|
6
6
|
Project-URL: Repository, https://github.com/evolvingstuff/metalist
|
|
@@ -19,6 +19,7 @@ Requires-Dist: mutagen==1.48.1
|
|
|
19
19
|
Requires-Dist: nh3==0.3.6
|
|
20
20
|
Requires-Dist: pillow==12.3.0
|
|
21
21
|
Requires-Dist: pydantic==2.13.4
|
|
22
|
+
Requires-Dist: python-dotenv==1.2.2
|
|
22
23
|
Requires-Dist: python-multipart==0.0.32
|
|
23
24
|
Requires-Dist: starlette==1.3.1
|
|
24
25
|
Requires-Dist: tqdm==4.70.0
|
|
@@ -84,7 +85,7 @@ After the first installation, update and restart MetaList with one cross-platfor
|
|
|
84
85
|
```bash
|
|
85
86
|
metalist update
|
|
86
87
|
```
|
|
87
|
-
The updater stops running namespaces, hands off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock,
|
|
88
|
+
The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it stops running namespaces, hands off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock, installs the exact version reported by PyPI with a forced cache refresh, launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.3.13.`).
|
|
88
89
|
|
|
89
90
|
For pip, users can run `pip install metalist`. For a non-editable local install from this checkout, use `uv pip install .` or `pip install .` instead of the editable command below.
|
|
90
91
|
|
|
@@ -268,6 +269,18 @@ node --test tests/unit/*.mjs
|
|
|
268
269
|
.venv/bin/python -c "from pathlib import Path; import main; main._run_startup_sanity_gates(repo_root=Path.cwd())"
|
|
269
270
|
```
|
|
270
271
|
|
|
272
|
+
Source-checkout development launches can enable the mandatory Python and
|
|
273
|
+
JavaScript sanity gates with an ignored repository-root `.env` file:
|
|
274
|
+
|
|
275
|
+
```dotenv
|
|
276
|
+
METALIST_ENVIRONMENT=development
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`main.py` prints the resolved environment at startup. An explicit process
|
|
280
|
+
environment value takes precedence over `.env`; only `development` and
|
|
281
|
+
`production` are accepted. When the variable and `.env` file are absent,
|
|
282
|
+
MetaList runs in production mode and skips the source sanity gates.
|
|
283
|
+
|
|
271
284
|
`TEST_MODE=1` and `POST /api2/test/reset` still exist for deterministic browser automation if we decide to add a new harness later, but Cypress is not part of the current workflow.
|
|
272
285
|
|
|
273
286
|
### Diagrams
|
|
@@ -51,7 +51,7 @@ After the first installation, update and restart MetaList with one cross-platfor
|
|
|
51
51
|
```bash
|
|
52
52
|
metalist update
|
|
53
53
|
```
|
|
54
|
-
The updater stops running namespaces, hands off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock,
|
|
54
|
+
The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it stops running namespaces, hands off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock, installs the exact version reported by PyPI with a forced cache refresh, launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.3.13.`).
|
|
55
55
|
|
|
56
56
|
For pip, users can run `pip install metalist`. For a non-editable local install from this checkout, use `uv pip install .` or `pip install .` instead of the editable command below.
|
|
57
57
|
|
|
@@ -235,6 +235,18 @@ node --test tests/unit/*.mjs
|
|
|
235
235
|
.venv/bin/python -c "from pathlib import Path; import main; main._run_startup_sanity_gates(repo_root=Path.cwd())"
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
+
Source-checkout development launches can enable the mandatory Python and
|
|
239
|
+
JavaScript sanity gates with an ignored repository-root `.env` file:
|
|
240
|
+
|
|
241
|
+
```dotenv
|
|
242
|
+
METALIST_ENVIRONMENT=development
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`main.py` prints the resolved environment at startup. An explicit process
|
|
246
|
+
environment value takes precedence over `.env`; only `development` and
|
|
247
|
+
`production` are accepted. When the variable and `.env` file are absent,
|
|
248
|
+
MetaList runs in production mode and skips the source sanity gates.
|
|
249
|
+
|
|
238
250
|
`TEST_MODE=1` and `POST /api2/test/reset` still exist for deterministic browser automation if we decide to add a new harness later, but Cypress is not part of the current workflow.
|
|
239
251
|
|
|
240
252
|
### Diagrams
|
|
@@ -7,7 +7,6 @@ from app.api.request_auth import read_request_auth_token
|
|
|
7
7
|
from app.services.tokens import token_service
|
|
8
8
|
from app.services.auth_service import AuthService
|
|
9
9
|
from app.services.maintenance_mode import maintenance_service
|
|
10
|
-
from app.services.runtime_lock import purge_decrypted_runtime_state
|
|
11
10
|
from app.models.database import SafeSession
|
|
12
11
|
from app.config import API_PREFIX, TEST_MODE
|
|
13
12
|
|
|
@@ -62,9 +61,6 @@ class AuthMiddleware(BaseHTTPMiddleware):
|
|
|
62
61
|
"""Check authentication for protected routes."""
|
|
63
62
|
path = request.url.path
|
|
64
63
|
|
|
65
|
-
if not token_service.has_active_tokens():
|
|
66
|
-
purge_decrypted_runtime_state()
|
|
67
|
-
|
|
68
64
|
# Block any v1 API usage with an explicit 410 Gone (no DB access)
|
|
69
65
|
if path.startswith('/api') and not (path.startswith(API_PREFIX) or path == API_PREFIX):
|
|
70
66
|
ref = request.headers.get('referer', '-')
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import secrets
|
|
5
|
-
import sqlite3
|
|
6
4
|
from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response
|
|
7
|
-
from pydantic import BaseModel, Field
|
|
5
|
+
from pydantic import BaseModel, Field, SecretStr
|
|
8
6
|
from typing import Annotated, Optional
|
|
9
7
|
from concurrent.futures import Future, ThreadPoolExecutor
|
|
10
8
|
from threading import Lock
|
|
@@ -16,20 +14,13 @@ import time
|
|
|
16
14
|
from app.api.transactions import transactional_route
|
|
17
15
|
from app.api.deps import get_db
|
|
18
16
|
from app.config import ACTIVE_NAMESPACE
|
|
19
|
-
from app.config import KDF_ALGORITHM
|
|
20
|
-
from app.config import KDF_MAX_MEMORY_COST_KIB
|
|
21
|
-
from app.config import KDF_MAX_PARALLELISM
|
|
22
|
-
from app.config import KDF_MAX_TIME_COST
|
|
23
|
-
from app.config import KDF_MIN_MEMORY_COST_KIB
|
|
24
|
-
from app.config import KDF_MIN_PARALLELISM
|
|
25
|
-
from app.config import KDF_MIN_TIME_COST
|
|
26
17
|
from app.config import KDF_TIME_COST
|
|
27
|
-
from app.config import VAULT_VERSION
|
|
28
18
|
from app.config import VERSION
|
|
29
|
-
from app.db.
|
|
19
|
+
from app.db.migrations import CURRENT_DATABASE_VERSION
|
|
30
20
|
from app.db.settings_sql import fetch_settings
|
|
31
21
|
from app.models.database import SafeSession
|
|
32
22
|
from app.services.auth_service import AuthService
|
|
23
|
+
from app.services.auth_service import migrate_restored_database_if_unlocked
|
|
33
24
|
from app.services.backup_service import (
|
|
34
25
|
BackupFileInfo,
|
|
35
26
|
create_timestamped_backup,
|
|
@@ -70,6 +61,8 @@ from app.services.reminders import reminder_store
|
|
|
70
61
|
from app.services.search_history import search_history_store
|
|
71
62
|
from app.services.sound_storage import sound_store
|
|
72
63
|
from app.services.runtime_lock import purge_decrypted_runtime_state
|
|
64
|
+
from app.services.diagnostics import activate_authenticated_logging
|
|
65
|
+
from app.services.diagnostics import deactivate_authenticated_logging
|
|
73
66
|
from app.services.hydration_state import hydration_state
|
|
74
67
|
from app.services.file_registry import file_registry
|
|
75
68
|
from app.services.file_storage import bootstrap_file_registry
|
|
@@ -98,7 +91,7 @@ logger = logging.getLogger(__name__)
|
|
|
98
91
|
|
|
99
92
|
|
|
100
93
|
class LoginRequest(BaseModel):
|
|
101
|
-
password:
|
|
94
|
+
password: SecretStr
|
|
102
95
|
|
|
103
96
|
|
|
104
97
|
class LoginResponse(BaseModel):
|
|
@@ -143,17 +136,17 @@ class HydrationStatusResponse(BaseModel):
|
|
|
143
136
|
|
|
144
137
|
|
|
145
138
|
class PasswordCreateRequest(BaseModel):
|
|
146
|
-
password:
|
|
139
|
+
password: SecretStr
|
|
147
140
|
|
|
148
141
|
|
|
149
142
|
class PasswordChangeRequest(BaseModel):
|
|
150
|
-
current_password:
|
|
151
|
-
new_password:
|
|
143
|
+
current_password: SecretStr
|
|
144
|
+
new_password: SecretStr
|
|
152
145
|
iterations: Optional[int] = None
|
|
153
146
|
|
|
154
147
|
|
|
155
148
|
class PasswordRemoveRequest(BaseModel):
|
|
156
|
-
current_password:
|
|
149
|
+
current_password: SecretStr
|
|
157
150
|
|
|
158
151
|
|
|
159
152
|
class SessionTimeoutResponse(BaseModel):
|
|
@@ -226,6 +219,14 @@ _server_restart_lock = Lock()
|
|
|
226
219
|
_server_restart_scheduled = False
|
|
227
220
|
|
|
228
221
|
|
|
222
|
+
def _active_diagnostics_namespace() -> str:
|
|
223
|
+
if ACTIVE_NAMESPACE is None:
|
|
224
|
+
return "default"
|
|
225
|
+
if ACTIVE_NAMESPACE == "":
|
|
226
|
+
raise RuntimeError("Active namespace must not be empty")
|
|
227
|
+
return ACTIVE_NAMESPACE
|
|
228
|
+
|
|
229
|
+
|
|
229
230
|
def _reexec_server_process() -> None:
|
|
230
231
|
argv = [sys.executable, *sys.argv]
|
|
231
232
|
os.execv(sys.executable, argv)
|
|
@@ -265,156 +266,6 @@ def _namespace_target_exists(*, namespace: str) -> bool:
|
|
|
265
266
|
return False
|
|
266
267
|
|
|
267
268
|
|
|
268
|
-
def _read_namespace_auth_settings(*, namespace: str) -> dict[str, object] | None:
|
|
269
|
-
database_path = resolve_namespaced_database_path(namespace=namespace)
|
|
270
|
-
if not database_path.exists():
|
|
271
|
-
return None
|
|
272
|
-
if not database_path.is_file():
|
|
273
|
-
raise RuntimeError(f"Namespace database path is not a file: {database_path}")
|
|
274
|
-
|
|
275
|
-
connection = sqlite3.connect(database_path)
|
|
276
|
-
connection.row_factory = sqlite3.Row
|
|
277
|
-
try:
|
|
278
|
-
table_row = connection.execute(
|
|
279
|
-
"SELECT 1 FROM sqlite_master WHERE type = ? AND name = ?",
|
|
280
|
-
("table", APP_SETTINGS_TABLE),
|
|
281
|
-
).fetchone()
|
|
282
|
-
if table_row is None:
|
|
283
|
-
raise RuntimeError(f"Namespace database has no {APP_SETTINGS_TABLE} table: {namespace}")
|
|
284
|
-
row = connection.execute(
|
|
285
|
-
f"""
|
|
286
|
-
SELECT
|
|
287
|
-
auth_verifier,
|
|
288
|
-
auth_salt,
|
|
289
|
-
auth_iterations,
|
|
290
|
-
kek_iterations,
|
|
291
|
-
vault_version,
|
|
292
|
-
kdf_algorithm,
|
|
293
|
-
kdf_memory_cost_kib,
|
|
294
|
-
kdf_parallelism,
|
|
295
|
-
encryption_enabled
|
|
296
|
-
FROM {APP_SETTINGS_TABLE}
|
|
297
|
-
WHERE id = 1
|
|
298
|
-
"""
|
|
299
|
-
).fetchone()
|
|
300
|
-
if row is None:
|
|
301
|
-
raise RuntimeError(f"Namespace database has no app_settings row: {namespace}")
|
|
302
|
-
return {
|
|
303
|
-
"auth_verifier": row["auth_verifier"],
|
|
304
|
-
"auth_salt": row["auth_salt"],
|
|
305
|
-
"auth_iterations": row["auth_iterations"],
|
|
306
|
-
"kek_iterations": row["kek_iterations"],
|
|
307
|
-
"vault_version": row["vault_version"],
|
|
308
|
-
"kdf_algorithm": row["kdf_algorithm"],
|
|
309
|
-
"kdf_memory_cost_kib": row["kdf_memory_cost_kib"],
|
|
310
|
-
"kdf_parallelism": row["kdf_parallelism"],
|
|
311
|
-
"encryption_enabled": row["encryption_enabled"],
|
|
312
|
-
}
|
|
313
|
-
finally:
|
|
314
|
-
connection.close()
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
def _namespace_auth_settings_have_password(*, settings: dict[str, object] | None) -> bool:
|
|
318
|
-
if settings is None:
|
|
319
|
-
return False
|
|
320
|
-
encryption_enabled = settings["encryption_enabled"]
|
|
321
|
-
if encryption_enabled not in (0, 1, False, True):
|
|
322
|
-
raise RuntimeError(f"Invalid encryption_enabled value in namespace DB: {encryption_enabled!r}")
|
|
323
|
-
if not bool(encryption_enabled):
|
|
324
|
-
return False
|
|
325
|
-
_assert_supported_namespace_vault_profile(settings=settings)
|
|
326
|
-
if settings["auth_verifier"] is None:
|
|
327
|
-
raise RuntimeError("Namespace encryption is enabled but auth_verifier is missing")
|
|
328
|
-
if settings["auth_salt"] is None:
|
|
329
|
-
raise RuntimeError("Namespace auth_verifier is set but auth_salt is NULL")
|
|
330
|
-
if settings["auth_iterations"] is None:
|
|
331
|
-
raise RuntimeError("Namespace auth_verifier is set but auth_iterations is NULL")
|
|
332
|
-
return True
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
def _namespace_requires_password(*, namespace: str) -> bool:
|
|
336
|
-
settings = _read_namespace_auth_settings(namespace=namespace)
|
|
337
|
-
return _namespace_auth_settings_have_password(settings=settings)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def _assert_supported_namespace_vault_profile(*, settings: dict[str, object]) -> None:
|
|
341
|
-
vault_version = settings["vault_version"]
|
|
342
|
-
if vault_version is None:
|
|
343
|
-
raise RuntimeError("Namespace encryption is enabled but vault_version is NULL")
|
|
344
|
-
if vault_version != VAULT_VERSION:
|
|
345
|
-
raise RuntimeError(f"Unsupported namespace vault version: {vault_version}")
|
|
346
|
-
kdf_algorithm = settings["kdf_algorithm"]
|
|
347
|
-
if kdf_algorithm is None:
|
|
348
|
-
raise RuntimeError("Namespace encryption is enabled but kdf_algorithm is NULL")
|
|
349
|
-
if kdf_algorithm != KDF_ALGORITHM:
|
|
350
|
-
raise RuntimeError(f"Unsupported namespace kdf_algorithm: {kdf_algorithm}")
|
|
351
|
-
auth_iterations = settings["auth_iterations"]
|
|
352
|
-
if auth_iterations is None:
|
|
353
|
-
raise RuntimeError("Namespace encryption is enabled but auth_iterations is NULL")
|
|
354
|
-
if not isinstance(auth_iterations, int):
|
|
355
|
-
raise RuntimeError(f"Namespace auth_iterations is not an integer: {auth_iterations!r}")
|
|
356
|
-
if not (KDF_MIN_TIME_COST <= auth_iterations <= KDF_MAX_TIME_COST):
|
|
357
|
-
raise RuntimeError(f"Namespace auth_iterations out of range: {auth_iterations}")
|
|
358
|
-
kek_iterations = settings["kek_iterations"]
|
|
359
|
-
if kek_iterations is None:
|
|
360
|
-
raise RuntimeError("Namespace encryption is enabled but kek_iterations is NULL")
|
|
361
|
-
if not isinstance(kek_iterations, int):
|
|
362
|
-
raise RuntimeError(f"Namespace kek_iterations is not an integer: {kek_iterations!r}")
|
|
363
|
-
if not (KDF_MIN_TIME_COST <= kek_iterations <= KDF_MAX_TIME_COST):
|
|
364
|
-
raise RuntimeError(f"Namespace kek_iterations out of range: {kek_iterations}")
|
|
365
|
-
memory_cost_kib = settings["kdf_memory_cost_kib"]
|
|
366
|
-
if memory_cost_kib is None:
|
|
367
|
-
raise RuntimeError("Namespace encryption is enabled but kdf_memory_cost_kib is NULL")
|
|
368
|
-
if not isinstance(memory_cost_kib, int):
|
|
369
|
-
raise RuntimeError(f"Namespace kdf_memory_cost_kib is not an integer: {memory_cost_kib!r}")
|
|
370
|
-
if not (KDF_MIN_MEMORY_COST_KIB <= memory_cost_kib <= KDF_MAX_MEMORY_COST_KIB):
|
|
371
|
-
raise RuntimeError(f"Namespace kdf_memory_cost_kib out of range: {memory_cost_kib}")
|
|
372
|
-
parallelism = settings["kdf_parallelism"]
|
|
373
|
-
if parallelism is None:
|
|
374
|
-
raise RuntimeError("Namespace encryption is enabled but kdf_parallelism is NULL")
|
|
375
|
-
if not isinstance(parallelism, int):
|
|
376
|
-
raise RuntimeError(f"Namespace kdf_parallelism is not an integer: {parallelism!r}")
|
|
377
|
-
if not (KDF_MIN_PARALLELISM <= parallelism <= KDF_MAX_PARALLELISM):
|
|
378
|
-
raise RuntimeError(f"Namespace kdf_parallelism out of range: {parallelism}")
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
def _verify_namespace_password(*, namespace: str, password: str) -> None:
|
|
382
|
-
if not isinstance(password, str):
|
|
383
|
-
raise TypeError("namespace password must be a string")
|
|
384
|
-
settings = _read_namespace_auth_settings(namespace=namespace)
|
|
385
|
-
if not _namespace_auth_settings_have_password(settings=settings):
|
|
386
|
-
return
|
|
387
|
-
if password == "":
|
|
388
|
-
raise HTTPException(status_code=400, detail="Namespace password is required")
|
|
389
|
-
|
|
390
|
-
assert settings is not None
|
|
391
|
-
auth_salt = settings["auth_salt"]
|
|
392
|
-
auth_iterations = settings["auth_iterations"]
|
|
393
|
-
memory_cost_kib = settings["kdf_memory_cost_kib"]
|
|
394
|
-
parallelism = settings["kdf_parallelism"]
|
|
395
|
-
auth_verifier = settings["auth_verifier"]
|
|
396
|
-
if not isinstance(auth_salt, bytes):
|
|
397
|
-
raise RuntimeError("Namespace auth_salt is not bytes")
|
|
398
|
-
if not isinstance(auth_iterations, int):
|
|
399
|
-
raise RuntimeError("Namespace auth_iterations is not an integer")
|
|
400
|
-
if not isinstance(memory_cost_kib, int):
|
|
401
|
-
raise RuntimeError("Namespace kdf_memory_cost_kib is not an integer")
|
|
402
|
-
if not isinstance(parallelism, int):
|
|
403
|
-
raise RuntimeError("Namespace kdf_parallelism is not an integer")
|
|
404
|
-
if not isinstance(auth_verifier, str):
|
|
405
|
-
raise RuntimeError("Namespace auth_verifier is not a string")
|
|
406
|
-
|
|
407
|
-
candidate = EncryptionService().derive_master_key(
|
|
408
|
-
password,
|
|
409
|
-
auth_salt,
|
|
410
|
-
auth_iterations,
|
|
411
|
-
memory_cost_kib,
|
|
412
|
-
parallelism,
|
|
413
|
-
).hex()
|
|
414
|
-
if not secrets.compare_digest(candidate, auth_verifier):
|
|
415
|
-
raise HTTPException(status_code=401, detail="Invalid password")
|
|
416
|
-
|
|
417
|
-
|
|
418
269
|
def _run_hydration(*, rebuild_required: bool) -> None:
|
|
419
270
|
hydration_state.set_phase(
|
|
420
271
|
phase="database_check",
|
|
@@ -505,19 +356,27 @@ def _reset_runtime_state_after_restore() -> bool:
|
|
|
505
356
|
|
|
506
357
|
session = SafeSession()
|
|
507
358
|
try:
|
|
359
|
+
connection = session.connection()
|
|
508
360
|
with SafeSession.allow_reads("auth:backup_restore:settings"):
|
|
509
|
-
settings = fetch_settings(
|
|
361
|
+
settings = fetch_settings(connection)
|
|
510
362
|
if settings is None:
|
|
511
363
|
raise RuntimeError("App settings missing after backup restore")
|
|
512
364
|
|
|
513
365
|
password_required = bool(settings["encryption_enabled"])
|
|
514
366
|
set_encryption_required(password_required)
|
|
367
|
+
database_version = migrate_restored_database_if_unlocked(
|
|
368
|
+
connection=connection,
|
|
369
|
+
password_required=password_required,
|
|
370
|
+
)
|
|
515
371
|
|
|
516
372
|
with SafeSession.allow_reads("auth:backup_restore:runtime_stores"):
|
|
517
|
-
tab_state_store.bootstrap(connection=
|
|
518
|
-
link_title_store.bootstrap(connection=
|
|
519
|
-
reminder_store.bootstrap(connection=
|
|
520
|
-
|
|
373
|
+
tab_state_store.bootstrap(connection=connection)
|
|
374
|
+
link_title_store.bootstrap(connection=connection)
|
|
375
|
+
reminder_store.bootstrap(connection=connection)
|
|
376
|
+
if not password_required or database_version == CURRENT_DATABASE_VERSION:
|
|
377
|
+
search_history_store.bootstrap(connection=connection)
|
|
378
|
+
else:
|
|
379
|
+
search_history_store.reset()
|
|
521
380
|
|
|
522
381
|
if password_required:
|
|
523
382
|
return True
|
|
@@ -587,15 +446,6 @@ def _require_string_field(payload: dict[str, object], field_name: str) -> str:
|
|
|
587
446
|
return value
|
|
588
447
|
|
|
589
448
|
|
|
590
|
-
def _require_string_field_allow_empty(payload: dict[str, object], field_name: str) -> str:
|
|
591
|
-
if field_name not in payload:
|
|
592
|
-
raise HTTPException(status_code=400, detail=f"{field_name} is required")
|
|
593
|
-
value = payload[field_name]
|
|
594
|
-
if not isinstance(value, str):
|
|
595
|
-
raise HTTPException(status_code=400, detail=f"{field_name} must be a string")
|
|
596
|
-
return value
|
|
597
|
-
|
|
598
|
-
|
|
599
449
|
def _require_int_field(payload: dict[str, object], field_name: str) -> int:
|
|
600
450
|
if field_name not in payload:
|
|
601
451
|
raise HTTPException(status_code=400, detail=f"{field_name} is required")
|
|
@@ -634,6 +484,7 @@ def login(
|
|
|
634
484
|
tab_id: Annotated[str, Depends(_require_tab_id)],
|
|
635
485
|
db: Annotated[SafeSession, Depends(get_db)],
|
|
636
486
|
):
|
|
487
|
+
submitted_password = payload.password.get_secret_value()
|
|
637
488
|
rate_limit_key = _login_rate_limit_key(request)
|
|
638
489
|
allowed, retry_after_seconds = login_rate_limiter.check_allowed(rate_limit_key)
|
|
639
490
|
if not allowed:
|
|
@@ -645,14 +496,20 @@ def login(
|
|
|
645
496
|
auth = AuthService(db)
|
|
646
497
|
if not auth.has_password():
|
|
647
498
|
raise HTTPException(status_code=400, detail="No password is set. Please set a password first.")
|
|
648
|
-
if not auth.verify_password(
|
|
499
|
+
if not auth.verify_password(submitted_password):
|
|
649
500
|
login_rate_limiter.record_failure(rate_limit_key)
|
|
650
501
|
raise HTTPException(status_code=401, detail="Invalid password")
|
|
651
502
|
|
|
652
|
-
dek = auth.unwrap_dek_for_password(
|
|
503
|
+
dek = auth.unwrap_dek_for_password(submitted_password)
|
|
653
504
|
auth.run_authenticated_database_migrations(dek=dek)
|
|
654
505
|
|
|
655
506
|
set_session_dek(dek)
|
|
507
|
+
with SafeSession.allow_reads("auth:search_history_post_migration"):
|
|
508
|
+
search_history_store.bootstrap(connection=db.connection())
|
|
509
|
+
activate_authenticated_logging(
|
|
510
|
+
namespace=_active_diagnostics_namespace(),
|
|
511
|
+
dek=dek,
|
|
512
|
+
)
|
|
656
513
|
ensure_rules_decrypted_and_compiled(token="")
|
|
657
514
|
tab_state_store.ensure_decrypted(token="")
|
|
658
515
|
link_title_store.ensure_decrypted(token="")
|
|
@@ -679,6 +536,7 @@ def logout(
|
|
|
679
536
|
if not purge_decrypted_runtime_state():
|
|
680
537
|
clear_all_locks()
|
|
681
538
|
clear_encryption_key()
|
|
539
|
+
deactivate_authenticated_logging()
|
|
682
540
|
clear_auth_cookie(response=response)
|
|
683
541
|
return {"message": "Logout successful"}
|
|
684
542
|
|
|
@@ -726,6 +584,7 @@ def restore_from_backup(
|
|
|
726
584
|
try:
|
|
727
585
|
backup_file = restore_backup(payload.filename)
|
|
728
586
|
password_required = _reset_runtime_state_after_restore()
|
|
587
|
+
deactivate_authenticated_logging()
|
|
729
588
|
finally:
|
|
730
589
|
maintenance_service.exit_maintenance()
|
|
731
590
|
|
|
@@ -978,15 +837,8 @@ def save_namespace_ports(
|
|
|
978
837
|
def _delete_namespace_from_body(*, body: dict[str, object], target_namespace: str) -> dict[str, object]:
|
|
979
838
|
normalized_target_namespace = validate_namespace(namespace=target_namespace)
|
|
980
839
|
confirmed_namespace = _require_string_field(body, "confirmed_namespace")
|
|
981
|
-
current_password = _require_string_field_allow_empty(body, "current_password")
|
|
982
840
|
redirect_namespace = _require_string_field(body, "redirect_namespace")
|
|
983
841
|
|
|
984
|
-
if _namespace_requires_password(namespace=normalized_target_namespace):
|
|
985
|
-
_verify_namespace_password(
|
|
986
|
-
namespace=normalized_target_namespace,
|
|
987
|
-
password=current_password,
|
|
988
|
-
)
|
|
989
|
-
|
|
990
842
|
delete_capture = CapturedExceptionContext(
|
|
991
843
|
RuntimeError,
|
|
992
844
|
ValueError,
|
|
@@ -1028,9 +880,6 @@ def namespace_delete_preflight(
|
|
|
1028
880
|
namespace=_require_string_field(body, "target_namespace"),
|
|
1029
881
|
)
|
|
1030
882
|
target_exists = _namespace_target_exists(namespace=target_namespace)
|
|
1031
|
-
target_requires_password = False
|
|
1032
|
-
if target_exists:
|
|
1033
|
-
target_requires_password = _namespace_requires_password(namespace=target_namespace)
|
|
1034
883
|
catalog = build_namespace_catalog(
|
|
1035
884
|
environ=os.environ,
|
|
1036
885
|
current_namespace=ACTIVE_NAMESPACE,
|
|
@@ -1054,7 +903,6 @@ def namespace_delete_preflight(
|
|
|
1054
903
|
return {
|
|
1055
904
|
"target_namespace": target_namespace,
|
|
1056
905
|
"target_exists": target_exists,
|
|
1057
|
-
"target_requires_password": target_requires_password,
|
|
1058
906
|
"is_current_namespace": target_namespace == ACTIVE_NAMESPACE,
|
|
1059
907
|
"redirect_namespaces": redirect_namespaces,
|
|
1060
908
|
"recreates_default": recreates_default,
|
|
@@ -1199,7 +1047,7 @@ def create_password(
|
|
|
1199
1047
|
auth = AuthService(db)
|
|
1200
1048
|
if auth.has_password():
|
|
1201
1049
|
raise HTTPException(status_code=400, detail="Password already exists. Use change endpoint instead.")
|
|
1202
|
-
success, message = auth.set_password(payload.password, KDF_TIME_COST)
|
|
1050
|
+
success, message = auth.set_password(payload.password.get_secret_value(), KDF_TIME_COST)
|
|
1203
1051
|
if not success:
|
|
1204
1052
|
raise HTTPException(status_code=400, detail=message)
|
|
1205
1053
|
token_service.revoke_all_tokens()
|
|
@@ -1220,8 +1068,8 @@ def change_password(
|
|
|
1220
1068
|
else:
|
|
1221
1069
|
time_cost = KDF_TIME_COST
|
|
1222
1070
|
success, message = auth.change_password(
|
|
1223
|
-
payload.current_password,
|
|
1224
|
-
payload.new_password,
|
|
1071
|
+
payload.current_password.get_secret_value(),
|
|
1072
|
+
payload.new_password.get_secret_value(),
|
|
1225
1073
|
time_cost,
|
|
1226
1074
|
)
|
|
1227
1075
|
if not success:
|
|
@@ -1239,12 +1087,13 @@ def remove_password(
|
|
|
1239
1087
|
token: Annotated[str, Depends(_require_auth)],
|
|
1240
1088
|
):
|
|
1241
1089
|
auth = AuthService(db)
|
|
1242
|
-
success, message = auth.remove_password(payload.current_password)
|
|
1090
|
+
success, message = auth.remove_password(payload.current_password.get_secret_value())
|
|
1243
1091
|
if not success:
|
|
1244
1092
|
raise HTTPException(status_code=400, detail=message)
|
|
1245
1093
|
token_service.revoke_all_tokens()
|
|
1246
1094
|
clear_all_locks()
|
|
1247
1095
|
clear_encryption_key()
|
|
1096
|
+
deactivate_authenticated_logging()
|
|
1248
1097
|
return {"message": message}
|
|
1249
1098
|
|
|
1250
1099
|
|