feat: add ability to disable specific routes (#119)

This commit is contained in:
2023-12-15 14:34:03 +02:00
committed by GitHub
parent c54b14b833
commit e784032bc6
19 changed files with 128 additions and 21 deletions

View File

@ -342,6 +342,10 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"min": 0,
"max": 65535,
},
"service_only": {
"type": "boolean",
"coerce": "boolean",
},
"static_path": {
"type": "path",
"coerce": "absolute_path",

View File

@ -25,18 +25,20 @@ from pkgutil import ModuleInfo, iter_modules
from types import ModuleType
from typing import Any, Type, TypeGuard
from ahriman.core.configuration import Configuration
from ahriman.web.views.base import BaseView
__all__ = ["setup_routes"]
def _dynamic_routes(module_root: Path) -> dict[str, Type[View]]:
def _dynamic_routes(module_root: Path, configuration: Configuration) -> dict[str, Type[View]]:
"""
extract dynamic routes based on views
Args:
module_root(Path): root module path with views
configuration(Configuration): configuration instance
Returns:
dict[str, Type[View]]: map of the route to its view
@ -52,7 +54,9 @@ def _dynamic_routes(module_root: Path) -> dict[str, Type[View]]:
view = getattr(module, attribute_name)
if not is_base_view(view):
continue
routes.update([(route, view) for route in view.ROUTES])
view_routes = view.routes(configuration)
routes.update([(route, view) for route in view_routes])
return routes
@ -101,16 +105,16 @@ def _modules(module_root: Path) -> Generator[ModuleInfo, None, None]:
yield module_info
def setup_routes(application: Application, static_path: Path) -> None:
def setup_routes(application: Application, configuration: Configuration) -> None:
"""
setup all defined routes
Args:
application(Application): web application instance
static_path(Path): path to static files directory
configuration(Configuration): configuration instance
"""
application.router.add_static("/static", static_path, follow_symlinks=True)
application.router.add_static("/static", configuration.getpath("web", "static_path"), follow_symlinks=True)
views = Path(__file__).parent / "views"
for route, view in _dynamic_routes(views).items():
views_root = Path(__file__).parent / "views"
for route, view in _dynamic_routes(views_root, configuration).items():
application.router.add_view(route, view)

View File

@ -115,6 +115,21 @@ class BaseView(View, CorsViewMixin):
permission: UserAccess = getattr(cls, f"{method}_PERMISSION", UserAccess.Full)
return permission
@classmethod
def routes(cls, configuration: Configuration) -> list[str]:
"""
extract routes list for the view
Args:
configuration(Configuration): configuration instance
Returns:
list[str]: list of routes defined for the view. By default, it tries to read :attr:`ROUTES` option if set
and returns empty list otherwise
"""
del configuration
return cls.ROUTES
@staticmethod
def get_non_empty(extractor: Callable[[str], T | None], key: str) -> T:
"""

View File

@ -0,0 +1,41 @@
#
# Copyright (c) 2021-2023 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.core.configuration import Configuration
class StatusViewGuard:
ROUTES: list[str]
@classmethod
def routes(cls, configuration: Configuration) -> list[str]:
"""
extract routes list for the view
Args:
configuration(Configuration): configuration instance
Returns:
list[str]: list of routes defined for the view. By default, it tries to read :attr:`ROUTES` option if set
and returns empty list otherwise
"""
if configuration.getboolean("web", "service_only", fallback=False):
return []
return cls.ROUTES

View File

@ -26,9 +26,10 @@ from ahriman.models.changes import Changes
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ChangesSchema, ErrorSchema, PackageNameSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class ChangesView(BaseView):
class ChangesView(StatusViewGuard, BaseView):
"""
package changes web view

View File

@ -28,9 +28,10 @@ from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogsSchema, PackageNameSchema, RepositoryIdSchema, \
VersionedLogSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class LogsView(BaseView):
class LogsView(StatusViewGuard, BaseView):
"""
package logs web view

View File

@ -28,9 +28,10 @@ from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PackageStatusSchema, \
PackageStatusSimplifiedSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class PackageView(BaseView):
class PackageView(StatusViewGuard, BaseView):
"""
package base specific web view

View File

@ -28,9 +28,10 @@ from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageStatusSchema, PaginationSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class PackagesView(BaseView):
class PackagesView(StatusViewGuard, BaseView):
"""
global watcher view

View File

@ -24,9 +24,10 @@ from aiohttp.web import HTTPNoContent, HTTPNotFound, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PatchNameSchema, PatchSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class PatchView(BaseView):
class PatchView(StatusViewGuard, BaseView):
"""
package patch web view

View File

@ -25,9 +25,10 @@ from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PatchSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class PatchesView(BaseView):
class PatchesView(StatusViewGuard, BaseView):
"""
package patches web view

View File

@ -28,9 +28,10 @@ from ahriman.models.internal_status import InternalStatus
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, InternalStatusSchema, StatusSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class StatusView(BaseView):
class StatusView(StatusViewGuard, BaseView):
"""
web service status web view

View File

@ -24,9 +24,10 @@ from aiohttp.web import Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogSchema, PackageNameSchema, PaginationSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
class LogsView(BaseView):
class LogsView(StatusViewGuard, BaseView):
"""
package logs web view

View File

@ -146,7 +146,7 @@ def setup_server(configuration: Configuration, spawner: Spawn, repositories: lis
application.middlewares.append(exception_handler(application.logger))
application.logger.info("setup routes")
setup_routes(application, configuration.getpath("web", "static_path"))
setup_routes(application, configuration)
application.logger.info("setup CORS")
setup_cors(application)