supernote 0.6.2__tar.gz → 0.8.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.
- {supernote-0.6.2 → supernote-0.8.0}/PKG-INFO +41 -54
- {supernote-0.6.2 → supernote-0.8.0}/README.md +35 -53
- {supernote-0.6.2 → supernote-0.8.0}/pyproject.toml +6 -1
- {supernote-0.6.2 → supernote-0.8.0}/supernote/__init__.py +4 -2
- supernote-0.8.0/supernote/cli/admin.py +177 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/cli/client.py +77 -42
- {supernote-0.6.2 → supernote-0.8.0}/supernote/cli/main.py +11 -12
- supernote-0.8.0/supernote/cli/server.py +42 -0
- supernote-0.8.0/supernote/client/admin.py +76 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/client/auth.py +30 -18
- supernote-0.8.0/supernote/client/client.py +357 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/client/cloud_client.py +19 -19
- supernote-0.8.0/supernote/client/device.py +309 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/client/exceptions.py +8 -0
- supernote-0.8.0/supernote/client/hashing.py +50 -0
- supernote-0.8.0/supernote/client/login_client.py +154 -0
- supernote-0.8.0/supernote/client/schedule.py +149 -0
- supernote-0.8.0/supernote/client/web.py +208 -0
- supernote-0.8.0/supernote/models/__init__.py +14 -0
- supernote-0.8.0/supernote/models/auth.py +380 -0
- supernote-0.8.0/supernote/models/base.py +256 -0
- supernote-0.8.0/supernote/models/equipment.py +276 -0
- supernote-0.8.0/supernote/models/file_common.py +94 -0
- supernote-0.8.0/supernote/models/file_device.py +505 -0
- supernote-0.8.0/supernote/models/file_web.py +602 -0
- supernote-0.8.0/supernote/models/schedule.py +663 -0
- supernote-0.8.0/supernote/models/summary.py +591 -0
- supernote-0.8.0/supernote/models/system.py +433 -0
- supernote-0.8.0/supernote/models/user.py +638 -0
- supernote-0.8.0/supernote/server/app.py +270 -0
- supernote-0.8.0/supernote/server/config.py +124 -0
- supernote-0.8.0/supernote/server/constants.py +19 -0
- supernote-0.8.0/supernote/server/db/__init__.py +9 -0
- supernote-0.8.0/supernote/server/db/base.py +8 -0
- supernote-0.8.0/supernote/server/db/models/__init__.py +12 -0
- supernote-0.8.0/supernote/server/db/models/device.py +17 -0
- supernote-0.8.0/supernote/server/db/models/file.py +104 -0
- supernote-0.8.0/supernote/server/db/models/kv.py +17 -0
- supernote-0.8.0/supernote/server/db/models/login_record.py +22 -0
- supernote-0.8.0/supernote/server/db/models/schedule.py +83 -0
- supernote-0.8.0/supernote/server/db/models/user.py +30 -0
- supernote-0.8.0/supernote/server/db/session.py +88 -0
- supernote-0.8.0/supernote/server/exceptions.py +140 -0
- supernote-0.8.0/supernote/server/routes/admin.py +109 -0
- supernote-0.8.0/supernote/server/routes/auth.py +336 -0
- supernote-0.8.0/supernote/server/routes/file_device.py +515 -0
- supernote-0.8.0/supernote/server/routes/file_web.py +597 -0
- supernote-0.8.0/supernote/server/routes/oss.py +275 -0
- supernote-0.8.0/supernote/server/routes/schedule.py +223 -0
- supernote-0.8.0/supernote/server/routes/system.py +57 -0
- supernote-0.8.0/supernote/server/services/__init__.py +15 -0
- supernote-0.8.0/supernote/server/services/blob.py +147 -0
- supernote-0.8.0/supernote/server/services/coordination.py +176 -0
- supernote-0.8.0/supernote/server/services/file.py +954 -0
- supernote-0.8.0/supernote/server/services/integrity.py +91 -0
- supernote-0.8.0/supernote/server/services/schedule.py +161 -0
- supernote-0.8.0/supernote/server/services/user.py +475 -0
- supernote-0.8.0/supernote/server/services/vfs.py +468 -0
- supernote-0.8.0/supernote/server/utils/hashing.py +13 -0
- supernote-0.8.0/supernote/server/utils/rate_limit.py +52 -0
- supernote-0.8.0/supernote/server/utils/unique_id.py +48 -0
- supernote-0.8.0/supernote/server/utils/url_signer.py +193 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote.egg-info/PKG-INFO +41 -54
- supernote-0.8.0/supernote.egg-info/SOURCES.txt +84 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote.egg-info/requires.txt +5 -0
- supernote-0.6.2/supernote/cli/server.py +0 -137
- supernote-0.6.2/supernote/client/client.py +0 -184
- supernote-0.6.2/supernote/client/login_client.py +0 -174
- supernote-0.6.2/supernote/server/app.py +0 -143
- supernote-0.6.2/supernote/server/config.py +0 -90
- supernote-0.6.2/supernote/server/models/__init__.py +0 -1
- supernote-0.6.2/supernote/server/models/auth.py +0 -139
- supernote-0.6.2/supernote/server/models/base.py +0 -30
- supernote-0.6.2/supernote/server/models/file.py +0 -361
- supernote-0.6.2/supernote/server/routes/auth.py +0 -155
- supernote-0.6.2/supernote/server/routes/file.py +0 -476
- supernote-0.6.2/supernote/server/routes/system.py +0 -33
- supernote-0.6.2/supernote/server/services/__init__.py +0 -3
- supernote-0.6.2/supernote/server/services/file.py +0 -347
- supernote-0.6.2/supernote/server/services/state.py +0 -107
- supernote-0.6.2/supernote/server/services/storage.py +0 -323
- supernote-0.6.2/supernote/server/services/user.py +0 -156
- supernote-0.6.2/supernote.egg-info/SOURCES.txt +0 -48
- {supernote-0.6.2 → supernote-0.8.0}/LICENSE +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/setup.cfg +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/cli/__init__.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/cli/notebook.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/client/__init__.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/client/api_model.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/__init__.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/color.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/converter.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/decoder.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/exceptions.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/fileformat.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/manipulator.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/parser.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/notebook/utils.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/py.typed +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/server/__init__.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote/server/routes/decorators.py +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote.egg-info/dependency_links.txt +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote.egg-info/entry_points.txt +0 -0
- {supernote-0.6.2 → supernote-0.8.0}/supernote.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: supernote
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.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
|
|
@@ -24,6 +24,11 @@ Requires-Dist: aiohttp>=3.13.2; extra == "server"
|
|
|
24
24
|
Requires-Dist: mashumaro>=3.17; extra == "server"
|
|
25
25
|
Requires-Dist: pyyaml>=6.0; extra == "server"
|
|
26
26
|
Requires-Dist: PyJWT>=2.10.1; extra == "server"
|
|
27
|
+
Requires-Dist: SQLAlchemy[asyncio]>=2.0.0; extra == "server"
|
|
28
|
+
Requires-Dist: alembic>=1.13.0; extra == "server"
|
|
29
|
+
Requires-Dist: aiosqlite>=0.19.0; extra == "server"
|
|
30
|
+
Requires-Dist: aiofiles>=25.1.0; extra == "server"
|
|
31
|
+
Requires-Dist: aiohttp-remotes>=1.3.0; extra == "server"
|
|
27
32
|
Provides-Extra: all
|
|
28
33
|
Requires-Dist: supernote[client,server]; extra == "all"
|
|
29
34
|
Dynamic: license-file
|
|
@@ -38,7 +43,7 @@ This content is shared between the [documentation](https://allenporter.github.io
|
|
|
38
43
|
|
|
39
44
|
- **Notebook Parsing**: Convert `.note` files to PDF, PNG, SVG, or text
|
|
40
45
|
- **Private Server**: Self-hosted Supernote Private Cloud implementation
|
|
41
|
-
- **Client**: Interact with Supernote
|
|
46
|
+
- **Client**: Interact with Supernote (Private) Cloud API
|
|
42
47
|
|
|
43
48
|
## Installation
|
|
44
49
|
|
|
@@ -53,16 +58,9 @@ pip install supernote[client] # + Client
|
|
|
53
58
|
pip install supernote[all]
|
|
54
59
|
```
|
|
55
60
|
|
|
56
|
-
## Local Development
|
|
61
|
+
## Local Development Setup
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
uv venv --python=3.14
|
|
62
|
-
source .venv/bin/activate
|
|
63
|
-
uv pip install -r requirements_dev.txt
|
|
64
|
-
uv pip install -e ".[all]"
|
|
65
|
-
```
|
|
63
|
+
To set up the project for development, please refer to the [Contributing Guide](docs/CONTRIBUTING.md).
|
|
66
64
|
|
|
67
65
|
## Quick Start
|
|
68
66
|
|
|
@@ -79,38 +77,37 @@ The notebook parser is a fork and slightly lighter dependency version of [supern
|
|
|
79
77
|
limitation. All credit goes to the original authors of [supernote-tool](https://github.com/jya-dev/supernote-tool) for providing an amazing low level utility.
|
|
80
78
|
|
|
81
79
|
|
|
82
|
-
###
|
|
80
|
+
### Server Setup & Bootstrap
|
|
83
81
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
supernote
|
|
82
|
+
1. **Start the Server**:
|
|
83
|
+
```bash
|
|
84
|
+
supernote serve
|
|
85
|
+
```
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
2. **Register the Admin**:
|
|
88
|
+
The first user registered becomes the system administrator.
|
|
89
|
+
```bash
|
|
90
|
+
supernote admin user add email@example.com --url http://localhost:8080
|
|
91
|
+
```
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
3. **Login to CLI**:
|
|
94
|
+
```bash
|
|
95
|
+
supernote cloud-login email@example.com --url http://localhost:8080
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
See the [Bootstrap Guide](docs/bootstrap_guide.md) for detailed deployment and security instructions.
|
|
94
99
|
|
|
95
100
|
### Run with Docker
|
|
96
101
|
|
|
97
102
|
```bash
|
|
98
103
|
# Build image
|
|
99
|
-
docker build -t supernote
|
|
100
|
-
|
|
101
|
-
# Generate initial configuration
|
|
102
|
-
mkdir config
|
|
103
|
-
docker run --rm supernote-server supernote-server config init > config/config.yaml
|
|
104
|
-
docker run --rm -it supernote-server supernote-server user add alice >> config/users.yaml
|
|
104
|
+
docker build -t supernote .
|
|
105
105
|
|
|
106
106
|
# Run server
|
|
107
|
-
docker run -d -p 8080:8080
|
|
108
|
-
-v $(pwd)/config:/config \
|
|
109
|
-
-v $(pwd)/storage:/data \
|
|
110
|
-
supernote-server
|
|
107
|
+
docker run -d -p 8080:8080 -v $(pwd)/storage:/storage supernote serve
|
|
111
108
|
```
|
|
112
109
|
|
|
113
|
-
See [Server Documentation](supernote/server/README.
|
|
110
|
+
See [Server Documentation](https://github.com/allenporter/supernote-lite/blob/main/supernote/server/README.mdd) for details.
|
|
114
111
|
|
|
115
112
|
|
|
116
113
|
### Access Supernote Services
|
|
@@ -128,36 +125,26 @@ async with SupernoteClient.from_credentials(email, password) as client:
|
|
|
128
125
|
|
|
129
126
|
```bash
|
|
130
127
|
# Notebook operations
|
|
131
|
-
supernote convert input.note output.pdf
|
|
132
|
-
supernote analyze input.note
|
|
128
|
+
supernote notebook convert input.note output.pdf
|
|
129
|
+
supernote notebook analyze input.note
|
|
133
130
|
|
|
134
131
|
# Server operations
|
|
135
|
-
supernote
|
|
136
|
-
supernote
|
|
137
|
-
|
|
132
|
+
supernote serve
|
|
133
|
+
supernote admin user list
|
|
134
|
+
supernote admin user add email@example.com
|
|
138
135
|
|
|
139
136
|
# Client operations
|
|
140
|
-
supernote
|
|
141
|
-
supernote
|
|
137
|
+
supernote cloud-login --url http://localhost:8080 email@example.com
|
|
138
|
+
supernote cloud-ls
|
|
142
139
|
```
|
|
143
140
|
|
|
144
|
-
##
|
|
145
|
-
|
|
146
|
-
This package is designed for:
|
|
147
|
-
1. **Server operators** - Self-hosting Supernote Private Cloud
|
|
148
|
-
2. **Developers** - Integrating Supernote into applications
|
|
149
|
-
3. **Reference** - Understanding Supernote protocols
|
|
150
|
-
|
|
151
|
-
See [ARCHITECTURE.md](supernote/server/ARCHITECTURE.md) for protocol details.
|
|
141
|
+
## Contributing
|
|
152
142
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
uv pip install -r requirements_dev.txt
|
|
159
|
-
uv pip install -e ".[all]"
|
|
160
|
-
```
|
|
143
|
+
We welcome contributions! Please see our [Contributing Guide](docs/CONTRIBUTING.md) for details on:
|
|
144
|
+
- Local development setup
|
|
145
|
+
- Project architecture
|
|
146
|
+
- Coding standards
|
|
147
|
+
- Running tests
|
|
161
148
|
|
|
162
149
|
## Credits
|
|
163
150
|
|
|
@@ -8,7 +8,7 @@ This content is shared between the [documentation](https://allenporter.github.io
|
|
|
8
8
|
|
|
9
9
|
- **Notebook Parsing**: Convert `.note` files to PDF, PNG, SVG, or text
|
|
10
10
|
- **Private Server**: Self-hosted Supernote Private Cloud implementation
|
|
11
|
-
- **Client**: Interact with Supernote
|
|
11
|
+
- **Client**: Interact with Supernote (Private) Cloud API
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -23,16 +23,9 @@ pip install supernote[client] # + Client
|
|
|
23
23
|
pip install supernote[all]
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
## Local Development
|
|
26
|
+
## Local Development Setup
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
uv venv --python=3.14
|
|
32
|
-
source .venv/bin/activate
|
|
33
|
-
uv pip install -r requirements_dev.txt
|
|
34
|
-
uv pip install -e ".[all]"
|
|
35
|
-
```
|
|
28
|
+
To set up the project for development, please refer to the [Contributing Guide](docs/CONTRIBUTING.md).
|
|
36
29
|
|
|
37
30
|
## Quick Start
|
|
38
31
|
|
|
@@ -49,38 +42,37 @@ The notebook parser is a fork and slightly lighter dependency version of [supern
|
|
|
49
42
|
limitation. All credit goes to the original authors of [supernote-tool](https://github.com/jya-dev/supernote-tool) for providing an amazing low level utility.
|
|
50
43
|
|
|
51
44
|
|
|
52
|
-
###
|
|
45
|
+
### Server Setup & Bootstrap
|
|
53
46
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
supernote
|
|
47
|
+
1. **Start the Server**:
|
|
48
|
+
```bash
|
|
49
|
+
supernote serve
|
|
50
|
+
```
|
|
57
51
|
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
2. **Register the Admin**:
|
|
53
|
+
The first user registered becomes the system administrator.
|
|
54
|
+
```bash
|
|
55
|
+
supernote admin user add email@example.com --url http://localhost:8080
|
|
56
|
+
```
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
3. **Login to CLI**:
|
|
59
|
+
```bash
|
|
60
|
+
supernote cloud-login email@example.com --url http://localhost:8080
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
See the [Bootstrap Guide](docs/bootstrap_guide.md) for detailed deployment and security instructions.
|
|
64
64
|
|
|
65
65
|
### Run with Docker
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
68
|
# Build image
|
|
69
|
-
docker build -t supernote
|
|
70
|
-
|
|
71
|
-
# Generate initial configuration
|
|
72
|
-
mkdir config
|
|
73
|
-
docker run --rm supernote-server supernote-server config init > config/config.yaml
|
|
74
|
-
docker run --rm -it supernote-server supernote-server user add alice >> config/users.yaml
|
|
69
|
+
docker build -t supernote .
|
|
75
70
|
|
|
76
71
|
# Run server
|
|
77
|
-
docker run -d -p 8080:8080
|
|
78
|
-
-v $(pwd)/config:/config \
|
|
79
|
-
-v $(pwd)/storage:/data \
|
|
80
|
-
supernote-server
|
|
72
|
+
docker run -d -p 8080:8080 -v $(pwd)/storage:/storage supernote serve
|
|
81
73
|
```
|
|
82
74
|
|
|
83
|
-
See [Server Documentation](supernote/server/README.
|
|
75
|
+
See [Server Documentation](https://github.com/allenporter/supernote-lite/blob/main/supernote/server/README.mdd) for details.
|
|
84
76
|
|
|
85
77
|
|
|
86
78
|
### Access Supernote Services
|
|
@@ -98,36 +90,26 @@ async with SupernoteClient.from_credentials(email, password) as client:
|
|
|
98
90
|
|
|
99
91
|
```bash
|
|
100
92
|
# Notebook operations
|
|
101
|
-
supernote convert input.note output.pdf
|
|
102
|
-
supernote analyze input.note
|
|
93
|
+
supernote notebook convert input.note output.pdf
|
|
94
|
+
supernote notebook analyze input.note
|
|
103
95
|
|
|
104
96
|
# Server operations
|
|
105
|
-
supernote
|
|
106
|
-
supernote
|
|
107
|
-
|
|
97
|
+
supernote serve
|
|
98
|
+
supernote admin user list
|
|
99
|
+
supernote admin user add email@example.com
|
|
108
100
|
|
|
109
101
|
# Client operations
|
|
110
|
-
supernote
|
|
111
|
-
supernote
|
|
102
|
+
supernote cloud-login --url http://localhost:8080 email@example.com
|
|
103
|
+
supernote cloud-ls
|
|
112
104
|
```
|
|
113
105
|
|
|
114
|
-
##
|
|
115
|
-
|
|
116
|
-
This package is designed for:
|
|
117
|
-
1. **Server operators** - Self-hosting Supernote Private Cloud
|
|
118
|
-
2. **Developers** - Integrating Supernote into applications
|
|
119
|
-
3. **Reference** - Understanding Supernote protocols
|
|
120
|
-
|
|
121
|
-
See [ARCHITECTURE.md](supernote/server/ARCHITECTURE.md) for protocol details.
|
|
106
|
+
## Contributing
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
uv pip install -r requirements_dev.txt
|
|
129
|
-
uv pip install -e ".[all]"
|
|
130
|
-
```
|
|
108
|
+
We welcome contributions! Please see our [Contributing Guide](docs/CONTRIBUTING.md) for details on:
|
|
109
|
+
- Local development setup
|
|
110
|
+
- Project architecture
|
|
111
|
+
- Coding standards
|
|
112
|
+
- Running tests
|
|
131
113
|
|
|
132
114
|
## Credits
|
|
133
115
|
|
|
@@ -4,7 +4,7 @@ requires = ["setuptools>=77.0"]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "supernote"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.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"
|
|
@@ -37,6 +37,11 @@ server = [
|
|
|
37
37
|
"mashumaro>=3.17",
|
|
38
38
|
"pyyaml>=6.0",
|
|
39
39
|
"PyJWT>=2.10.1",
|
|
40
|
+
"SQLAlchemy[asyncio]>=2.0.0",
|
|
41
|
+
"alembic>=1.13.0",
|
|
42
|
+
"aiosqlite>=0.19.0",
|
|
43
|
+
"aiofiles>=25.1.0",
|
|
44
|
+
"aiohttp-remotes>=1.3.0",
|
|
40
45
|
]
|
|
41
46
|
all = ["supernote[client,server]"]
|
|
42
47
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
# Core notebook parsing (always available)
|
|
6
|
-
from . import notebook
|
|
6
|
+
from . import notebook # noqa: F401
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
|
-
"
|
|
9
|
+
"models",
|
|
10
10
|
]
|
|
11
11
|
|
|
12
12
|
# Optional: Client library
|
|
@@ -24,3 +24,5 @@ try:
|
|
|
24
24
|
__all__.extend(["server"])
|
|
25
25
|
except ImportError:
|
|
26
26
|
pass
|
|
27
|
+
|
|
28
|
+
__all__.extend(["notebook"])
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Admin CLI commands."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import getpass
|
|
5
|
+
import hashlib
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from supernote.client.admin import AdminClient
|
|
9
|
+
from supernote.client.exceptions import ApiException
|
|
10
|
+
|
|
11
|
+
from .client import create_client
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def add_user_async(
|
|
15
|
+
url: str, email: str, password: str, display_name: str | None = None
|
|
16
|
+
):
|
|
17
|
+
"""Async implementation of add user."""
|
|
18
|
+
async with create_client(url) as client:
|
|
19
|
+
print(f"Attempting to register user '{email}' on {client.host}...")
|
|
20
|
+
|
|
21
|
+
admin_client = AdminClient(client)
|
|
22
|
+
|
|
23
|
+
# Hash password
|
|
24
|
+
password_md5 = hashlib.md5(password.encode()).hexdigest()
|
|
25
|
+
|
|
26
|
+
# Try Public Registration
|
|
27
|
+
try:
|
|
28
|
+
await admin_client.register(
|
|
29
|
+
email=email,
|
|
30
|
+
password=password_md5,
|
|
31
|
+
username=display_name,
|
|
32
|
+
)
|
|
33
|
+
print("Success! User created (Public Registration).")
|
|
34
|
+
return
|
|
35
|
+
except ApiException:
|
|
36
|
+
# If failed, we don't have detailed status code in simple try/except unless we check exception type
|
|
37
|
+
# But client raises ApiException
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# Try Admin Creation API
|
|
41
|
+
print("Public registration failed or disabled. Attempting Admin creation...")
|
|
42
|
+
try:
|
|
43
|
+
await admin_client.admin_create_user(
|
|
44
|
+
email=email,
|
|
45
|
+
password=password_md5,
|
|
46
|
+
username=display_name,
|
|
47
|
+
)
|
|
48
|
+
print("Success! User created (Admin API).")
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"Admin creation failed: {e}")
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def list_users_async():
|
|
55
|
+
"""Async implementation of list users."""
|
|
56
|
+
async with create_client() as client:
|
|
57
|
+
try:
|
|
58
|
+
users = await client.get_json("/api/admin/users", list)
|
|
59
|
+
|
|
60
|
+
print(f"\nTotal Users: {len(users)}\n")
|
|
61
|
+
print(f"{'Username':<30} {'Email':<30} {'Capacity':<10}")
|
|
62
|
+
print("-" * 75)
|
|
63
|
+
for u in users:
|
|
64
|
+
# users is a list of dicts if we passed list to get_json, or we need a VO
|
|
65
|
+
# client.get_json returns data_cls.from_json(result)
|
|
66
|
+
# But list doesn't have from_json.
|
|
67
|
+
# We should probably use raw get or define a List[UserVO] type if we can.
|
|
68
|
+
# For now let's just use raw request to get json list safely.
|
|
69
|
+
pass
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
# Re-doing list fetch with raw request because of typing limitation in client.get_json for list[dict]
|
|
74
|
+
try:
|
|
75
|
+
resp = await client.get("/api/admin/users")
|
|
76
|
+
users = await resp.json()
|
|
77
|
+
|
|
78
|
+
print(f"\nTotal Users: {len(users)}\n")
|
|
79
|
+
print(f"{'Email':<30} {'Name':<20} {'Capacity':<10}")
|
|
80
|
+
print("-" * 65)
|
|
81
|
+
for u in users:
|
|
82
|
+
print(
|
|
83
|
+
f"{u.get('email', 'N/A'):<30} {u.get('userName', 'N/A'):<20} {u.get('totalCapacity', '0'):<10}"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
print(f"Failed to list users: {e}")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def add_user(args):
|
|
91
|
+
password = args.password
|
|
92
|
+
if not password:
|
|
93
|
+
password = getpass.getpass(f"Password for {args.email}: ")
|
|
94
|
+
|
|
95
|
+
display_name = args.name or args.email.split("@")[0]
|
|
96
|
+
asyncio.run(add_user_async(args.url, args.email, password, display_name))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def list_users(args):
|
|
100
|
+
asyncio.run(list_users_async())
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
async def reset_password_async(url: str, email: str, password: str):
|
|
104
|
+
"""Async implementation of password reset."""
|
|
105
|
+
async with create_client(url) as client:
|
|
106
|
+
print(f"Attempting to reset password for '{email}' on {client.host}...")
|
|
107
|
+
admin_client = AdminClient(client)
|
|
108
|
+
|
|
109
|
+
password_md5 = hashlib.md5(password.encode()).hexdigest()
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
await admin_client.admin_reset_password(email, password_md5)
|
|
113
|
+
print("Success! Password Reset.")
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"Failed to reset password: {e}")
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def reset_password(args):
|
|
120
|
+
password = args.password
|
|
121
|
+
if not password:
|
|
122
|
+
password = getpass.getpass(f"New password for {args.email}: ")
|
|
123
|
+
confirm = getpass.getpass("Confirm password: ")
|
|
124
|
+
if password != confirm:
|
|
125
|
+
print("Passwords do not match.")
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
|
|
128
|
+
asyncio.run(reset_password_async(args.url, args.email, password))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def add_parser(subparsers):
|
|
132
|
+
# 'admin' parent command
|
|
133
|
+
parser_admin = subparsers.add_parser(
|
|
134
|
+
"admin",
|
|
135
|
+
help="Administrative commands for Supernote Server",
|
|
136
|
+
)
|
|
137
|
+
parser_admin.add_argument("--url", type=str, help="URL of the Supernote Server")
|
|
138
|
+
|
|
139
|
+
admin_subparsers = parser_admin.add_subparsers(dest="admin_command")
|
|
140
|
+
|
|
141
|
+
# --- User Management ---
|
|
142
|
+
parser_user = admin_subparsers.add_parser("user", help="User management commands")
|
|
143
|
+
user_subparsers = parser_user.add_subparsers(dest="user_command")
|
|
144
|
+
|
|
145
|
+
# user list
|
|
146
|
+
parser_user_list = user_subparsers.add_parser(
|
|
147
|
+
"list",
|
|
148
|
+
help="List all users",
|
|
149
|
+
)
|
|
150
|
+
parser_user_list.set_defaults(func=list_users)
|
|
151
|
+
|
|
152
|
+
# user add
|
|
153
|
+
parser_user_add = user_subparsers.add_parser(
|
|
154
|
+
"add",
|
|
155
|
+
help="Add a new user",
|
|
156
|
+
)
|
|
157
|
+
parser_user_add.add_argument(
|
|
158
|
+
"email", type=str, help="Email address for the new user"
|
|
159
|
+
)
|
|
160
|
+
parser_user_add.add_argument(
|
|
161
|
+
"--password", type=str, help="Password (if omitted, prompt interactively)"
|
|
162
|
+
)
|
|
163
|
+
parser_user_add.add_argument(
|
|
164
|
+
"--name", type=str, help="Display name (optional)", default=None
|
|
165
|
+
)
|
|
166
|
+
parser_user_add.set_defaults(func=add_user)
|
|
167
|
+
|
|
168
|
+
# user reset-password
|
|
169
|
+
parser_user_reset = user_subparsers.add_parser(
|
|
170
|
+
"reset-password",
|
|
171
|
+
help="Reset user password",
|
|
172
|
+
)
|
|
173
|
+
parser_user_reset.add_argument("email", type=str, help="Email address of the user")
|
|
174
|
+
parser_user_reset.add_argument(
|
|
175
|
+
"--password", type=str, help="New password (if omitted, prompt interactively)"
|
|
176
|
+
)
|
|
177
|
+
parser_user_reset.set_defaults(func=reset_password)
|