VBV-102: refactor server urls

This commit is contained in:
Daniel Egger 2022-09-02 14:28:46 +02:00
parent db01be1726
commit d5f4b37cfe
22 changed files with 85 additions and 127 deletions

View File

@ -7,7 +7,7 @@
<!-- workaround for vitejs bundling -> reference https:// -->
<link href="https://vbv-lernwelt.control.iterativ.ch/static/fonts/BuenosAires/stylesheet.css" rel="stylesheet">
<script defer src="/learnpath/icons/"></script>
<script defer src="/server/core/icons/"></script>
<!-- end workaround -->
<title>myVBV</title>

View File

@ -8,7 +8,7 @@ def main():
client.get('http://localhost:8000/')
client.post(
'http://localhost:8000/core/login/',
'http://localhost:8000/api/core/login/',
json={
'username': 'admin',
'password': 'test',
@ -16,7 +16,7 @@ def main():
)
response = client.get(
'http://localhost:8000/learnpath/api/page/unit-test-lernpfad/',
'http://localhost:8000/api/learnpath/page/unit-test-lernpfad/',
)
print(response.status_code)
print(response.json())

View File

@ -19,13 +19,13 @@ export const useLearningPathStore = defineStore({
if (this.learningPath && !reload) {
return this.learningPath;
}
const learningPathData = await itGet(`/learnpath/api/page/${slug}/`);
const learningPathData = await itGet(`/api/learnpath/page/${slug}/`);
const completionData = await itGet(`/api/completion/learning_path/${learningPathData.translation_key}/`);
if (!learningPathData) {
throw `No learning path found with: ${slug}`;
}
this.learningPath = LearningPath.fromJson(learningPathData, completionData);
return this.learningPath;
},

View File

@ -1,8 +1,8 @@
import * as log from 'loglevel';
import * as log from "loglevel";
import {defineStore} from 'pinia'
import {itGet, itPost} from '@/fetchHelpers';
import {useAppStore} from '@/stores/app';
import { defineStore } from "pinia";
import { itGet, itPost } from "@/fetchHelpers";
import { useAppStore } from "@/stores/app";
// typed state https://stackoverflow.com/questions/71012513/when-using-pinia-and-typescript-how-do-you-use-an-action-to-set-the-state
export type UserState = {
@ -34,7 +34,7 @@ export const useUserStore = defineStore({
actions: {
handleLogin(username: string, password: string, next='/') {
if (username && password) {
itPost('/core/login/', {
itPost('/api/core/login/', {
username,
password,
}).then((data) => {
@ -49,7 +49,7 @@ export const useUserStore = defineStore({
}
},
handleLogout() {
itPost('/core/logout/', {})
itPost('/api/core/logout/', {})
.then(data => {
Object.assign(this, initialUserState);
window.location.href = '/';

View File

@ -190,7 +190,7 @@ function log(data: any) {
<it-icon-message class="w-16 h-16 text-orange-500"/>
</div>
<div class="inline-flex flex-col text-green-500">
<div class="inline-flex flex-col text-blue-400">
ls-network big
<it-icon-ls-network class="w-16 h-16"/>
</div>

View File

@ -1,7 +1,7 @@
export const login = (username, password) => {
cy.request({
method: 'POST',
url: '/core/login/',
url: '/api/core/login/',
body: { username, password },
})
}

View File

@ -1,11 +0,0 @@
from django.conf import settings
from rest_framework.routers import DefaultRouter, SimpleRouter
if settings.DEBUG:
router = DefaultRouter()
else:
router = SimpleRouter()
app_name = "api"
urlpatterns = router.urls

View File

@ -78,7 +78,6 @@ THIRD_PARTY_APPS = [
"rest_framework.authtoken",
"corsheaders",
"drf_spectacular",
"django_htmx",
'wagtail.contrib.forms',
'wagtail.contrib.redirects',
@ -288,7 +287,7 @@ EMAIL_TIMEOUT = 5
# ADMIN
# ------------------------------------------------------------------------------
# Django Admin URL.
ADMIN_URL = "admin/"
ADMIN_URL = "server/admin/"
# https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = [("""Daniel Egger""", "info@iterativ.ch")]
# https://docs.djangoproject.com/en/dev/ref/settings/#managers
@ -454,7 +453,17 @@ REST_FRAMEWORK = {
CORS_URLS_REGEX = r"^/api/.*$"
# django-csp
CSP_DEFAULT_SRC = ("'self'", "'unsafe-inline'", 'ws://localhost:5173', 'localhost:8000', 'blob:', 'data:', 'http://*')
CSP_DEFAULT_SRC = [
"'self'",
"'unsafe-inline'",
'ws://localhost:5173',
'ws://127.0.0.1:5173',
'localhost:8000',
'localhost:8001',
'blob:',
'data:',
'http://*'
]
CSP_FRAME_ANCESTORS = ("'self'",)
# By Default swagger ui is available only to admin user. You can change permission classs to change that

View File

@ -5,19 +5,19 @@ from django.contrib.auth.decorators import user_passes_test
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import include, path, re_path
from django.views import defaults as default_views
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from ratelimit.exceptions import Ratelimited
from rest_framework.authtoken.views import obtain_auth_token
from wagtail import urls as wagtail_urls
from wagtail.admin import urls as wagtailadmin_urls
from wagtail.documents import urls as wagtaildocs_urls
from vbv_lernwelt.completion.views import request_learning_path_completion, request_circle_completion, \
mark_circle_completion
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
from vbv_lernwelt.core.views import (
rate_limit_exceeded_view,
permission_denied_view,
check_rate_limit, cypress_reset_view, vue_home, vue_login, me_user_view, vue_logout, )
from .wagtail_api import wagtail_api_router
check_rate_limit, cypress_reset_view, vue_home, vue_login, me_user_view, vue_logout, generate_web_component_icons, )
from vbv_lernwelt.learnpath.views import page_api_view
def raise_example_error(request):
@ -31,39 +31,43 @@ def raise_example_error(request):
# fmt: off
urlpatterns = [
path('admin/raise_error/', user_passes_test(lambda u: u.is_superuser, login_url='/login/')(raise_example_error), ),
path(settings.ADMIN_URL, admin.site.urls),
path("checkratelimit/", check_rate_limit),
# wagtail urls
path('server/cms/', include(wagtailadmin_urls)),
path('server/documents/', include(wagtaildocs_urls)),
path('server/pages/', include(wagtail_urls)),
# user management
path("sso/", include("vbv_lernwelt.sso.urls")),
path('cms/', include(wagtailadmin_urls)),
path('documents/', include(wagtaildocs_urls)),
path('pages/', include(wagtail_urls)),
path('learnpath/', include("vbv_lernwelt.learnpath.urls")),
path('api/completion/', include("vbv_lernwelt.completion.urls")),
re_path(r'api/core/me/$', me_user_view, name='me_user_view'),
re_path(r'core/login/$', django_view_authentication_exempt(vue_login), name='vue_login'),
re_path(r'core/logout/$', vue_logout, name='vue_logout'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
re_path(r'api/core/login/$', django_view_authentication_exempt(vue_login), name='vue_login'),
re_path(r'api/core/logout/$', vue_logout, name='vue_logout'),
# core
re_path(r"server/core/icons/$", generate_web_component_icons, name="generate_web_component_icons"),
# learnpath
path(r"api/learnpath/page/<slug:slug>/", page_api_view, name="page_api_view"),
# completion
path(r"api/completion/circle/<uuid:circle_key>/", request_circle_completion, name="request_circle_completion"),
path(r"api/completion/learning_path/<uuid:learning_path_key>/", request_learning_path_completion, name="request_learning_path_completion"),
path(r"api/completion/circle/mark/", mark_circle_completion, name="mark_circle_completion"),
# testing and debug
path('server/raise_error/', user_passes_test(lambda u: u.is_superuser, login_url='/login/')(raise_example_error), ),
path("server/checkratelimit/", check_rate_limit),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
# Static file serving when using Gunicorn + Uvicorn for local web socket development
urlpatterns += staticfiles_urlpatterns()
# API URLS
urlpatterns += [
# API base url
path("api/", include("config.api_router")),
path('wagtailapi/v2/', wagtail_api_router.urls),
# DRF auth token
path("auth-token/", obtain_auth_token),
path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"),
path("api/docs/", SpectacularSwaggerView.as_view(url_name="api-schema"), name="api-docs",),
]
if settings.APP_ENVIRONMENT != 'production':
urlpatterns += [
re_path(r'core/cypressreset/$', cypress_reset_view, name='cypress_reset_view'),
re_path(r'api/core/cypressreset/$', cypress_reset_view, name='cypress_reset_view'),
]
# fmt: on
@ -107,4 +111,4 @@ if settings.DEBUG:
# serve everything else via the vue app
urlpatterns += [re_path(r'^.*$', vue_home, name='home')]
urlpatterns += [re_path(r'^(?!.*(server/|api/|sso/)).*$', vue_home, name='home')]

View File

@ -1,16 +0,0 @@
from wagtail.api.v2.router import WagtailAPIRouter
from wagtail.api.v2.views import PagesAPIViewSet
from wagtail.documents.api.v2.views import DocumentsAPIViewSet
from wagtail.images.api.v2.views import ImagesAPIViewSet
# Create the router. "wagtailapi" is the URL namespace
wagtail_api_router = WagtailAPIRouter('wagtailapi')
# Add the three endpoints using the "register_endpoint" method.
# The first parameter is the name of the endpoint (eg. pages, images). This
# is used in the URL of the endpoint
# The second parameter is the endpoint class that handles the requests
wagtail_api_router.register_endpoint('pages', PagesAPIViewSet)
wagtail_api_router.register_endpoint('images', ImagesAPIViewSet)
wagtail_api_router.register_endpoint('documents', DocumentsAPIViewSet)

View File

@ -3,7 +3,7 @@ from django.test import TestCase, override_settings
class RateLimitTest(TestCase):
def setUp(self):
self.url = "/checkratelimit/"
self.url = "/server/checkratelimit/"
@override_settings(RATELIMIT_ENABLE=True)
def test_checkView_rateLimitAfter5Requests(self):

View File

@ -113,8 +113,6 @@ django-extensions==3.1.5
# via -r requirements-dev.in
django-filter==21.1
# via wagtail
django-htmx==1.9.0
# via -r requirements.in
django-ipware==4.0.2
# via -r requirements.in
django-model-utils==4.2.0

View File

@ -19,7 +19,6 @@ djangorestframework # https://github.com/encode/django-rest-framework
django-cors-headers # https://github.com/adamchainz/django-cors-headers
# DRF-spectacular for api documentation
drf-spectacular
django-htmx
dj-database-url
django-click
django-ratelimit

View File

@ -71,8 +71,6 @@ django-csp==3.7
# via -r requirements.in
django-filter==21.1
# via wagtail
django-htmx==1.9.0
# via -r requirements.in
django-ipware==4.0.2
# via -r requirements.in
django-model-utils==4.2.0

View File

@ -1,10 +0,0 @@
from django.urls import path
from vbv_lernwelt.completion.views import request_circle_completion, mark_circle_completion, \
request_learning_path_completion
urlpatterns = [
path(r"circle/<uuid:circle_key>/", request_circle_completion, name="request_circle_completion"),
path(r"learning_path/<uuid:learning_path_key>/", request_learning_path_completion, name="request_learning_path_completion"),
path(r"circle/mark/", mark_circle_completion, name="mark_circle_completion"),
]

View File

@ -1,4 +1,6 @@
# Create your views here.
import glob
from pathlib import Path
import requests
import structlog
@ -23,9 +25,7 @@ logger = structlog.get_logger(__name__)
@django_view_authentication_exempt
@ensure_csrf_cookie
def vue_home(request):
def vue_home(request, *args):
if settings.IT_SERVE_VUE:
try:
res = requests.get(f'{settings.IT_SERVE_VUE_URL}{request.get_full_path()}')
@ -114,3 +114,24 @@ def cypress_reset_view(request):
call_command('cypress_reset')
return HttpResponseRedirect('/admin/')
@django_view_authentication_exempt
def generate_web_component_icons(request):
svg_files = []
for filepath in glob.iglob(f'{settings.APPS_DIR}/static/icons/*.svg'):
with open(filepath, 'r') as f:
filename = Path(filepath).stem
elementname = 'it-' + filename
svg_files.append({
'filepath': filepath,
'content': f.read(),
'filename': filename,
'elementname': elementname,
'classname': filename.replace('-', '_'),
})
return render(
request, "core/icons.html",
context={'svg_files': svg_files},
content_type="application/javascript"
)

View File

@ -18,7 +18,7 @@ class TestRetrieveLearingPathContents(APITestCase):
def test_get_learnpathPage(self):
learning_path = LearningPath.objects.get(slug='unit-test-lernpfad')
response = self.client.get('/learnpath/api/page/unit-test-lernpfad/')
response = self.client.get('/api/learnpath/page/unit-test-lernpfad/')
print(response)
self.assertEqual(response.status_code, 200)
data = response.json()

View File

@ -1,8 +0,0 @@
from django.urls import path, re_path
from .views import generate_web_component_icons, page_api_view
urlpatterns = [
path(r"api/page/<slug:slug>/", page_api_view, name="page_api_view"),
re_path(r"icons/$", generate_web_component_icons, name="generate_web_component_icons"),
]

View File

@ -1,17 +1,11 @@
# Create your views here.
import glob
from pathlib import Path
import structlog
from django.conf import settings
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from rest_framework.decorators import api_view
from rest_framework.response import Response
from wagtail.models import Page
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
logger = structlog.get_logger(__name__)
@ -27,22 +21,3 @@ def page_api_view(request, slug):
return Response({"error": str(e)}, status=404)
@django_view_authentication_exempt
def generate_web_component_icons(request):
svg_files = []
for filepath in glob.iglob(f'{settings.APPS_DIR}/static/icons/*.svg'):
with open(filepath, 'r') as f:
filename = Path(filepath).stem
elementname = 'it-' + filename
svg_files.append({
'filepath': filepath,
'content': f.read(),
'filename': filename,
'elementname': elementname,
'classname': filename.replace('-', '_'),
})
return render(
request, "learnpath/icons.html",
context={'svg_files': svg_files},
content_type="application/javascript"
)

View File

@ -19,8 +19,7 @@
<!-- Le javascript
================================================== -->
{# Placed at the top of the document so pages load faster with defer #}
<script defer src="https://unpkg.com/htmx.org@1.6.1"></script>
<script defer src="/learnpath/icons/"></script>
<script defer src="/server/core/icons/"></script>
{% block javascript %}
<!-- Your stuff: Third-party javascript libraries go here -->

View File

@ -17,7 +17,7 @@
</style>
<script defer src="/learnpath/icons/"></script>
<script defer src="/server/core/icons/"></script>
<script type="text/javascript">
if (document.location.href.endsWith('/')) {