mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-22 01:52:17 -05:00
Wires the client to the auth API and adds the two screens that let someone sign in or claim an unclaimed instance. The load-bearing property is that when the server reports authentication is disabled, the store answers true to every capability getter. No call site has to know whether auth is on, and the default configuration renders exactly as it did before any of this existed. http.ts All six verb helpers now send credentials, and the four that carry a body attach the CSRF token from the readable companion cookie. ensureOk routes 401 to a sign-out and 403 to a toast, except under /auth/ — a rejected sign-in is an answer, not a sign-out, and routing it globally would make a typo'd password look like an expired session. The handlers are registered by the store rather than imported here, because the store already imports this module and the reverse would be a cycle. search.ts Moved onto getJson. It was the only hand-rolled fetch left in the app, so with auth on it would have sent no cookie and silently returned nothing. Its one caller already guards with isAbortError; the thrown type widens from Error to HttpError, which carries status. live.ts onclose ignored the close code entirely, so an auth rejection would have been retried on the backoff loop forever against a server that can never accept it. 4401 and 4403 now stop the socket and hand off to the store. Subscribers are kept, so the new rearm() brings every view back after signing in without re-subscribing — deliberately unlike teardown(), which drops the map entry. This only works because the backend accepts the handshake before closing; a pre-accept close never delivers its code. +layout.svelte The data effect is split. Live-sync no longer starts until the user can actually read, since otherwise it opens sockets the server closes on sight. Nothing renders at all until auth state is known, because showing the app and then replacing it flashes an interface at someone who cannot use it. Auth screens swap in place of the shell rather than redirecting to a route, which keeps the deep link the user arrived on. Screens are components rather than routes for that reason. They are the first real form in this codebase — everything else is change-on-blur auto-save — so PasswordInput, the checkbox and the submit button are new. They use a real <form> for Enter-to-submit and password-manager support, carry proper autocomplete attributes, and keep every control at the 44px tap target the mobile a11y audit enforces. 36 auth.* strings added to the English locale only; every other locale belongs to Weblate. Also exempts S105/S106 in tests. Those rules flag string literals that look like passwords, and authentication tests have to name the credentials they sign in with; the list already exempts S101 for the same reason. Verified: svelte-check reports 0 errors and 0 warnings across 1321 files, eslint is clean, prettier is clean on all tracked source, and the production build succeeds.
116 lines
3.3 KiB
TOML
116 lines
3.3 KiB
TOML
[project]
|
|
name = "spoolman"
|
|
version = "0.24.0"
|
|
description = "A web service that keeps track of 3D printing spools."
|
|
license = "MIT"
|
|
readme = "README.md"
|
|
authors = [{ name = "Donkie", email = "daniel.cf.hultgren@gmail.com" }]
|
|
dependencies = [
|
|
"uvicorn~=0.34",
|
|
"httptools>=0.6.4; platform_machine != \"armv7l\"",
|
|
"uvloop!=0.15.0,!=0.15.1,>=0.21.0; platform_machine != \"armv7l\" and sys_platform != \"win32\" and (sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\")",
|
|
"fastapi~=0.115",
|
|
"SQLAlchemy[aiomysql,aiosqlite,asyncio,postgresql_asyncpg]~=2.0",
|
|
"pydantic~=2.10",
|
|
"platformdirs~=4.3",
|
|
"alembic~=1.15",
|
|
"scheduler~=0.8",
|
|
"sqlalchemy-cockroachdb~=2.0",
|
|
"asyncpg~=0.30",
|
|
"psycopg2-binary~=2.9",
|
|
"setuptools~=78.1",
|
|
"WebSockets~=15.0",
|
|
"prometheus-client~=0.21",
|
|
"httpx~=0.28",
|
|
"hishel~=0.1",
|
|
"aiomysql[rsa]~=0.3",
|
|
]
|
|
requires-python = ">=3.14"
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"ruff==0.14.10",
|
|
"pytest~=9.1.1",
|
|
"pytest-asyncio~=1.4.0",
|
|
"httpx~=0.28.1",
|
|
"poethepoet>=0.40.0",
|
|
]
|
|
|
|
[tool.poe.tasks.generate-docs]
|
|
script = "spoolman.docs:generate_docs"
|
|
help = "Generate API documentation for Spoolman."
|
|
|
|
[tool.poe.tasks.bump]
|
|
script = "spoolman.bump:bump"
|
|
help = "Bump Spoolman version."
|
|
|
|
[tool.poe.tasks.run]
|
|
cmd = "uvicorn spoolman.main:app"
|
|
help = "Start Spoolman."
|
|
|
|
[tool.poe.tasks.itest]
|
|
cmd = "python tests_integration/run.py"
|
|
help = "Builds Spoolman and runs integration tests on the backend against all supported databases."
|
|
|
|
[tool.poe.tasks.itest-frontend]
|
|
cmd = "python tests_frontend/run.py"
|
|
help = "Builds Spoolman and runs Playwright frontend integration tests against a real deployment."
|
|
|
|
[tool.bumpversion]
|
|
current_version = "0.22.1"
|
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
serialize = ["{major}.{minor}.{patch}"]
|
|
allow_dirty = false
|
|
|
|
[[tool.bumpversion.files]]
|
|
filename = "pyproject.toml"
|
|
search = 'version = "{current_version}"'
|
|
replace = 'version = "{new_version}"'
|
|
|
|
[tool.ruff]
|
|
line-length = 120
|
|
target-version = "py314"
|
|
extend-exclude = ["client_v2"]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["ALL"]
|
|
ignore = [
|
|
"A003",
|
|
"COM812",
|
|
"D101",
|
|
"D104",
|
|
"D203",
|
|
"D213",
|
|
"D406",
|
|
"D407",
|
|
"DTZ003",
|
|
"EM101",
|
|
"EM102",
|
|
"FA100",
|
|
"FIX002",
|
|
"PLR0913",
|
|
"S104",
|
|
"S603",
|
|
"SIM108",
|
|
# TC001/2/3 want type-only imports moved into `if TYPE_CHECKING:` blocks. That is
|
|
# wrong here: FastAPI resolves `Annotated[AsyncSession, Depends(...)]` and websocket
|
|
# parameters at runtime, Pydantic resolves field types at runtime, and SQLAlchemy
|
|
# resolves `Mapped[...]` at mapper-configuration time. A TYPE_CHECKING-only import is
|
|
# absent from the runtime namespace, so applying these breaks dependency injection.
|
|
# Ruff marks all of them as unsafe fixes for exactly this reason. They only began
|
|
# firing when target-version moved to py314 (PEP 649 deferred annotations).
|
|
"TC001",
|
|
"TC002",
|
|
"TC003",
|
|
"TD002",
|
|
"TD003",
|
|
"TRY003",
|
|
"TRY201",
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
# S105/S106 flag string literals that look like passwords. Authentication tests have to
|
|
# name the credentials they sign in with, so the rule only produces noise there.
|
|
"tests*/*" = ["ANN201", "ASYNC210", "S101", "S105", "S106", "PLR2004", "D103", "TID252"]
|
|
"migrations/versions/*" = ["N999"]
|