Merge branch 'develop' of bitbucket.org:iterativ/vbv_lernwelt into develop

# Conflicts:
#	server/README.md
This commit is contained in:
Lorenz Padberg 2022-05-23 14:40:21 +02:00
commit 342d0bdd02
26 changed files with 139 additions and 394 deletions

4
.gitignore vendored
View File

@ -273,9 +273,6 @@ vbv_lernwelt/media/
.pytest_cache/ .pytest_cache/
.ipython/ .ipython/
project.css
project.min.css
tailwind-output.css
vendors.js vendors.js
*.min.js *.min.js
.env .env
@ -285,3 +282,4 @@ cypress/videos
cypress/screenshots cypress/screenshots
cypress/test-reports cypress/test-reports
/server/vbv_lernwelt/static/css/tailwind.css

View File

@ -2,18 +2,27 @@
Project setup is based on [cookiecutter-django](https://github.com/cookiecutter/cookiecutter-django) project template. Project setup is based on [cookiecutter-django](https://github.com/cookiecutter/cookiecutter-django) project template.
## Deployment to CapRover ## Run for development
``` ```bash
# run deploy script # run tailwind cli (on project root folder!)
./caprover_deploy.sh npm run tailwind
```
# run vue vite dev server
cd client && npm run dev
# run django dev server
cd server && python manage.py runserver
```
## Installation ## Installation
See `.tool-versions` file for used django and node version See `.tool-versions` file for used django and node version
### Server part
Run every sub command in the `server` directory
Create a new PostgreSQL database and role Create a new PostgreSQL database and role
```bash ```bash
@ -29,8 +38,6 @@ export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt'
Set `VBV_DJANGO_READ_DOT_ENV_FILE=True` to make the config read the `example.env` file (with direnv!?). Set `VBV_DJANGO_READ_DOT_ENV_FILE=True` to make the config read the `example.env` file (with direnv!?).
Apply migrations and run async server
```bash ```bash
python manage.py migrate python manage.py migrate
@ -38,17 +45,34 @@ python manage.py migrate
python manage.py runserver python manage.py runserver
# or async server # or async server
uvicorn config.asgi:application --host 0.0.0.0 --reload # uvicorn config.asgi:application --host 0.0.0.0 --reload
``` ```
## SASS Live-Reloading ### Client part
Run every command in the `client` directory
```bash ```bash
# start django server... npm install
# live reloading is hardcoded via proxy to port 8000 -> django server must get started manually
npm run dev
# open site with http://localhost:3000 # run dev server
npm run dev
```
### General part
Cypress and TailwindCSS ist installed for client and server, so there is this package.json on the project root directory
```bash
npm install
```
## Deployment to CapRover
```
# run deploy script
./caprover_deploy.sh
``` ```
## IntelliJ Configuration ## IntelliJ Configuration

View File

@ -4,6 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="/static/css/tailwind.css" rel="stylesheet">
<title>Vite App</title> <title>Vite App</title>
</head> </head>
<body> <body>

View File

@ -41,7 +41,6 @@
"sass": "^1.50.1", "sass": "^1.50.1",
"sass-loader": "^12.6.0", "sass-loader": "^12.6.0",
"start-server-and-test": "^1.14.0", "start-server-and-test": "^1.14.0",
"tailwindcss": "^3.0.24",
"typescript": "~4.6.3", "typescript": "~4.6.3",
"vite": "^2.9.1", "vite": "^2.9.1",
"vitest": "^0.8.1", "vitest": "^0.8.1",

View File

@ -1,7 +1,6 @@
module.exports = { module.exports = {
plugins: { plugins: {
'postcss-import': {}, 'postcss-import': {},
tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
} }

View File

@ -1,5 +1 @@
@import "fonts"; @import "fonts";
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,6 +1,6 @@
import type {NavigationGuardWithThis, RouteLocationNormalized} from 'vue-router'; import type {NavigationGuardWithThis, RouteLocationNormalized} from 'vue-router';
import {useUserStore} from '@/stores/user'
import type {UserState} from '@/stores/user' import type {UserState} from '@/stores/user'
import {useUserStore} from '@/stores/user'
import type {Store} from 'pinia'; import type {Store} from 'pinia';
const cookieName = 'loginStatus' const cookieName = 'loginStatus'

View File

@ -12,6 +12,7 @@ const router = createRouter({
name: 'home', name: 'home',
component: HomeView, component: HomeView,
meta: { meta: {
// no login required -> so `public === true`
public: true public: true
} }
}, },
@ -25,6 +26,10 @@ const router = createRouter({
public: true public: true
} }
}, },
{
path: '/analyse',
component: () => import('../views/CircleAnalyseExampleView.vue'),
},
{ {
path: '/profile', path: '/profile',
component: () => import('../views/ProfileView.vue'), component: () => import('../views/ProfileView.vue'),

View File

@ -0,0 +1,34 @@
<script>
import axios from 'axios';
export default {
data() {
return {
count: 0,
circleData: {}
}
},
mounted() {
console.log('CircleAnalyseExampleView mounted');
axios({
method: 'get',
url: 'http://localhost:8000/wagtailapi/v2/pages/?type=learnpath.Circle&slug=analyse&fields=title,description,learning_sequences'
}).then((response) => {
this.circleData = response.data.items[0];
});
}
}
</script>
<template>
<div class="circle">
<h1 class="text-3xl font-bold underline">
Hello world!
</h1>
</div>
</template>
<style scoped>
</style>

View File

@ -30,13 +30,5 @@ export default ({mode}) => {
'@': fileURLToPath(new URL('./src', import.meta.url)), '@': fileURLToPath(new URL('./src', import.meta.url)),
}, },
}, },
server: {
proxy: {
'^.*': process.env.VITE_PROXY_TARGET_BASE,
'/sso': process.env.VITE_PROXY_TARGET_BASE,
'/api': process.env.VITE_PROXY_TARGET_BASE,
'/todo': process.env.VITE_PROXY_TARGET_BASE,
}
},
}) })
} }

