feat: renew command

This commit is contained in:
2026-04-12 23:22:33 +02:00
parent 9cf3b4e83c
commit 7f7fdc2d2a
5 changed files with 40 additions and 34 deletions
+4 -14
View File
@@ -2,7 +2,7 @@
```txt ```txt
usage: stapler [-h] [--debug | --no-debug] [-d DATA_DIR] [--certificates | --no-certificates] [--certbot | --no-certbot] [--self-signed-path SELF_SIGNED_PATH] [--certbot-conf CERTBOT_CONF] usage: stapler [-h] [--debug | --no-debug] [-d DATA_DIR] [--certificates | --no-certificates] [--certbot | --no-certbot] [--self-signed-path SELF_SIGNED_PATH] [--certbot-conf CERTBOT_CONF]
[--certbot-www CERTBOT_WWW] [--certbot-www CERTBOT_WWW] [--host HOST] [-p PORT] [--https | --no-https] [-t TOKEN] [--max-size-bytes MAX_SIZE] [-b BIND]
COMMAND ... COMMAND ...
Static pages as simple as a gzip file Static pages as simple as a gzip file
@@ -10,6 +10,7 @@ Static pages as simple as a gzip file
positional arguments: positional arguments:
COMMAND COMMAND
run Run Stapler server run Run Stapler server
renew Renew certificates
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
@@ -26,21 +27,10 @@ options:
Certbot config dir (default: /etc/letsencrypt) Certbot config dir (default: /etc/letsencrypt)
--certbot-www CERTBOT_WWW --certbot-www CERTBOT_WWW
Certbot www dir (default: ./data/.certbot) Certbot www dir (default: ./data/.certbot)
(Each option can be supplied with equivalent environment variable.)
```
```txt
usage: stapler run [-h] [--host HOST] [-p PORT] [--https | --no-https] -t TOKEN [--max-size-bytes MAX_SIZE] [-b BIND]
Run Stapler server
options:
-h, --help show this help message and exit
--host HOST server default host (default: localhost:8080) --host HOST server default host (default: localhost:8080)
-p, --port PORT server port (default: 8080) -p, --port PORT server port (default: 8080)
--https, --no-https Use https (implies --certificates) (default: true) --https, --no-https Use https (implies --certificates) (default: true)
-t, --token TOKEN secret token for update requests -t, --token TOKEN secret token for update requests (default: )
--max-size-bytes MAX_SIZE --max-size-bytes MAX_SIZE
max size of accepted archives (in bytes) (default: 2000000) max size of accepted archives (in bytes) (default: 2000000)
-b, --bind BIND server bind address (default: 0.0.0.0) -b, --bind BIND server bind address (default: 0.0.0.0)
@@ -101,7 +91,7 @@ curl -X DELETE \
- [x] redirect /.well-known/acme-challenge to specific path - [x] redirect /.well-known/acme-challenge to specific path
- [x] certbot/self-signed create/renew in specific dir - [x] certbot/self-signed create/renew in specific dir
- [x] better logger - [x] better logger
- [ ] renew command - [x] renew command
- [x] https mode w/ multiple hosts - [x] https mode w/ multiple hosts
- [ ] restart command (on new/deleted host) - [ ] restart command (on new/deleted host)
- [ ] proper doc - [ ] proper doc
+4 -1
View File
@@ -1,3 +1,5 @@
import sys
from src.logs import setup_logs from src.logs import setup_logs
from src.params import parse_parameters from src.params import parse_parameters
from src.server import StaplerServer from src.server import StaplerServer
@@ -7,7 +9,8 @@ def main() -> None:
params = parse_parameters() params = parse_parameters()
setup_logs(params) setup_logs(params)
server = StaplerServer(params) server = StaplerServer(params)
server.start() method = getattr(server, params.command)
sys.exit(method())
if __name__ == "__main__": if __name__ == "__main__":
+1 -1
View File
@@ -181,7 +181,7 @@ class RequestHandler(http.server.SimpleHTTPRequestHandler):
self.logger.debug(fmt, *args) self.logger.debug(fmt, *args)
def __check_update_request(self) -> str | None: def __check_update_request(self) -> str | None:
if self.headers["X-Token"] != self.token: if len(self.token) and self.headers["X-Token"] != self.token:
self.send_error(http.HTTPStatus.UNAUTHORIZED, "Invalid token") self.send_error(http.HTTPStatus.UNAUTHORIZED, "Invalid token")
return None return None
if (sub_path := self.__get_subpath_full(self.path)) is None: if (sub_path := self.__get_subpath_full(self.path)) is None:
+13 -16
View File
@@ -1,6 +1,7 @@
import argparse import argparse
import dataclasses import dataclasses
import os import os
import typing
from . import project from . import project
@@ -21,7 +22,7 @@ class Parameters:
with_certbot: bool with_certbot: bool
with_certificates: bool with_certificates: bool
https: bool https: bool
command: str command: typing.Literal["run", "renew"]
debug: bool debug: bool
@classmethod @classmethod
@@ -95,7 +96,6 @@ def parse_parameters() -> Parameters:
epilog=__EPILOG, epilog=__EPILOG,
suggest_on_error=True, suggest_on_error=True,
) )
subparsers = parser.add_subparsers(dest="command", required=True, metavar="COMMAND")
parser.add_argument("--debug", action=argparse.BooleanOptionalAction) parser.add_argument("--debug", action=argparse.BooleanOptionalAction)
__add_arg_str( __add_arg_str(
parser, parser,
@@ -140,56 +140,53 @@ def parse_parameters() -> Parameters:
default="./data/.certbot", default="./data/.certbot",
help_txt="Certbot www dir", help_txt="Certbot www dir",
) )
run_parser = subparsers.add_parser(
"run",
help="Run Stapler server",
description="Run Stapler server",
epilog=__EPILOG,
)
__add_arg_str( __add_arg_str(
run_parser, parser,
"--host", "--host",
env_var="HOST", env_var="HOST",
default="localhost:8080", default="localhost:8080",
help_txt="server default host", help_txt="server default host",
) )
__add_arg_int( __add_arg_int(
run_parser, parser,
"-p", "-p",
"--port", "--port",
env_var="PORT", env_var="PORT",
default=8080, default=8080,
help_txt="server port", help_txt="server port",
) )
run_parser.add_argument( parser.add_argument(
"--https", "--https",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
help="Use https (implies --certificates) (default: true)", help="Use https (implies --certificates) (default: true)",
default=True, default=True,
) )
__add_arg_str_required( __add_arg_str(
run_parser, parser,
"-t", "-t",
"--token", "--token",
env_var="TOKEN", env_var="TOKEN",
default="",
help_txt="secret token for update requests", help_txt="secret token for update requests",
) )
__add_arg_int( __add_arg_int(
run_parser, parser,
"--max-size-bytes", "--max-size-bytes",
env_var="MAX_SIZE", env_var="MAX_SIZE",
default=2_000_000, default=2_000_000,
help_txt="max size of accepted archives (in bytes)", help_txt="max size of accepted archives (in bytes)",
) )
__add_arg_str( __add_arg_str(
run_parser, parser,
"-b", "-b",
"--bind", "--bind",
env_var="BIND", env_var="BIND",
default="0.0.0.0", default="0.0.0.0",
help_txt="server bind address", help_txt="server bind address",
) )
subparsers = parser.add_subparsers(dest="command", required=True, metavar="COMMAND")
subparsers.add_parser("run", help="Run Stapler server")
subparsers.add_parser("renew", help="Renew certificates")
args = parser.parse_args() args = parser.parse_args()
if args.https: if args.https:
args.with_certificates = True args.with_certificates = True
+18 -2
View File
@@ -20,11 +20,16 @@ class StaplerServer:
def request_handler(self, *args: typing.Any) -> http.server.BaseHTTPRequestHandler: def request_handler(self, *args: typing.Any) -> http.server.BaseHTTPRequestHandler:
return handler.RequestHandler(*args, params=self.params, registry=self.registry) return handler.RequestHandler(*args, params=self.params, registry=self.registry)
def __get_all_hosts(self) -> list[str]:
return [self.default_host, *self.registry.get_hosts()]
def __startup(self) -> None: def __startup(self) -> None:
self.logger.info("Starting up...") self.logger.info("Starting up...")
self.registry.load_pages() self.registry.load_pages()
if self.params.with_certificates: if self.params.with_certificates:
self.cert_manager.init([self.default_host, *self.registry.get_hosts()]) self.cert_manager.init(self.__get_all_hosts())
if not len(self.params.token):
self.logger.warning("No token provided update requests will fail")
def __create_https_context(self, server: http.server.HTTPServer) -> bool: def __create_https_context(self, server: http.server.HTTPServer) -> bool:
https = False https = False
@@ -35,7 +40,7 @@ class StaplerServer:
server.socket = context.wrap_socket(server.socket, server_side=True) server.socket = context.wrap_socket(server.socket, server_side=True)
return https return https
def start(self) -> None: def run(self) -> int:
self.logger.info("Version %s", project.get_version()) self.logger.info("Version %s", project.get_version())
self.__startup() self.__startup()
server = http.server.ThreadingHTTPServer( server = http.server.ThreadingHTTPServer(
@@ -55,3 +60,14 @@ class StaplerServer:
) )
with contextlib.suppress(KeyboardInterrupt): with contextlib.suppress(KeyboardInterrupt):
server.serve_forever() server.serve_forever()
return 0
def renew(self) -> int:
self.logger.info("Starting up...")
if not self.params.with_certificates:
self.logger.warning("Cannot renew without certificates")
return 1
self.registry.load_pages()
for host in self.__get_all_hosts():
self.cert_manager.create_or_update(host)
return 0