mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-03-14 05:53:39 +00:00
feat: brand-new interface (#158)
This was initally generated by ai, but later has been heavily edited. The reason why it has been implemented is that there are plans to implement more features to ui, but it becomes hard to add new features to plain js, so I decided to rewrite it in typescript. Yet because it is still ai slop, it is still possible to enable old interface via configuration, even though new interface is turned on by default to get feedback
This commit is contained in:
@@ -60,6 +60,16 @@ class Auth(LazyLogging):
|
||||
"""
|
||||
return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#login-modal" style="text-decoration: none"><i class="bi bi-box-arrow-in-right"></i> login</button>"""
|
||||
|
||||
@property
|
||||
def is_external(self) -> bool:
|
||||
"""
|
||||
check if the provider is external (e.g. OAuth)
|
||||
|
||||
Returns:
|
||||
bool: ``True`` in case if external provider is used and ``False`` otherwise
|
||||
"""
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def load(configuration: Configuration, database: SQLite) -> Auth:
|
||||
"""
|
||||
|
||||
@@ -36,7 +36,6 @@ class OAuth(Mapping):
|
||||
Attributes:
|
||||
client_id(str): application client id
|
||||
client_secret(str): application client secret key
|
||||
icon(str): icon to be used in login control
|
||||
provider(aioauth_client.OAuth2Client): provider class, should be one of aiohttp-client provided classes
|
||||
redirect_uri(str): redirect URI registered in provider
|
||||
scopes(str): list of scopes required by the application
|
||||
@@ -59,7 +58,6 @@ class OAuth(Mapping):
|
||||
self.provider = self.get_provider(configuration.get("auth", "oauth_provider"))
|
||||
# it is list, but we will have to convert to string it anyway
|
||||
self.scopes = configuration.get("auth", "oauth_scopes")
|
||||
self.icon = configuration.get("auth", "oauth_icon", fallback="google")
|
||||
|
||||
@property
|
||||
def auth_control(self) -> str:
|
||||
@@ -69,8 +67,17 @@ class OAuth(Mapping):
|
||||
Returns:
|
||||
str: login control as html code to insert
|
||||
"""
|
||||
return f"<a class=\"nav-link\" href=\"/api/v1/login\" title=\"login via OAuth2\"><i class=\"bi bi-{
|
||||
self.icon}\"></i> login</a>"
|
||||
return "<a class=\"nav-link\" href=\"/api/v1/login\" title=\"login via OAuth2\"><i class=\"bi bi-box-arrow-in-right\"></i> login</a>"
|
||||
|
||||
@property
|
||||
def is_external(self) -> bool:
|
||||
"""
|
||||
check if the provider is external (e.g. OAuth)
|
||||
|
||||
Returns:
|
||||
bool: ``True`` in case if external provider is used and ``False`` otherwise
|
||||
"""
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def get_provider(name: str) -> type[aioauth_client.OAuth2Client]:
|
||||
|
||||
@@ -161,10 +161,6 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
|
||||
"coerce": "integer",
|
||||
"min": 0,
|
||||
},
|
||||
"oauth_icon": {
|
||||
"type": "string",
|
||||
"empty": False,
|
||||
},
|
||||
"oauth_provider": {
|
||||
"type": "string",
|
||||
"empty": False,
|
||||
@@ -398,6 +394,10 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
|
||||
"path_exists": True,
|
||||
"path_type": "dir",
|
||||
},
|
||||
"template": {
|
||||
"type": "string",
|
||||
"empty": False,
|
||||
},
|
||||
"templates": {
|
||||
"type": "list",
|
||||
"coerce": "list",
|
||||
|
||||
@@ -35,7 +35,7 @@ from enum import Enum
|
||||
from filelock import FileLock
|
||||
from pathlib import Path
|
||||
from pwd import getpwuid
|
||||
from typing import Any, IO, TypeVar
|
||||
from typing import Any, IO, TypeVar, cast
|
||||
|
||||
from ahriman.core.exceptions import CalledProcessError, OptionError, UnsafeRunError
|
||||
from ahriman.core.types import Comparable
|
||||
@@ -285,16 +285,17 @@ def filelock(path: Path) -> Iterator[FileLock]:
|
||||
lock_path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def filter_json(source: dict[str, Any], known_fields: Iterable[str]) -> dict[str, Any]:
|
||||
def filter_json(source: T, known_fields: Iterable[str] | None = None) -> T:
|
||||
"""
|
||||
filter json object by fields used for json-to-object conversion
|
||||
recursively filter json object removing ``None`` values and optionally filtering by known fields
|
||||
|
||||
Args:
|
||||
source(dict[str, Any]): raw json object
|
||||
known_fields(Iterable[str]): list of fields which have to be known for the target object
|
||||
source(T): raw json object (dict, list, or scalar)
|
||||
known_fields(Iterable[str] | None, optional): list of fields which have to be known for the target object
|
||||
(Default value = None)
|
||||
|
||||
Returns:
|
||||
dict[str, Any]: json object without unknown and empty fields
|
||||
T: json without ``None`` values
|
||||
|
||||
Examples:
|
||||
This wrapper is mainly used for the dataclasses, thus the flow must be something like this::
|
||||
@@ -306,7 +307,15 @@ def filter_json(source: dict[str, Any], known_fields: Iterable[str]) -> dict[str
|
||||
>>> properties = filter_json(dump, known_fields)
|
||||
>>> package = Package(**properties)
|
||||
"""
|
||||
return {key: value for key, value in source.items() if key in known_fields and value is not None}
|
||||
if isinstance(source, dict):
|
||||
return cast(T, {
|
||||
key: filter_json(value)
|
||||
for key, value in source.items()
|
||||
if value is not None and (known_fields is None or key in known_fields)
|
||||
})
|
||||
if isinstance(source, list):
|
||||
return cast(T, [filter_json(value) for value in source])
|
||||
return source
|
||||
|
||||
|
||||
def full_version(epoch: str | int | None, pkgver: str, pkgrel: str) -> str:
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
#
|
||||
from ahriman.web.schemas.any_schema import AnySchema
|
||||
from ahriman.web.schemas.aur_package_schema import AURPackageSchema
|
||||
from ahriman.web.schemas.auth_info_schema import AuthInfoSchema
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.auto_refresh_interval_schema import AutoRefreshIntervalSchema
|
||||
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
|
||||
from ahriman.web.schemas.changes_schema import ChangesSchema
|
||||
from ahriman.web.schemas.configuration_schema import ConfigurationSchema
|
||||
@@ -30,6 +32,7 @@ from ahriman.web.schemas.event_schema import EventSchema
|
||||
from ahriman.web.schemas.event_search_schema import EventSearchSchema
|
||||
from ahriman.web.schemas.file_schema import FileSchema
|
||||
from ahriman.web.schemas.info_schema import InfoSchema
|
||||
from ahriman.web.schemas.info_v2_schema import InfoV2Schema
|
||||
from ahriman.web.schemas.internal_status_schema import InternalStatusSchema
|
||||
from ahriman.web.schemas.log_schema import LogSchema
|
||||
from ahriman.web.schemas.login_schema import LoginSchema
|
||||
|
||||
39
src/ahriman/web/schemas/auth_info_schema.py
Normal file
39
src/ahriman/web/schemas/auth_info_schema.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from ahriman.web.apispec import Schema, fields
|
||||
|
||||
|
||||
class AuthInfoSchema(Schema):
|
||||
"""
|
||||
authorization information schema
|
||||
"""
|
||||
|
||||
control = fields.String(required=True, metadata={
|
||||
"description": "HTML control for login interface",
|
||||
})
|
||||
enabled = fields.Boolean(required=True, metadata={
|
||||
"description": "Whether authentication is enabled or not",
|
||||
})
|
||||
external = fields.Boolean(required=True, metadata={
|
||||
"description": "Whether authorization provider is external (e.g. OAuth)",
|
||||
})
|
||||
username = fields.String(metadata={
|
||||
"description": "Currently authenticated username if any",
|
||||
})
|
||||
36
src/ahriman/web/schemas/auto_refresh_interval_schema.py
Normal file
36
src/ahriman/web/schemas/auto_refresh_interval_schema.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from ahriman.web.apispec import Schema, fields
|
||||
|
||||
|
||||
class AutoRefreshIntervalSchema(Schema):
|
||||
"""
|
||||
auto refresh interval schema
|
||||
"""
|
||||
|
||||
interval = fields.Integer(required=True, metadata={
|
||||
"description": "Auto refresh interval in milliseconds",
|
||||
})
|
||||
is_active = fields.Boolean(required=True, metadata={
|
||||
"description": "Whether this interval is the default active one",
|
||||
})
|
||||
text = fields.String(required=True, metadata={
|
||||
"description": "Human readable interval description",
|
||||
})
|
||||
@@ -27,7 +27,7 @@ class InfoSchema(Schema):
|
||||
response service information schema
|
||||
"""
|
||||
|
||||
auth = fields.Boolean(dump_default=False, required=True, metadata={
|
||||
auth = fields.Boolean(required=True, metadata={
|
||||
"description": "Whether authentication is enabled or not",
|
||||
})
|
||||
repositories = fields.Nested(RepositoryIdSchema(many=True), required=True, metadata={
|
||||
|
||||
50
src/ahriman/web/schemas/info_v2_schema.py
Normal file
50
src/ahriman/web/schemas/info_v2_schema.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from ahriman import __version__
|
||||
from ahriman.web.apispec import Schema, fields
|
||||
from ahriman.web.schemas.auth_info_schema import AuthInfoSchema
|
||||
from ahriman.web.schemas.auto_refresh_interval_schema import AutoRefreshIntervalSchema
|
||||
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
|
||||
|
||||
|
||||
class InfoV2Schema(Schema):
|
||||
"""
|
||||
response service information schema
|
||||
"""
|
||||
|
||||
auth = fields.Nested(AuthInfoSchema(), required=True, metadata={
|
||||
"description": "Authorization descriptor",
|
||||
})
|
||||
autorefresh_intervals = fields.Nested(AutoRefreshIntervalSchema(many=True), metadata={
|
||||
"description": "Available auto refresh intervals",
|
||||
})
|
||||
docs_enabled = fields.Boolean(metadata={
|
||||
"description": "Whether API documentation is enabled",
|
||||
})
|
||||
index_url = fields.String(metadata={
|
||||
"description": "URL to the repository index page",
|
||||
})
|
||||
repositories = fields.Nested(RepositoryIdSchema(many=True), required=True, metadata={
|
||||
"description": "List of loaded repositories",
|
||||
})
|
||||
version = fields.String(required=True, metadata={
|
||||
"description": "Service version",
|
||||
"example": __version__,
|
||||
})
|
||||
@@ -29,6 +29,10 @@ class RepositoryIdSchema(Schema):
|
||||
"description": "Repository architecture",
|
||||
"example": "x86_64",
|
||||
})
|
||||
id = fields.String(metadata={
|
||||
"description": "Unique repository identifier",
|
||||
"example": "aur-x86_64",
|
||||
})
|
||||
repository = fields.String(metadata={
|
||||
"description": "Repository name",
|
||||
"example": "aur",
|
||||
|
||||
73
src/ahriman/web/server_info.py
Normal file
73
src/ahriman/web/server_info.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from ahriman import __version__
|
||||
from ahriman.core.auth.helpers import authorized_userid
|
||||
from ahriman.core.types import Comparable
|
||||
from ahriman.core.utils import pretty_interval
|
||||
from ahriman.web.apispec import aiohttp_apispec
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
__all__ = ["server_info"]
|
||||
|
||||
|
||||
async def server_info(view: BaseView) -> dict[str, Any]:
|
||||
"""
|
||||
generate server info which can be used in responses directly
|
||||
|
||||
Args:
|
||||
view(BaseView): view of the request
|
||||
|
||||
Returns:
|
||||
dict[str, Any]: server info as a json response
|
||||
"""
|
||||
autorefresh_intervals = [
|
||||
{
|
||||
"interval": interval * 1000, # milliseconds
|
||||
"is_active": index == 0, # first element is always default
|
||||
"text": pretty_interval(interval),
|
||||
}
|
||||
for index, interval in enumerate(view.configuration.getintlist("web", "autorefresh_intervals", fallback=[]))
|
||||
if interval > 0 # special case if 0 exists and first, refresh will not be turned on by default
|
||||
]
|
||||
comparator: Callable[[dict[str, Any]], Comparable] = lambda interval: interval["interval"]
|
||||
|
||||
return {
|
||||
"auth": {
|
||||
"control": view.validator.auth_control,
|
||||
"enabled": view.validator.enabled,
|
||||
"external": view.validator.is_external,
|
||||
"username": await authorized_userid(view.request),
|
||||
},
|
||||
"autorefresh_intervals": sorted(autorefresh_intervals, key=comparator),
|
||||
"docs_enabled": aiohttp_apispec is not None,
|
||||
"index_url": view.configuration.get("web", "index_url", fallback=None),
|
||||
"repositories": [
|
||||
{
|
||||
"id": repository_id.id,
|
||||
**repository_id.view(),
|
||||
}
|
||||
for repository_id in sorted(view.services)
|
||||
],
|
||||
"version": __version__,
|
||||
}
|
||||
@@ -17,10 +17,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, StreamResponse, View
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, Response, StreamResponse, View, json_response
|
||||
from aiohttp_cors import CorsViewMixin
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import ClassVar, TypeVar
|
||||
from typing import Any, ClassVar, TypeVar
|
||||
|
||||
from ahriman.core.auth import Auth
|
||||
from ahriman.core.configuration import Configuration
|
||||
@@ -29,6 +29,7 @@ from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.core.sign.gpg import GPG
|
||||
from ahriman.core.spawn import Spawn
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.core.utils import filter_json
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.keys import AuthKey, ConfigurationKey, SpawnKey, WatcherKey, WorkersKey
|
||||
@@ -162,6 +163,20 @@ class BaseView(View, CorsViewMixin):
|
||||
raise KeyError(f"Key {key} is missing or empty") from None
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def json_response(data: dict[str, Any] | list[Any], **kwargs: Any) -> Response:
|
||||
"""
|
||||
filter and convert data and return :class:`aiohttp.web.Response` object
|
||||
|
||||
Args:
|
||||
data(dict[str, Any] | list[Any]): response in json format
|
||||
**kwargs(Any): keyword arguments for :func:`aiohttp.web.json_response` function
|
||||
|
||||
Returns:
|
||||
Response: generated response object
|
||||
"""
|
||||
return json_response(filter_json(data), **kwargs)
|
||||
|
||||
# pylint: disable=not-callable,protected-access
|
||||
async def head(self) -> StreamResponse:
|
||||
"""
|
||||
|
||||
@@ -19,12 +19,11 @@
|
||||
#
|
||||
import aiohttp_jinja2
|
||||
|
||||
from typing import Any, ClassVar
|
||||
from aiohttp.web import Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.core.auth.helpers import authorized_userid
|
||||
from ahriman.core.utils import pretty_interval
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.apispec import aiohttp_apispec
|
||||
from ahriman.web.server_info import server_info
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -48,6 +47,7 @@ class IndexView(BaseView):
|
||||
* id - unique repository identifier, string, required
|
||||
* repository - repository name, string, required
|
||||
* architecture - repository architecture, string, required
|
||||
* version - service version, string, required
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
@@ -56,41 +56,14 @@ class IndexView(BaseView):
|
||||
GET_PERMISSION: ClassVar[UserAccess] = UserAccess.Unauthorized
|
||||
ROUTES = ["/", "/index.html"]
|
||||
|
||||
@aiohttp_jinja2.template("build-status.jinja2")
|
||||
async def get(self) -> dict[str, Any]:
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
process get request. No parameters supported here
|
||||
|
||||
Returns:
|
||||
dict[str, Any]: parameters for jinja template
|
||||
Response: 200 with rendered index page
|
||||
"""
|
||||
auth_username = await authorized_userid(self.request)
|
||||
auth = {
|
||||
"control": self.validator.auth_control,
|
||||
"enabled": self.validator.enabled,
|
||||
"username": auth_username,
|
||||
}
|
||||
context = await server_info(self)
|
||||
|
||||
autorefresh_intervals = [
|
||||
{
|
||||
"interval": interval * 1000, # milliseconds
|
||||
"is_active": index == 0, # first element is always default
|
||||
"text": pretty_interval(interval),
|
||||
}
|
||||
for index, interval in enumerate(self.configuration.getintlist("web", "autorefresh_intervals", fallback=[]))
|
||||
if interval > 0 # special case if 0 exists and first, refresh will not be turned on by default
|
||||
]
|
||||
|
||||
return {
|
||||
"auth": auth,
|
||||
"autorefresh_intervals": sorted(autorefresh_intervals, key=lambda interval: interval["interval"]),
|
||||
"docs_enabled": aiohttp_apispec is not None,
|
||||
"index_url": self.configuration.get("web", "index_url", fallback=None),
|
||||
"repositories": [
|
||||
{
|
||||
"id": repository.id,
|
||||
**repository.view(),
|
||||
}
|
||||
for repository in sorted(self.services)
|
||||
]
|
||||
}
|
||||
template = self.configuration.get("web", "template", fallback="build-status.jinja2")
|
||||
return aiohttp_jinja2.render_template(template, self.request, context)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.event import Event
|
||||
@@ -70,7 +70,7 @@ class EventsView(BaseView):
|
||||
events = self.service().event_get(event, object_id, from_date, to_date, limit, offset)
|
||||
response = [event.view() for event in events]
|
||||
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Audit log"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response
|
||||
from collections.abc import Callable
|
||||
from typing import ClassVar
|
||||
|
||||
@@ -78,7 +78,7 @@ class WorkersView(BaseView):
|
||||
comparator: Callable[[Worker], Comparable] = lambda item: item.identifier
|
||||
response = [worker.view() for worker in sorted(workers, key=comparator)]
|
||||
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Distributed"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.changes import Changes
|
||||
@@ -65,7 +65,7 @@ class ChangesView(StatusViewGuard, BaseView):
|
||||
|
||||
changes = self.service(package_base=package_base).package_changes_get(package_base)
|
||||
|
||||
return json_response(changes.view())
|
||||
return self.json_response(changes.view())
|
||||
|
||||
@apidocs(
|
||||
tags=["Packages"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.dependencies import Dependencies
|
||||
@@ -65,7 +65,7 @@ class DependenciesView(StatusViewGuard, BaseView):
|
||||
|
||||
dependencies = self.service(package_base=package_base).package_dependencies_get(package_base)
|
||||
|
||||
return json_response(dependencies.view())
|
||||
return self.json_response(dependencies.view())
|
||||
|
||||
@apidocs(
|
||||
tags=["Packages"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
@@ -99,7 +99,7 @@ class LogsView(StatusViewGuard, BaseView):
|
||||
"status": status.view(),
|
||||
"logs": "\n".join(f"[{pretty_datetime(log_record.created)}] {log_record.message}" for log_record in logs)
|
||||
}
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Packages"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
@@ -105,7 +105,7 @@ class PackageView(StatusViewGuard, BaseView):
|
||||
"repository": repository_id.view(),
|
||||
}
|
||||
]
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Packages"],
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
import itertools
|
||||
|
||||
from aiohttp.web import HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPNoContent, Response
|
||||
from collections.abc import Callable
|
||||
from typing import ClassVar
|
||||
|
||||
@@ -78,7 +78,7 @@ class PackagesView(StatusViewGuard, BaseView):
|
||||
} for package, status in itertools.islice(sorted(packages, key=comparator), offset, stop)
|
||||
]
|
||||
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Packages"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPNoContent, HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import HTTPNoContent, HTTPNotFound, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@@ -89,4 +89,4 @@ class PatchView(StatusViewGuard, BaseView):
|
||||
if selected is None:
|
||||
raise HTTPNotFound(reason=f"Patch {variable} is unknown")
|
||||
|
||||
return json_response(selected.view())
|
||||
return self.json_response(selected.view())
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
@@ -60,7 +60,7 @@ class PatchesView(StatusViewGuard, BaseView):
|
||||
patches = self.service().package_patches_get(package_base, None)
|
||||
|
||||
response = [patch.view() for patch in patches]
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Packages"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
@@ -78,4 +78,4 @@ class AddView(BaseView):
|
||||
refresh=data.get("refresh", False),
|
||||
)
|
||||
|
||||
return json_response({"process_id": process_id})
|
||||
return self.json_response({"process_id": process_id})
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPNoContent, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.core.formatters import ConfigurationPrinter
|
||||
@@ -64,7 +64,7 @@ class ConfigView(BaseView):
|
||||
for key, value in values.items()
|
||||
if key not in ConfigurationPrinter.HIDE_KEYS
|
||||
]
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@apidocs(
|
||||
tags=["Actions"],
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@@ -71,7 +71,7 @@ class PGPView(BaseView):
|
||||
except Exception:
|
||||
raise HTTPNotFound(reason=f"Key {key} is unknown")
|
||||
|
||||
return json_response({"key": key})
|
||||
return self.json_response({"key": key})
|
||||
|
||||
@apidocs(
|
||||
tags=["Actions"],
|
||||
@@ -100,4 +100,4 @@ class PGPView(BaseView):
|
||||
|
||||
process_id = self.spawner.key_import(key, data.get("server"))
|
||||
|
||||
return json_response({"process_id": process_id})
|
||||
return self.json_response({"process_id": process_id})
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import HTTPNotFound, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@@ -66,4 +66,4 @@ class ProcessView(BaseView):
|
||||
"is_alive": is_alive,
|
||||
}
|
||||
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@@ -74,4 +74,4 @@ class RebuildView(BaseView):
|
||||
increment=data.get("increment", True),
|
||||
)
|
||||
|
||||
return json_response({"process_id": process_id})
|
||||
return self.json_response({"process_id": process_id})
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@@ -67,4 +67,4 @@ class RemoveView(BaseView):
|
||||
repository_id = self.repository_id()
|
||||
process_id = self.spawner.packages_remove(repository_id, packages)
|
||||
|
||||
return json_response({"process_id": process_id})
|
||||
return self.json_response({"process_id": process_id})
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
@@ -78,4 +78,4 @@ class RequestView(BaseView):
|
||||
refresh=False, # refresh doesn't work here
|
||||
)
|
||||
|
||||
return json_response({"process_id": process_id})
|
||||
return self.json_response({"process_id": process_id})
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response
|
||||
from collections.abc import Callable
|
||||
from typing import ClassVar
|
||||
|
||||
@@ -83,4 +83,4 @@ class SearchView(BaseView):
|
||||
"description": package.description,
|
||||
} for package in sorted(packages, key=comparator)
|
||||
]
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@@ -75,4 +75,4 @@ class UpdateView(BaseView):
|
||||
refresh=data.get("refresh", False),
|
||||
)
|
||||
|
||||
return json_response({"process_id": process_id})
|
||||
return self.json_response({"process_id": process_id})
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import Response, json_response
|
||||
from aiohttp.web import Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman import __version__
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.apispec.decorators import apidocs
|
||||
from ahriman.web.schemas import InfoSchema
|
||||
from ahriman.web.server_info import server_info
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -52,13 +52,11 @@ class InfoView(BaseView):
|
||||
Returns:
|
||||
Response: 200 with service information object
|
||||
"""
|
||||
info = await server_info(self)
|
||||
response = {
|
||||
"auth": self.validator.enabled,
|
||||
"repositories": [
|
||||
repository_id.view()
|
||||
for repository_id in sorted(self.services)
|
||||
],
|
||||
"version": __version__,
|
||||
"auth": info["auth"]["enabled"],
|
||||
"repositories": info["repositories"],
|
||||
"version": info["version"],
|
||||
}
|
||||
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import Response, json_response
|
||||
from aiohttp.web import Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@@ -56,4 +56,4 @@ class RepositoriesView(BaseView):
|
||||
for repository_id in sorted(self.services)
|
||||
]
|
||||
|
||||
return json_response(repositories)
|
||||
return self.json_response(repositories)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman import __version__
|
||||
@@ -75,7 +75,7 @@ class StatusView(StatusViewGuard, BaseView):
|
||||
version=__version__,
|
||||
)
|
||||
|
||||
return json_response(status.view())
|
||||
return self.json_response(status.view())
|
||||
|
||||
@apidocs(
|
||||
tags=["Status"],
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
import itertools
|
||||
|
||||
from aiohttp.web import Response, json_response
|
||||
from aiohttp.web import Response
|
||||
from dataclasses import replace
|
||||
from typing import ClassVar
|
||||
|
||||
@@ -31,8 +31,7 @@ from ahriman.web.views.status_view_guard import StatusViewGuard
|
||||
|
||||
|
||||
class LogsView(StatusViewGuard, BaseView):
|
||||
""" else:
|
||||
|
||||
"""
|
||||
package logs web view
|
||||
|
||||
Attributes:
|
||||
@@ -80,4 +79,4 @@ class LogsView(StatusViewGuard, BaseView):
|
||||
]
|
||||
|
||||
response = [log_record.view() for log_record in logs]
|
||||
return json_response(response)
|
||||
return self.json_response(response)
|
||||
|
||||
19
src/ahriman/web/views/v2/status/__init__.py
Normal file
19
src/ahriman/web/views/v2/status/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
56
src/ahriman/web/views/v2/status/info.py
Normal file
56
src/ahriman/web/views/v2/status/info.py
Normal file
@@ -0,0 +1,56 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import Response
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.apispec.decorators import apidocs
|
||||
from ahriman.web.schemas import InfoV2Schema
|
||||
from ahriman.web.server_info import server_info
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class InfoView(BaseView):
|
||||
"""
|
||||
web service information view
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION: ClassVar[UserAccess] = UserAccess.Unauthorized
|
||||
ROUTES = ["/api/v2/info"]
|
||||
|
||||
@apidocs(
|
||||
tags=["Status"],
|
||||
summary="Service information",
|
||||
description="Perform basic service health check and returns its information",
|
||||
permission=GET_PERMISSION,
|
||||
schema=InfoV2Schema,
|
||||
)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get service information
|
||||
|
||||
Returns:
|
||||
Response: 200 with service information object
|
||||
"""
|
||||
response = await server_info(self)
|
||||
return self.json_response(response)
|
||||
Reference in New Issue
Block a user