Coverage for seedboxsync/front/views/settings/apikeys.py: 81%
53 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
1#
2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr>
3#
4# For the full copyright and license information, please view the LICENSE
5# file that was distributed with this source code.
6#
7"""SeedboxSync Flask view for settings ApiKeys."""
9from typing import cast
10from flask import abort, flash, redirect, render_template, url_for
11from flask_login import current_user
12from werkzeug.wrappers.response import Response
13from seedboxsync.core import current_app
14from seedboxsync.core.database.models import ApiKey, User
15from seedboxsync.front.babel import gettext as _
16from seedboxsync.front.forms import ApiKeyForm, EmptyCSRFForm
17from seedboxsync.front.login_manager import login_required
18from seedboxsync.front.utils import toast
19from seedboxsync.front.views import bp_settings as bp
22@bp.route("/apikeys", methods=["GET"])
23@login_required # type: ignore[untyped-decorator]
24def apikeys() -> str | Response:
25 """
26 Render the API keys management page and handle key creation.
28 Displays active API keys for the currently authenticated user and handles
29 generating new keys upon valid form submission.
31 Returns:
32 str | Response: Rendered HTML template containing the user's API keys list.
33 """
34 apikeys: list[ApiKey] = []
35 if current_user.is_authenticated:
36 apikeys = list(ApiKey.select(ApiKey.id, ApiKey.name, ApiKey.created, ApiKey.last_used))
38 return render_template("settings/apikeys.html", apikeys=apikeys)
41@bp.route("/apikeys/create", methods=["GET", "POST"])
42@login_required # type: ignore[untyped-decorator]
43def apikeys_create() -> str | Response:
44 """
45 Render and process the user creation view.
47 Handles fetching user data, populating the creation form, verifying password
48 confirmations, hashing new passwords, and saving updates to the database.
50 Returns:
51 str | Response: Rendered HTML edit form template.
52 """
53 form = ApiKeyForm()
54 if form.validate_on_submit():
55 try:
56 apikey = ApiKey()
57 apikey_name = form.name.data or ""
58 user_instance = cast(User, current_user)
59 _apikey, apikey_raw = apikey.generate(user_instance, name=apikey_name)
60 toast(_("API key created successfully."), _("API key"), "success")
61 flash(
62 _("API key '%(apikey_name)s' created successfully. Copy it now, as it will not be displayed again: '%(apikey_raw)s'")
63 % {"apikey_name": apikey_name, "apikey_raw": apikey_raw},
64 "info",
65 )
66 return redirect(url_for("settings.apikeys"))
67 except Exception as e:
68 current_app.logger.exception("Failed to save apikey.", exc_info=e)
69 toast(_("Failed to save apikey."), _("API key"), "danger")
70 return render_template("settings/apikeys_create.html", form=form)
73@bp.route("/apikeys/<int:apikey_id>/delete", methods=["GET", "POST"])
74@login_required # type: ignore[untyped-decorator]
75def apikeys_delete(apikey_id: int) -> str | Response:
76 """
77 Render and process the apikey delete view.
79 Handles fetching apikey data, populating the delete form, verifying password
80 confirmations, hashing new passwords, and saving updates to the database.
82 Args:
83 apikey_id (int): Database identifier of the apikey to edit.
85 Returns:
86 str | Response: Rendered HTML edit form template.
88 Raises:
89 HTTPException: 404 error if no apikey matches the given ID.
90 """
91 form = EmptyCSRFForm()
92 try:
93 apikey = ApiKey.get(ApiKey.id == apikey_id)
94 except ApiKey.DoesNotExist: # type: ignore[attr-defined]
95 abort(404, f"API key id {apikey_id} doesn't exist.")
97 if form.validate_on_submit():
98 try:
99 apikey_name = apikey.name
100 apikey.delete_instance()
101 toast(_("API key '%(apikey_name)s' deleted successfully.") % {"apikey_name": apikey_name}, _("API key"), "success")
102 return redirect(url_for("settings.apikeys"))
103 except Exception as e:
104 current_app.logger.exception("Failed to delete API key.", exc_info=e)
105 toast(_("Failed to delete API key."), _("API key"), "danger")
107 return render_template("settings/apikeys_delete.html", form=form, apikey=apikey)