devpi-admin 1.0.1__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. devpi_admin-1.1.0/PKG-INFO +228 -0
  2. devpi_admin-1.1.0/README.md +207 -0
  3. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/_version.py +2 -2
  4. devpi_admin-1.1.0/src/devpi_admin/main.py +326 -0
  5. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/static/css/style.css +94 -14
  6. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/static/js/app.js +486 -248
  7. devpi_admin-1.1.0/tests/test_cached_versions.py +91 -0
  8. devpi_admin-1.1.0/tests/test_helpers.py +101 -0
  9. devpi_admin-1.1.0/tests/test_json_safe.py +74 -0
  10. devpi_admin-1.0.1/PKG-INFO +0 -163
  11. devpi_admin-1.0.1/README.md +0 -142
  12. devpi_admin-1.0.1/src/devpi_admin/main.py +0 -72
  13. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/.gitignore +0 -0
  14. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/LICENSE +0 -0
  15. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/pyproject.toml +0 -0
  16. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/__init__.py +0 -0
  17. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/static/index.html +0 -0
  18. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/static/js/api.js +0 -0
  19. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/static/js/marked.min.js +0 -0
  20. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/src/devpi_admin/static/js/theme.js +0 -0
  21. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/tests/__init__.py +0 -0
  22. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/tests/test_hooks.py +0 -0
  23. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/tests/test_package.py +0 -0
  24. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/tests/test_tween.py +0 -0
  25. {devpi_admin-1.0.1 → devpi_admin-1.1.0}/tests/test_wants_html.py +0 -0
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: devpi-admin
3
+ Version: 1.1.0
4
+ Summary: Modern web UI plugin for devpi-server — drop-in replacement for devpi-web
5
+ Author-email: Pavel Revak <pavelrevak@gmail.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: admin,devpi,pypi,ui,web
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Framework :: Pyramid
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Classifier: Topic :: System :: Software Distribution
18
+ Requires-Python: >=3.9
19
+ Requires-Dist: devpi-server>=6.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # devpi-admin
23
+
24
+ A modern web UI plugin for [devpi-server](https://devpi.net/) — a drop-in replacement for
25
+ `devpi-web`. Ships as a Python package that registers itself as a devpi-server plugin via the
26
+ standard entry point mechanism, so a single `pip install devpi-admin` is enough.
27
+
28
+ The UI itself is a bundled single-page application (pure HTML + CSS + vanilla JavaScript, no
29
+ build step) served under `/+admin/`. All devpi REST API endpoints remain untouched — the SPA
30
+ talks to the standard devpi JSON API directly.
31
+
32
+ ## Features
33
+
34
+ ### Dashboard
35
+ - Server info with version of devpi-server and all installed plugins (auto-detected)
36
+ - Cache metrics with hit-rate bars (storage, changelog, relpath caches)
37
+ - Whoosh search index queue status
38
+
39
+ ### Indexes
40
+ - Visual cards color-coded by type: green (stage), amber (volatile stage), blue (mirror)
41
+ - `pip install` command with copy-to-clipboard (click to copy, green flash feedback)
42
+ - `pip.conf` toggle — switch between short form and full `--index-url` / `--trusted-host`
43
+ - `pip.conf` generator — download a ready-to-use config per index
44
+ - Create / edit / delete indexes via modal dialogs
45
+ - `bases` editor with drag & drop priority ordering and transitive inheritance display
46
+ - `acl_upload` tag picker with user selection dropdown
47
+ - `volatile`, `mirror_url`, `title` configuration
48
+
49
+ ### Users
50
+ - Create, edit (email, password), delete users (admin only)
51
+
52
+ ### Packages
53
+ - Client-side search with PEP 503 name normalization
54
+ - Mirror indexes: shows only cached packages (fast filesystem scan, no 17 MB index download);
55
+ "Download full index" button available for complete browse
56
+ - Package cards with latest version and `pip install` command
57
+
58
+ ### Package detail (PyPI-like layout)
59
+ - **Sidebar**: metadata (author, license, Python version, keywords, platform, maintainer,
60
+ extras, project URLs, dependencies), `pip install` command, file downloads with upload dates
61
+ - **Version list**: cached versions shown normally, uncached versions link to pypi.org (↗);
62
+ "Load all versions" button for mirrors
63
+ - **README**: rendered markdown (via `marked.js`); fetched from PyPI.org for mirror packages
64
+ where devpi doesn't cache the description
65
+
66
+ ### General
67
+ - **Anonymous browsing** — visitors can explore public indexes without logging in; admin
68
+ actions (create/edit/delete) appear only after authentication
69
+ - **Dark / light / auto theme** with half-circle icon for auto mode
70
+ - **Responsive mobile menu** with hamburger toggle
71
+ - **ESC + outside-click** dismissal for modals, dropdown menus, mobile menu
72
+ - **Login via modal** — no separate login page
73
+
74
+ ## Plugin API endpoints
75
+
76
+ In addition to serving the SPA, `devpi-admin` registers custom API endpoints under
77
+ `/+admin-api/` for features that the standard devpi REST API doesn't provide efficiently:
78
+
79
+ | Endpoint | Method | Description |
80
+ |----------|--------|-------------|
81
+ | `/+admin-api/cached/{user}/{index}` | GET | List cached package names for a mirror index (filesystem scan) |
82
+ | `/+admin-api/versions/{user}/{index}/{project}` | GET | Version list with cached/uncached distinction |
83
+ | `/+admin-api/versions/{user}/{index}/{project}?all=1` | GET | Include all upstream versions (mirrors) |
84
+ | `/+admin-api/versiondata/{user}/{index}/{project}/{version}` | GET | Metadata + file links for a single version |
85
+
86
+ ## Installation
87
+
88
+ ```bash
89
+ pip install devpi-admin
90
+ ```
91
+
92
+ This pulls in `devpi-server` as a dependency. If you are using devpi in a dedicated venv
93
+ (recommended), install the plugin into the same venv:
94
+
95
+ ```bash
96
+ /var/lib/pypi/venv/bin/pip install devpi-admin
97
+ systemctl --user restart devpi # or however you run devpi-server
98
+ ```
99
+
100
+ You should uninstall `devpi-web` — `devpi-admin` replaces it entirely:
101
+
102
+ ```bash
103
+ pip uninstall devpi-web
104
+ ```
105
+
106
+ Both plugins can technically coexist but it is not recommended. `devpi-admin` intercepts `/`
107
+ for HTML requests while `devpi-web` would still serve its own HTML on other routes like
108
+ `/<user>/<index>/<package>`, leading to a confusing mixed experience.
109
+
110
+ ## Usage
111
+
112
+ After restart, open:
113
+
114
+ ```
115
+ http://<your-devpi-host>:3141/
116
+ ```
117
+
118
+ Browser visits to `/` are redirected to `/+admin/`, which serves the SPA. Direct links like
119
+ `http://<host>:3141/+admin/#packages/ci/testing` work and can be bookmarked.
120
+
121
+ devpi CLI tools and other JSON clients are unaffected — they send `Accept: application/json`
122
+ and bypass the redirect.
123
+
124
+ ## How it works
125
+
126
+ `devpi-admin` registers a `devpi_server` entry point that hooks into
127
+ `devpiserver_pyramid_configure` (with `@hookimpl` from pluggy) to:
128
+
129
+ 1. Serve the bundled static assets under `/+admin/` via a Pyramid static view.
130
+ 2. Add an explicit view at `/+admin/` that returns `index.html`.
131
+ 3. Register custom API views under `/+admin-api/` for cached-package and per-version queries.
132
+ 4. Install a tween that redirects HTML browser requests on `/` to `/+admin/` while leaving
133
+ JSON requests intact.
134
+
135
+ The plugin uses devpi-server internals (`xom.model.getstage`, `stage.list_versions`,
136
+ `stage.get_versiondata`, `stage.get_releaselinks`) and direct filesystem access
137
+ (`serverdir/+files/`) for the cached-packages API.
138
+
139
+ ## Requirements
140
+
141
+ - Python 3.9+
142
+ - devpi-server 6.0+
143
+ - A browser with ES6 support (`Promise`, `fetch`, `sessionStorage`)
144
+
145
+ ## Routes (UI)
146
+
147
+ Routing is hash-based, so any of these URLs can be bookmarked or shared:
148
+
149
+ | Hash | View |
150
+ |------|------|
151
+ | `#` | Status dashboard (default) |
152
+ | `#indexes` | All indexes |
153
+ | `#indexes/<user>` | Indexes filtered by user |
154
+ | `#packages/<user>/<index>` | Packages in an index |
155
+ | `#package/<user>/<index>/<name>` | Package detail (latest cached version) |
156
+ | `#package/<user>/<index>/<name>?version=<ver>` | Specific version |
157
+ | `#users` | User management (requires login) |
158
+
159
+ ## Project layout
160
+
161
+ ```
162
+ devpi-admin/
163
+ ├── pyproject.toml
164
+ ├── README.md
165
+ ├── LICENSE
166
+ ├── .github/workflows/
167
+ │ ├── tests.yml — CI on push/PR (Python 3.10 + 3.14)
168
+ │ └── publish.yml — publish to PyPI on release
169
+ ├── src/
170
+ │ └── devpi_admin/
171
+ │ ├── __init__.py — version (from git tag via hatch-vcs)
172
+ │ ├── main.py — Pyramid hooks, tween, API views
173
+ │ └── static/
174
+ │ ├── index.html — SPA entry point
175
+ │ ├── css/style.css
176
+ │ └── js/
177
+ │ ├── api.js — devpi REST wrapper + auth
178
+ │ ├── theme.js — theme toggle (light/dark/auto)
179
+ │ ├── marked.min.js — vendored markdown renderer
180
+ │ └── app.js — routing, views, rendering
181
+ └── tests/
182
+ ├── test_cached_versions.py — filesystem scan (tmpdir)
183
+ ├── test_helpers.py — filename parsing, normalization
184
+ ├── test_hooks.py — pluggy hook registration
185
+ ├── test_json_safe.py — readonly view conversion
186
+ ├── test_package.py — entry point, static files
187
+ ├── test_tween.py — redirect behavior
188
+ └── test_wants_html.py — Accept header heuristic
189
+ ```
190
+
191
+ ## Development
192
+
193
+ ```bash
194
+ git clone <repo>
195
+ cd devpi-admin
196
+ python -m venv .venv
197
+ .venv/bin/pip install -e .
198
+ ```
199
+
200
+ The static files live at `src/devpi_admin/static/` and can be edited in place — changes
201
+ show up on the next browser reload, no restart of devpi-server required (static views
202
+ read from disk on each request). Python changes (`main.py`) require a devpi-server restart.
203
+
204
+ Run the unit tests:
205
+
206
+ ```bash
207
+ PYTHONWARNINGS="ignore::UserWarning" python -m unittest discover -v tests/
208
+ ```
209
+
210
+ (The `PYTHONWARNINGS` shim hides an unrelated deprecation warning emitted by Pyramid 2.1
211
+ when it imports `pkg_resources`.)
212
+
213
+ ## Releasing
214
+
215
+ Version is derived from the git tag via `hatch-vcs`. To release:
216
+
217
+ 1. `git tag v0.1.0 && git push --tags`
218
+ 2. On GitHub: Releases → Draft new release → select tag → Publish
219
+ 3. The `publish.yml` workflow runs tests, builds wheel+sdist, and uploads to PyPI via trusted
220
+ publishing (no API tokens needed — configure the GitHub environment `pypi` in PyPI settings).
221
+
222
+ ## Author
223
+
224
+ Pavel Revak <pavelrevak@gmail.com>
225
+
226
+ ## License
227
+
228
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,207 @@
1
+ # devpi-admin
2
+
3
+ A modern web UI plugin for [devpi-server](https://devpi.net/) — a drop-in replacement for
4
+ `devpi-web`. Ships as a Python package that registers itself as a devpi-server plugin via the
5
+ standard entry point mechanism, so a single `pip install devpi-admin` is enough.
6
+
7
+ The UI itself is a bundled single-page application (pure HTML + CSS + vanilla JavaScript, no
8
+ build step) served under `/+admin/`. All devpi REST API endpoints remain untouched — the SPA
9
+ talks to the standard devpi JSON API directly.
10
+
11
+ ## Features
12
+
13
+ ### Dashboard
14
+ - Server info with version of devpi-server and all installed plugins (auto-detected)
15
+ - Cache metrics with hit-rate bars (storage, changelog, relpath caches)
16
+ - Whoosh search index queue status
17
+
18
+ ### Indexes
19
+ - Visual cards color-coded by type: green (stage), amber (volatile stage), blue (mirror)
20
+ - `pip install` command with copy-to-clipboard (click to copy, green flash feedback)
21
+ - `pip.conf` toggle — switch between short form and full `--index-url` / `--trusted-host`
22
+ - `pip.conf` generator — download a ready-to-use config per index
23
+ - Create / edit / delete indexes via modal dialogs
24
+ - `bases` editor with drag & drop priority ordering and transitive inheritance display
25
+ - `acl_upload` tag picker with user selection dropdown
26
+ - `volatile`, `mirror_url`, `title` configuration
27
+
28
+ ### Users
29
+ - Create, edit (email, password), delete users (admin only)
30
+
31
+ ### Packages
32
+ - Client-side search with PEP 503 name normalization
33
+ - Mirror indexes: shows only cached packages (fast filesystem scan, no 17 MB index download);
34
+ "Download full index" button available for complete browse
35
+ - Package cards with latest version and `pip install` command
36
+
37
+ ### Package detail (PyPI-like layout)
38
+ - **Sidebar**: metadata (author, license, Python version, keywords, platform, maintainer,
39
+ extras, project URLs, dependencies), `pip install` command, file downloads with upload dates
40
+ - **Version list**: cached versions shown normally, uncached versions link to pypi.org (↗);
41
+ "Load all versions" button for mirrors
42
+ - **README**: rendered markdown (via `marked.js`); fetched from PyPI.org for mirror packages
43
+ where devpi doesn't cache the description
44
+
45
+ ### General
46
+ - **Anonymous browsing** — visitors can explore public indexes without logging in; admin
47
+ actions (create/edit/delete) appear only after authentication
48
+ - **Dark / light / auto theme** with half-circle icon for auto mode
49
+ - **Responsive mobile menu** with hamburger toggle
50
+ - **ESC + outside-click** dismissal for modals, dropdown menus, mobile menu
51
+ - **Login via modal** — no separate login page
52
+
53
+ ## Plugin API endpoints
54
+
55
+ In addition to serving the SPA, `devpi-admin` registers custom API endpoints under
56
+ `/+admin-api/` for features that the standard devpi REST API doesn't provide efficiently:
57
+
58
+ | Endpoint | Method | Description |
59
+ |----------|--------|-------------|
60
+ | `/+admin-api/cached/{user}/{index}` | GET | List cached package names for a mirror index (filesystem scan) |
61
+ | `/+admin-api/versions/{user}/{index}/{project}` | GET | Version list with cached/uncached distinction |
62
+ | `/+admin-api/versions/{user}/{index}/{project}?all=1` | GET | Include all upstream versions (mirrors) |
63
+ | `/+admin-api/versiondata/{user}/{index}/{project}/{version}` | GET | Metadata + file links for a single version |
64
+
65
+ ## Installation
66
+
67
+ ```bash
68
+ pip install devpi-admin
69
+ ```
70
+
71
+ This pulls in `devpi-server` as a dependency. If you are using devpi in a dedicated venv
72
+ (recommended), install the plugin into the same venv:
73
+
74
+ ```bash
75
+ /var/lib/pypi/venv/bin/pip install devpi-admin
76
+ systemctl --user restart devpi # or however you run devpi-server
77
+ ```
78
+
79
+ You should uninstall `devpi-web` — `devpi-admin` replaces it entirely:
80
+
81
+ ```bash
82
+ pip uninstall devpi-web
83
+ ```
84
+
85
+ Both plugins can technically coexist but it is not recommended. `devpi-admin` intercepts `/`
86
+ for HTML requests while `devpi-web` would still serve its own HTML on other routes like
87
+ `/<user>/<index>/<package>`, leading to a confusing mixed experience.
88
+
89
+ ## Usage
90
+
91
+ After restart, open:
92
+
93
+ ```
94
+ http://<your-devpi-host>:3141/
95
+ ```
96
+
97
+ Browser visits to `/` are redirected to `/+admin/`, which serves the SPA. Direct links like
98
+ `http://<host>:3141/+admin/#packages/ci/testing` work and can be bookmarked.
99
+
100
+ devpi CLI tools and other JSON clients are unaffected — they send `Accept: application/json`
101
+ and bypass the redirect.
102
+
103
+ ## How it works
104
+
105
+ `devpi-admin` registers a `devpi_server` entry point that hooks into
106
+ `devpiserver_pyramid_configure` (with `@hookimpl` from pluggy) to:
107
+
108
+ 1. Serve the bundled static assets under `/+admin/` via a Pyramid static view.
109
+ 2. Add an explicit view at `/+admin/` that returns `index.html`.
110
+ 3. Register custom API views under `/+admin-api/` for cached-package and per-version queries.
111
+ 4. Install a tween that redirects HTML browser requests on `/` to `/+admin/` while leaving
112
+ JSON requests intact.
113
+
114
+ The plugin uses devpi-server internals (`xom.model.getstage`, `stage.list_versions`,
115
+ `stage.get_versiondata`, `stage.get_releaselinks`) and direct filesystem access
116
+ (`serverdir/+files/`) for the cached-packages API.
117
+
118
+ ## Requirements
119
+
120
+ - Python 3.9+
121
+ - devpi-server 6.0+
122
+ - A browser with ES6 support (`Promise`, `fetch`, `sessionStorage`)
123
+
124
+ ## Routes (UI)
125
+
126
+ Routing is hash-based, so any of these URLs can be bookmarked or shared:
127
+
128
+ | Hash | View |
129
+ |------|------|
130
+ | `#` | Status dashboard (default) |
131
+ | `#indexes` | All indexes |
132
+ | `#indexes/<user>` | Indexes filtered by user |
133
+ | `#packages/<user>/<index>` | Packages in an index |
134
+ | `#package/<user>/<index>/<name>` | Package detail (latest cached version) |
135
+ | `#package/<user>/<index>/<name>?version=<ver>` | Specific version |
136
+ | `#users` | User management (requires login) |
137
+
138
+ ## Project layout
139
+
140
+ ```
141
+ devpi-admin/
142
+ ├── pyproject.toml
143
+ ├── README.md
144
+ ├── LICENSE
145
+ ├── .github/workflows/
146
+ │ ├── tests.yml — CI on push/PR (Python 3.10 + 3.14)
147
+ │ └── publish.yml — publish to PyPI on release
148
+ ├── src/
149
+ │ └── devpi_admin/
150
+ │ ├── __init__.py — version (from git tag via hatch-vcs)
151
+ │ ├── main.py — Pyramid hooks, tween, API views
152
+ │ └── static/
153
+ │ ├── index.html — SPA entry point
154
+ │ ├── css/style.css
155
+ │ └── js/
156
+ │ ├── api.js — devpi REST wrapper + auth
157
+ │ ├── theme.js — theme toggle (light/dark/auto)
158
+ │ ├── marked.min.js — vendored markdown renderer
159
+ │ └── app.js — routing, views, rendering
160
+ └── tests/
161
+ ├── test_cached_versions.py — filesystem scan (tmpdir)
162
+ ├── test_helpers.py — filename parsing, normalization
163
+ ├── test_hooks.py — pluggy hook registration
164
+ ├── test_json_safe.py — readonly view conversion
165
+ ├── test_package.py — entry point, static files
166
+ ├── test_tween.py — redirect behavior
167
+ └── test_wants_html.py — Accept header heuristic
168
+ ```
169
+
170
+ ## Development
171
+
172
+ ```bash
173
+ git clone <repo>
174
+ cd devpi-admin
175
+ python -m venv .venv
176
+ .venv/bin/pip install -e .
177
+ ```
178
+
179
+ The static files live at `src/devpi_admin/static/` and can be edited in place — changes
180
+ show up on the next browser reload, no restart of devpi-server required (static views
181
+ read from disk on each request). Python changes (`main.py`) require a devpi-server restart.
182
+
183
+ Run the unit tests:
184
+
185
+ ```bash
186
+ PYTHONWARNINGS="ignore::UserWarning" python -m unittest discover -v tests/
187
+ ```
188
+
189
+ (The `PYTHONWARNINGS` shim hides an unrelated deprecation warning emitted by Pyramid 2.1
190
+ when it imports `pkg_resources`.)
191
+
192
+ ## Releasing
193
+
194
+ Version is derived from the git tag via `hatch-vcs`. To release:
195
+
196
+ 1. `git tag v0.1.0 && git push --tags`
197
+ 2. On GitHub: Releases → Draft new release → select tag → Publish
198
+ 3. The `publish.yml` workflow runs tests, builds wheel+sdist, and uploads to PyPI via trusted
199
+ publishing (no API tokens needed — configure the GitHub environment `pypi` in PyPI settings).
200
+
201
+ ## Author
202
+
203
+ Pavel Revak <pavelrevak@gmail.com>
204
+
205
+ ## License
206
+
207
+ MIT — see [LICENSE](LICENSE).
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.0.1'
22
- __version_tuple__ = version_tuple = (1, 0, 1)
21
+ __version__ = version = '1.1.0'
22
+ __version_tuple__ = version_tuple = (1, 1, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None