Binary file not shown.

View File

@ -6,12 +6,11 @@
"cypress:open": "cypress open", "cypress:open": "cypress open",
"cypress:run": "cypress run", "cypress:run": "cypress run",
"cypress:ci": "cypress run --config baseUrl=http://localhost:8001", "cypress:ci": "cypress run --config baseUrl=http://localhost:8001",
"cypress:ci:open": "cypress open --config baseUrl=http://localhost:8001" "cypress:ci:open": "cypress open --config baseUrl=http://localhost:8001",
"tailwind": "tailwindcss -i ./tailwind/input.css -o ./server/vbv_lernwelt/static/css/tailwind.css --watch"
}, },
"devDependencies": { "devDependencies": {
"cypress": "^9.4.1" "cypress": "^9.4.1",
},
"dependencies": {
"tailwindcss": "^3.0.24" "tailwindcss": "^3.0.24"
} }
} }

View File

@ -1,72 +0,0 @@
# VBV Lernwelt
Project setup is based on [cookiecutter-django](https://github.com/cookiecutter/cookiecutter-django) project template.
## Installation
See `.tool-versions` file for used django and node version
Create a new PostgreSQL database and role
```bash
createdb vbv_lernwelt
createuser vbv_lernwelt
```
Set the environment variable accordingly
```bash
export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt'
```
Set `VBV_DJANGO_READ_DOT_ENV_FILE=True` to make the config read the `example.env` file (with direnv!?).
Apply migrations and run async server
```bash
python manage.py migrate
# sync server
python manage.py runserver
# or async server
uvicorn config.asgi:application --host 0.0.0.0 --reload
```
## SASS Live-Reloading
```bash
# start django server...
# live reloading is hardcoded via proxy to port 8000 -> django server must get started manually
npm run dev
# open site with http://localhost:3000
```
## IntelliJ Configuration
* In the .idea/vbv_lernwelt.iml file change the module type to "PYTHON_MODULE".
* Add django facet in "Project Structure".
* Run configuration with "Python -> server.py" to have async debugging support.
## Wagtail API intro
get all pages:
http://localhost:8000/api/v2/pages/
get Analyse Circle (the one with the most demo data)
http://localhost:8000/api/v2/pages/?title=Analyse
Get Circles only
http://localhost:8000/api/v2/pages/?type=learnpath.Circle
Get All Contents from that circle:
http://localhost:8000/api/v2/pages/?child_of=11

View File

@ -201,6 +201,8 @@ MEDIA_ROOT = str(APPS_DIR / "media")
# https://docs.djangoproject.com/en/dev/ref/settings/#media-url # https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = "/media/" MEDIA_URL = "/media/"
IT_SERVE_VUE = env.bool("IT_SERVE_VUE", DEBUG)
IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", 'http://localhost:3000')
# WAGTAIL # WAGTAIL
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -5,24 +5,23 @@ from django.contrib import admin
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import include, path from django.urls import include, path, re_path
from django.views import defaults as default_views from django.views import defaults as default_views
from django.views.generic import TemplateView
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from grapple import urls as grapple_urls
from ratelimit.exceptions import Ratelimited from ratelimit.exceptions import Ratelimited
from rest_framework.authtoken.views import obtain_auth_token from rest_framework.authtoken.views import obtain_auth_token
from wagtail.admin import urls as wagtailadmin_urls
from wagtail.core import urls as wagtail_urls
from wagtail.documents import urls as wagtaildocs_urls
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
from vbv_lernwelt.core.views import ( from vbv_lernwelt.core.views import (
rate_limit_exceeded_view, rate_limit_exceeded_view,
permission_denied_view, permission_denied_view,
check_rate_limit, check_rate_limit, vue_home,
) )
from wagtail.admin import urls as wagtailadmin_urls from .wagtail_api import api_router
from wagtail.core import urls as wagtail_urls
from wagtail.documents import urls as wagtaildocs_urls
from grapple import urls as grapple_urls
from .api import api_router
def raise_example_error(request): def raise_example_error(request):
@ -36,7 +35,6 @@ def raise_example_error(request):
# fmt: off # fmt: off
urlpatterns = [ urlpatterns = [
path("", django_view_authentication_exempt(TemplateView.as_view(template_name="pages/home.html")), name="home"),
path('admin/raise_error/', user_passes_test(lambda u: u.is_superuser, login_url='/login/')(raise_example_error), ), 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(settings.ADMIN_URL, admin.site.urls),
path("checkratelimit/", check_rate_limit), path("checkratelimit/", check_rate_limit),
@ -59,7 +57,7 @@ if settings.ALLOW_LOCAL_LOGIN:
urlpatterns += [ urlpatterns += [
# API base url # API base url
path("api/", include("config.api_router")), path("api/", include("config.api_router")),
path('api/v2/', api_router.urls), path('wagtailapi/v2/', api_router.urls),
# DRF auth token # DRF auth token
path("auth-token/", obtain_auth_token), path("auth-token/", obtain_auth_token),
@ -106,3 +104,8 @@ if settings.DEBUG:
import debug_toolbar import debug_toolbar
urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
# serve everything else via the vue app
urlpatterns += [re_path(r'^.*$', vue_home, name='home')]

View File

@ -1,6 +1,8 @@
export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt' export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt'
#export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR #export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR
export VBV_DJANGO_DEBUG=True export VBV_DJANGO_DEBUG=True
# oauth is for the moment not used
export OAUTH_CLIENT_ID=iterativ export OAUTH_CLIENT_ID=iterativ
export OAUTH_CLIENT_SECRET=abced-1234 export OAUTH_CLIENT_SECRET=abced-1234
export OAUTH_ACCESS_TOKEN_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token export OAUTH_ACCESS_TOKEN_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token

View File

@ -1,125 +0,0 @@
////////////////////////////////
// Setup
////////////////////////////////
// Gulp and package
const { src, dest, parallel, series, watch } = require('gulp')
const pjson = require('./package.json')
// Plugins
const autoprefixer = require('autoprefixer')
const browserSync = require('browser-sync').create()
const cssnano = require ('cssnano')
const pixrem = require('pixrem')
const plumber = require('gulp-plumber')
const postcss = require('gulp-postcss')
const reload = browserSync.reload
const rename = require('gulp-rename')
const sass = require('gulp-sass')(require('sass'))
// const spawn = require('child_process').spawn
const uglify = require('gulp-uglify-es').default
// Relative paths function
function pathsConfig(appName) {
this.app = `./${pjson.name}`
const vendorsRoot = 'node_modules'
return {
app: this.app,
templates: `${this.app}/templates`,
css: `${this.app}/static/css`,
sass: `${this.app}/static/sass`,
fonts: `${this.app}/static/fonts`,
images: `${this.app}/static/images`,
js: `${this.app}/static/js`,
}
}
var paths = pathsConfig()
////////////////////////////////
// Tasks
////////////////////////////////
// Styles autoprefixing and minification
function styles() {
var processCss = [
autoprefixer(), // adds vendor prefixes
pixrem(), // add fallbacks for rem units
]
var minifyCss = [
cssnano({ preset: 'default' }) // minify result
]
return src(`${paths.sass}/project.scss`)
.pipe(sass({
includePaths: [
paths.sass
]
}).on('error', sass.logError))
.pipe(plumber()) // Checks for errors
.pipe(postcss(processCss))
.pipe(dest(paths.css))
.pipe(rename({ suffix: '.min' }))
.pipe(postcss(minifyCss)) // Minifies the result
.pipe(dest(paths.css))
}
// Javascript minification
function scripts() {
return src(`${paths.js}/project.js`)
.pipe(plumber()) // Checks for errors
.pipe(uglify()) // Minifies the js
.pipe(rename({ suffix: '.min' }))
.pipe(dest(paths.js))
}
// Browser sync server for live reload
function initBrowserSync() {
browserSync.init(
[
`${paths.css}/*.css`,
`${paths.js}/*.js`,
`${paths.templates}/*.html`
], {
// https://www.browsersync.io/docs/options/#option-open
// Disable as it doesn't work from inside a container
open: false,
// https://www.browsersync.io/docs/options/#option-proxy
proxy: {
target: 'localhost:8000',
proxyReq: [
function(proxyReq, req) {
// Assign proxy "host" header same as current request at Browsersync server
proxyReq.setHeader('Host', req.headers.host)
}
]
}
}
)
}
// Watch
function watchPaths() {
watch(`${paths.sass}/*.scss`, styles)
watch(`${paths.templates}/**/*.html`).on("change", reload)
watch([`${paths.js}/*.js`, `!${paths.js}/*.min.js`], scripts).on("change", reload)
}
// Generate all assets
const generateAssets = parallel(
styles,
scripts,
)
// Set up dev environment
const dev = parallel(
initBrowserSync,
watchPaths
)
exports.default = series(generateAssets, dev)
exports["generate-assets"] = generateAssets
exports["dev"] = dev

View File

@ -1,30 +0,0 @@
{
"name": "vbv_lernwelt",
"version": "0.1.0",
"devDependencies": {
"autoprefixer": "^10.4.0",
"browser-sync": "^2.27.7",
"cssnano": "^5.0.11",
"gulp": "^4.0.2",
"gulp-plumber": "^1.2.1",
"gulp-postcss": "^9.0.1",
"gulp-rename": "^2.0.0",
"gulp-sass": "^5.0.0",
"gulp-uglify-es": "^3.0.0",
"pixrem": "^5.0.0",
"postcss": "^8.3.11",
"sass": "^1.43.4",
"tailwindcss": "^3.0.18"
},
"engines": {
"node": "16"
},
"browserslist": [
"last 2 versions"
],
"scripts": {
"dev": "gulp",
"tailwind": "tailwindcss -i ./vbv_lernwelt/static/tailwind/input.css -o ./vbv_lernwelt/static/css/tailwind-output.css --watch",
"build": "tailwindcss -i ./vbv_lernwelt/static/tailwind/input.css -o ./vbv_lernwelt/static/css/tailwind-output.css && gulp generate-assets"
}
}

View File

@ -1,7 +0,0 @@
module.exports = {
content: ["./vbv_lernwelt/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}

View File

@ -1,11 +1,31 @@
# Create your views here. # Create your views here.
import requests
from django.conf import settings
from django.http import JsonResponse, HttpResponse from django.http import JsonResponse, HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.views.decorators.csrf import ensure_csrf_cookie
from ratelimit.decorators import ratelimit from ratelimit.decorators import ratelimit
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
@django_view_authentication_exempt
@ensure_csrf_cookie
def vue_home(request):
if settings.IT_SERVE_VUE:
try:
res = requests.get(f'{settings.IT_SERVE_VUE_URL}{request.get_full_path()}')
headers = res.headers
content_type = headers.get('content-type', 'text/html')
return HttpResponse(res.text, content_type=content_type)
except Exception as e:
print(f'Can not connect to vue dev server at {settings.IT_SERVE_VUE_URL}:', e)
return
# render index.html from `npm run build`
return render(request, 'index.html', {})
def permission_denied_view(request, exception): def permission_denied_view(request, exception):
return render(request, "403.html", status=403) return render(request, "403.html", status=403)

View File

@ -1 +0,0 @@
/* Project specific Javascript goes here. */

View File

@ -1,4 +0,0 @@
.task.htmx-swapping {
opacity: 0;
transition: opacity 1s ease-out;
}

View File

@ -1,108 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div class="container mx-auto">
<div class="flex justify-between flex-col md:flex-row">
<img class="w-full md:w-2/4"
src="https://www.thezebra.com/insurance-news/wp-content/uploads/2016/01/Tree-fallen-on-car-1024x682.jpeg"/>
<div class="w-full md:w-2/4 flex flex-col justify-center p-4 md:p-16">
<h2 class="text-xl md:text-3xl font-bold">Machen Sie hier eine Selbstevaluation</h2>
<p class="my-4 text-xl">Hier steht noch etwas mehr Text</p>
</div>
</div>
</div>
<div class="container mx-auto bg-blue-100 border-t-2 border-gray-900">
<div class="p-8 flex flex-col md:flex-row">
<div class="w-full md:w-1/2 xl:w-1/3 px-4">
<div class="bg-white rounded-lg overflow-hidden mb-10 shadow-md">
<img
src="https://cdn.tailgrids.com/1.0/assets/images/cards/card-01/image-01.jpg"
alt="image"
class="w-full"
/>
<div class="p-8 sm:p-9 md:p-7 xl:p-9 text-center">
<a
href="javascript:void(0)"
class="
inline-block
py-2
px-7
border border-[#E5E7EB]
rounded-full
text-base text-body-color
font-medium
hover:border-primary hover:bg-primary hover:text-white
transition
"
>
Kurs X
</a>
</div>
</div>
</div>
<div class="w-full md:w-1/2 xl:w-1/3 px-4">
<div class="bg-white rounded-lg overflow-hidden mb-10 shadow-md">
<img
src="https://cdn.tailgrids.com/1.0/assets/images/cards/card-01/image-02.jpg"
alt="image"
class="w-full"
/>
<div class="p-8 sm:p-9 md:p-7 xl:p-9 text-center">
<a
href="javascript:void(0)"
class="
inline-block
py-2
px-7
border border-[#E5E7EB]
rounded-full
text-base text-body-color
font-medium
hover:border-primary hover:bg-primary hover:text-white
transition
"
>
Kurs Y
</a>
</div>
</div>
</div>
<div class="w-full md:w-1/2 xl:w-1/3 px-4">
<div class="bg-white rounded-lg overflow-hidden mb-10 shadow-md">
<img
src="https://cdn.tailgrids.com/1.0/assets/images/cards/card-01/image-03.jpg"
alt="image"
class="w-full"
/>
<div class="p-8 sm:p-9 md:p-7 xl:p-9 text-center">
<a
href="javascript:void(0)"
class="
inline-block
py-2
px-7
border border-[#E5E7EB]
rounded-full
text-base text-body-color
font-medium
hover:border-primary hover:bg-primary hover:text-white
transition
"
>
Kurs Z
</a>
</div>
</div>
</div>
</div>
<p class="p-8 text-right">weitere Kurse entdecken und buchen</p>
<div class="border-t-2 border-blue-400 m-8 p-8"></div>
</div>
{% endblock %}

18
tailwind.config.js Normal file
View File

@ -0,0 +1,18 @@
module.exports = {
content: [
'./client/index.html',
'./client/src/**/*.{vue,js,ts,jsx,tsx}',
'./server/vbv_lernwelt/**/*.{html,js}',
],
theme: {
colors: {
'white': '#FFFFFF',
'blue-dark': '#00224D'
},
fontFamily: {
sans: ['BuenosAires', 'sans-serif'],
},
extend: {},
},
plugins: [],
}