chore: scaffold payroll extension
Repo tooling and LNbits extension metadata only — no runtime code yet, so this lands separately from the feature work per the workspace commit rules (cross-cutting concerns commit first). - MIT licence, .gitignore, README - Makefile + pyproject wired for the standard aio fork lint pipeline (black + ruff + mypy) and pytest - config.json declaring id/version/tile so the extension is installable from the aiolabs catalog once there is something to install Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jy52j9GRZ6XKa1Zt21LLj
This commit is contained in:
commit
a211a8ab8b
7 changed files with 191 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
.venv/
|
||||
node_modules/
|
||||
*.sqlite3
|
||||
.DS_Store
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.mypy_cache/
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 aiolabs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
32
Makefile
Normal file
32
Makefile
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
all: format check
|
||||
|
||||
format: black ruff
|
||||
|
||||
check: mypy checkblack checkruff
|
||||
|
||||
mypy:
|
||||
uv run mypy .
|
||||
|
||||
black:
|
||||
uv run black .
|
||||
|
||||
ruff:
|
||||
uv run ruff check . --fix
|
||||
|
||||
checkruff:
|
||||
uv run ruff check .
|
||||
|
||||
checkblack:
|
||||
uv run black --check .
|
||||
|
||||
test:
|
||||
PYTHONUNBUFFERED=1 \
|
||||
DEBUG=true \
|
||||
uv run pytest
|
||||
|
||||
install-pre-commit-hook:
|
||||
@echo "Installing pre-commit hook to git"
|
||||
uv run pre-commit install
|
||||
|
||||
pre-commit:
|
||||
uv run pre-commit run --all-files
|
||||
42
README.md
Normal file
42
README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Payroll
|
||||
|
||||
Scheduled recurring payouts between LNbits wallets, driven by the instance
|
||||
**super user**.
|
||||
|
||||
Pick a user from the account directory, point the payout at one of their
|
||||
wallets, set an amount + currency, a start date, how often it pays and how
|
||||
many times — Payroll then moves the money on schedule, from a source wallet
|
||||
you control, and keeps a ledger of every attempt.
|
||||
|
||||
## Status
|
||||
|
||||
Early. Built for `aiolabs` LNbits instances; see `docs/` for the data model
|
||||
and operational notes.
|
||||
|
||||
## Who can use it
|
||||
|
||||
Everything under `/payroll/api/v1/` that reads the account directory or
|
||||
touches a contract is gated on `check_super_user` — payroll moves money
|
||||
between accounts the operator does not own, so wallet-level admin keys are
|
||||
deliberately *not* enough. (`require_admin_key` authorises writes to your
|
||||
own wallet only; it is not instance-admin. See the auth table in the
|
||||
workspace `CLAUDE.md`.)
|
||||
|
||||
Add `payroll` to `LNBITS_ADMIN_EXTENSIONS` so the extension is hidden from
|
||||
ordinary users in the UI as well.
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
make # black + ruff + mypy
|
||||
make test # pytest
|
||||
```
|
||||
|
||||
The dev LNbits compose mounts `~/dev/shared/extensions/` at `/shared` with
|
||||
`LNBITS_EXTENSIONS_PATH=/shared`, so this checkout *is* the installed
|
||||
extension. FakeWallet is enough — no regtest needed for anything except
|
||||
end-to-end Lightning behaviour.
|
||||
|
||||
## Licence
|
||||
|
||||
MIT
|
||||
23
config.json
Normal file
23
config.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"id": "payroll",
|
||||
"version": "0.1.0",
|
||||
"name": "Payroll",
|
||||
"repo": "https://git.atitlan.io/aiolabs/payroll",
|
||||
"short_description": "Scheduled recurring salary payouts between LNbits wallets",
|
||||
"description": "",
|
||||
"tile": "/payroll/static/image/payroll.png",
|
||||
"min_lnbits_version": "1.4.1",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "padreug",
|
||||
"uri": "https://git.atitlan.io/padreug",
|
||||
"role": "Author"
|
||||
}
|
||||
],
|
||||
"images": [],
|
||||
"license": "MIT",
|
||||
"paid_features": "",
|
||||
"tags": ["Finance"],
|
||||
"donate": "",
|
||||
"hidden": false
|
||||
}
|
||||
63
pyproject.toml
Normal file
63
pyproject.toml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
[project]
|
||||
name = "lnbits-payroll"
|
||||
version = "0.0.0"
|
||||
requires-python = ">=3.10,<3.13"
|
||||
description = "Recurring salary payouts between LNbits wallets, scheduled by the instance super user."
|
||||
authors = [{ name = "aiolabs", email = "dev@aiolabs.dev" }]
|
||||
urls = { Homepage = "https://git.atitlan.io/aiolabs/payroll", Repository = "https://git.atitlan.io/aiolabs/payroll" }
|
||||
dependencies = [ "lnbits>1" ]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"black",
|
||||
"pytest-asyncio",
|
||||
"pytest",
|
||||
"mypy",
|
||||
"pre-commit",
|
||||
"ruff",
|
||||
]
|
||||
|
||||
[tool.poetry]
|
||||
package-mode = false
|
||||
|
||||
[tool.mypy]
|
||||
plugins = ["pydantic.mypy"]
|
||||
|
||||
[tool.pydantic-mypy]
|
||||
init_forbid_extra = true
|
||||
init_typed = true
|
||||
warn_required_dynamic_aliases = true
|
||||
warn_untyped_fields = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
log_cli = false
|
||||
testpaths = [
|
||||
"tests"
|
||||
]
|
||||
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["F", "E", "W", "I", "A", "C", "N", "UP", "RUF", "B"]
|
||||
ignore = ["C901"]
|
||||
|
||||
fixable = ["ALL"]
|
||||
unfixable = []
|
||||
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
[tool.ruff.lint.pep8-naming]
|
||||
classmethod-decorators = [
|
||||
"validator",
|
||||
"root_validator",
|
||||
]
|
||||
|
||||
[tool.ruff.lint.flake8-bugbear]
|
||||
extend-immutable-calls = [
|
||||
"fastapi.Depends",
|
||||
"fastapi.Query",
|
||||
]
|
||||
BIN
static/image/payroll.png
Normal file
BIN
static/image/payroll.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 481 B |
Loading…
Add table
Add a link
Reference in a new issue