mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender-pwa.git
synced 2025-07-16 09:38:51 +02:00
Merge remote-tracking branch 'htwk-org/development'
# Conflicts: # backend/go.mod # frontend/index.html # frontend/package-lock.json # frontend/package.json # frontend/public/themes/lara-dark-blue/theme.css # frontend/public/themes/lara-dark-blue/theme.css.map # frontend/public/themes/lara-light-blue/theme.css # frontend/public/themes/lara-light-blue/theme.css.map # frontend/src/App.vue # frontend/src/components/DarkModeSwitcher.vue # frontend/src/i18n/index.ts # frontend/src/main.ts # frontend/src/router/index.ts # frontend/src/view/CalendarLink.vue # frontend/src/view/edit/EditCalendar.vue # frontend/vite.config.ts # reverseproxy.conf # reverseproxy.local.conf # services/data-manager/main.go # services/data-manager/model/roomOccupancyModel.go # services/data-manager/service/addRoute.go # services/data-manager/service/addSchedule.go # services/data-manager/service/db/dbGroups.go # services/data-manager/service/feed/feedFunctions.go # services/data-manager/service/fetch/sport/sportFetcher.go # services/data-manager/service/fetch/v1/fetchSeminarEventService.go # services/data-manager/service/fetch/v1/fetchSeminarGroupService.go # services/data-manager/service/fetch/v2/fetcher.go # services/data-manager/service/functions/filter.go # services/data-manager/service/functions/filter_test.go # services/data-manager/service/functions/time/parse.go # services/data-manager/service/room/roomService.go # services/data-manager/service/room/roomService_test.go # services/go.sum # services/ical/service/connector/grpc/client.go
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
||||
.DS_Store
|
||||
workspace.xml
|
||||
**/.idea/
|
||||
.sass-cache/
|
||||
.sass-cache/
|
||||
/services/pb_data/
|
||||
|
122
.gitlab-ci.yml
122
.gitlab-ci.yml
@ -18,6 +18,7 @@ stages:
|
||||
- lint
|
||||
- build
|
||||
- test
|
||||
- sonarqube-check
|
||||
- oci-build
|
||||
- deploy
|
||||
- deploy-dev # New stage for development deployment
|
||||
@ -33,33 +34,82 @@ lint-frontend:
|
||||
- npm i
|
||||
- npm run lint-no-fix
|
||||
|
||||
lint-backend:
|
||||
lint-data-manager:
|
||||
stage: lint
|
||||
image: golangci/golangci-lint:latest
|
||||
rules:
|
||||
- changes:
|
||||
- backend/**/*
|
||||
- services/data-manager/**/*
|
||||
script:
|
||||
- cd backend
|
||||
- cd services/data-manager
|
||||
- go mod download
|
||||
- golangci-lint --version
|
||||
- golangci-lint run -v --skip-dirs=migrations --timeout=5m
|
||||
|
||||
lint-ical:
|
||||
stage: lint
|
||||
image: golangci/golangci-lint:latest
|
||||
rules:
|
||||
- changes:
|
||||
- services/ical/**/*
|
||||
script:
|
||||
- cd services/ical
|
||||
- go mod download
|
||||
- golangci-lint --version
|
||||
- golangci-lint run -v --skip-dirs=migrations --timeout=5m
|
||||
|
||||
|
||||
build-backend:
|
||||
build-data-manager:
|
||||
image: golang:alpine
|
||||
stage: build
|
||||
rules:
|
||||
- changes:
|
||||
- backend/**/*
|
||||
- services/data-manager/**/*
|
||||
script:
|
||||
- cd backend
|
||||
- cd services/data-manager
|
||||
- go build -o htwkalender
|
||||
artifacts:
|
||||
paths:
|
||||
- backend/htwkalender
|
||||
- backend/go.sum
|
||||
- backend/go.mod
|
||||
- data-manager/htwkalender
|
||||
- data-manager/go.sum
|
||||
- data-manager/go.mod
|
||||
|
||||
build-ical:
|
||||
image: golang:alpine
|
||||
stage: build
|
||||
rules:
|
||||
- changes:
|
||||
- services/ical/**/*
|
||||
script:
|
||||
- cd services/ical
|
||||
- go build -o htwkalender-ical
|
||||
artifacts:
|
||||
paths:
|
||||
- data-manager/htwkalender-ical
|
||||
- data-manager/go.sum
|
||||
- data-manager/go.mod
|
||||
|
||||
sonarqube-data-manager:
|
||||
stage: sonarqube-check
|
||||
image:
|
||||
name: sonarsource/sonar-scanner-cli:5.0
|
||||
entrypoint: [""]
|
||||
variables:
|
||||
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
|
||||
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
|
||||
cache:
|
||||
key: "${CI_JOB_NAME}"
|
||||
paths:
|
||||
- .sonar/cache
|
||||
script:
|
||||
- cd services/data-manager
|
||||
- sonar-scanner
|
||||
allow_failure: true
|
||||
only:
|
||||
- merge_requests
|
||||
- master
|
||||
- main
|
||||
- develop
|
||||
|
||||
build-frontend:
|
||||
image: node:lts
|
||||
@ -75,17 +125,29 @@ build-frontend:
|
||||
paths:
|
||||
- frontend/build
|
||||
|
||||
test-backend:
|
||||
test-data-manager:
|
||||
image: golang:alpine
|
||||
stage: test
|
||||
rules:
|
||||
- changes:
|
||||
- backend/**/*
|
||||
- services/data-manager/**/*
|
||||
script:
|
||||
- cd backend
|
||||
- cd services/data-manager
|
||||
- go test -v ./...
|
||||
dependencies:
|
||||
- build-backend
|
||||
- build-data-manager
|
||||
|
||||
test-ical:
|
||||
image: golang:alpine
|
||||
stage: test
|
||||
rules:
|
||||
- changes:
|
||||
- services/ical/**/*
|
||||
script:
|
||||
- cd services/ical
|
||||
- go test -v ./...
|
||||
dependencies:
|
||||
- build-ical
|
||||
|
||||
test-frontend:
|
||||
image: node:lts
|
||||
@ -100,7 +162,7 @@ test-frontend:
|
||||
dependencies:
|
||||
- lint-frontend
|
||||
|
||||
build-backend-image:
|
||||
build-data-manager-image:
|
||||
stage: oci-build
|
||||
image: docker:latest
|
||||
services:
|
||||
@ -108,7 +170,7 @@ build-backend-image:
|
||||
tags:
|
||||
- image
|
||||
variables:
|
||||
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-backend
|
||||
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-data-manager
|
||||
DOCKER_HOST: tcp://docker:2376
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
DOCKER_TLS_VERIFY: 1
|
||||
@ -116,12 +178,35 @@ build-backend-image:
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
script:
|
||||
- docker build --pull -t $IMAGE_TAG -f ./backend/Dockerfile --target prod ./backend
|
||||
- docker build --pull -t $IMAGE_TAG -f ./services/data-manager/Dockerfile --target prod ./services
|
||||
- docker push $IMAGE_TAG
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development"
|
||||
changes:
|
||||
- backend/**/*
|
||||
- services/data-manager/**/*
|
||||
|
||||
build-ical-image:
|
||||
stage: oci-build
|
||||
image: docker:latest
|
||||
services:
|
||||
- docker:dind
|
||||
tags:
|
||||
- image
|
||||
variables:
|
||||
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-ical
|
||||
DOCKER_HOST: tcp://docker:2376
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
DOCKER_TLS_VERIFY: 1
|
||||
DOCKER_CERT_PATH: "/certs/client"
|
||||
before_script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
script:
|
||||
- docker build --pull -t $IMAGE_TAG -f ./services/ical/Dockerfile --target prod ./services
|
||||
- docker push $IMAGE_TAG
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "development"
|
||||
changes:
|
||||
- services/ical/**/*
|
||||
|
||||
build-frontend-image:
|
||||
stage: oci-build
|
||||
@ -191,6 +276,7 @@ deploy-all:
|
||||
ssh -p $CI_SSH_PORT -o StrictHostKeyChecking=no -o LogLevel=ERROR $CI_SSH_USER@$CI_SSH_HOST
|
||||
"cd /home/$CI_SSH_USER/docker/htwkalender/ &&
|
||||
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY &&
|
||||
docker compose -f ./docker-compose.prod.yml down && docker compose -f ./docker-compose.prod.yml up -d --remove-orphans && docker logout"
|
||||
docker compose -f ./docker-compose.prod.yml down && docker compose -f ./docker-compose.prod.yml up -d --remove-orphans && docker logout &&
|
||||
docker exec --user root htwkalender-htwkalender-frontend-1 /bin/sh -c \"echo 'google-site-verification: $GOOGLE_VERIFICATION.html' > ./$GOOGLE_VERIFICATION.html\" "
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
|
@ -1,38 +1,31 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
/label ~bug
|
||||
|
@ -1,20 +1,13 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
/label ~feature
|
||||
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
101
backend/go.mod
101
backend/go.mod
@ -1,101 +0,0 @@
|
||||
module htwkalender
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.8.1
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/jordic/goics v0.0.0-20210404174824-5a0337b716a0
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||
github.com/pocketbase/dbx v1.10.1
|
||||
github.com/pocketbase/pocketbase v0.20.5
|
||||
golang.org/x/net v0.20.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.49.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/ganigeorgiev/fexpr v0.4.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.19 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/cobra v1.8.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
go.mongodb.org/mongo-driver v1.15.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
gocloud.dev v0.36.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/image v0.14.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/term v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.16.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/api v0.153.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
lukechampine.com/uint128 v1.3.0 // indirect
|
||||
modernc.org/cc/v3 v3.41.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.15 // indirect
|
||||
modernc.org/libc v1.37.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/sqlite v1.27.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
@ -1,199 +0,0 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"htwkalender/service/db"
|
||||
"htwkalender/service/ical"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func addFeedRoutes(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/createFeed",
|
||||
Handler: func(c echo.Context) error {
|
||||
requestBody, _ := io.ReadAll(c.Request().Body)
|
||||
result, err := ical.CreateIndividualFeed(requestBody, app)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, "Failed to create individual feed")
|
||||
}
|
||||
return c.JSON(http.StatusOK, result)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/feed",
|
||||
Handler: func(c echo.Context) error {
|
||||
token := c.QueryParam("token")
|
||||
result, err := ical.Feed(app, token)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, "Failed to get feed")
|
||||
}
|
||||
var responseWriter = c.Response().Writer
|
||||
responseWriter.Header().Set("Content-type", "text/calendar")
|
||||
responseWriter.Header().Set("charset", "utf-8")
|
||||
responseWriter.Header().Set("Content-Disposition", "inline")
|
||||
responseWriter.Header().Set("filename", "calendar.ics")
|
||||
|
||||
responseWriter.WriteHeader(http.StatusOK)
|
||||
_, err = responseWriter.Write([]byte(result))
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, "Failed to write feed")
|
||||
}
|
||||
c.Response().Writer = responseWriter
|
||||
return nil
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/api/feed",
|
||||
Handler: func(c echo.Context) error {
|
||||
token := c.QueryParam("token")
|
||||
err := db.DeleteFeed(app.Dao(), token)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusNotFound, err)
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, "Feed deleted")
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodPut,
|
||||
Path: "/api/feed",
|
||||
Handler: func(c echo.Context) error {
|
||||
token := c.QueryParam("token")
|
||||
return c.JSON(http.StatusOK, "token: "+token)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Route for Thunderbird to get calendar server information
|
||||
// Response is a 200 OK without additional content
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: "PROPFIND",
|
||||
Path: "/",
|
||||
Handler: func(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Route for Thunderbird to get calendar server information
|
||||
// Response is a 200 OK without additional content
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: "PROPFIND",
|
||||
Path: "/.well-known/caldav",
|
||||
Handler: func(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: "PROPFIND",
|
||||
Path: "/api/feed",
|
||||
Handler: func(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodHead,
|
||||
Path: "/api/feed",
|
||||
Handler: func(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, "")
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package events
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"htwkalender/service/db"
|
||||
"htwkalender/service/functions"
|
||||
)
|
||||
|
||||
func GetAllCourses(app *pocketbase.PocketBase) []string {
|
||||
return db.GetAllCourses(app)
|
||||
}
|
||||
|
||||
func GetAllCoursesForSemester(app *pocketbase.PocketBase, semester string) []string {
|
||||
return db.GetAllCoursesForSemester(app, semester)
|
||||
}
|
||||
|
||||
func GetAllCoursesForSemesterWithEvents(app *pocketbase.PocketBase, semester string) ([]string, error) {
|
||||
courses, err := db.GetAllCoursesForSemesterWithEvents(app, semester)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// remove empty courses like " " or ""
|
||||
courses = removeEmptyCourses(courses)
|
||||
return courses, nil
|
||||
}
|
||||
}
|
||||
|
||||
// removeEmptyCourses removes empty courses from the list of courses
|
||||
func removeEmptyCourses(courses []string) []string {
|
||||
var filteredCourses []string
|
||||
|
||||
for index, course := range courses {
|
||||
if !functions.OnlyWhitespace(course) || len(course) != 0 {
|
||||
filteredCourses = append(filteredCourses, courses[index])
|
||||
}
|
||||
}
|
||||
return filteredCourses
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"golang.org/x/net/html"
|
||||
"htwkalender/model"
|
||||
"htwkalender/service/date"
|
||||
"htwkalender/service/functions"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func toEvents(tables [][]*html.Node, days []string) []model.Event {
|
||||
var events []model.Event
|
||||
|
||||
for table := range tables {
|
||||
for row := range tables[table] {
|
||||
|
||||
tableData := findTableData(tables[table][row])
|
||||
if len(tableData) > 0 {
|
||||
start, _ := types.ParseDateTime(date.CreateTimeFromHourAndMinuteString(getTextContent(tableData[1])))
|
||||
end, _ := types.ParseDateTime(date.CreateTimeFromHourAndMinuteString(getTextContent(tableData[2])))
|
||||
|
||||
courses := getTextContent(tableData[7])
|
||||
name := getTextContent(tableData[3])
|
||||
if functions.OnlyWhitespace(name) {
|
||||
name = "Sonderveranstaltung"
|
||||
}
|
||||
|
||||
if len(courses) > 0 {
|
||||
for _, course := range strings.Split(courses, " ") {
|
||||
events = append(events, model.Event{
|
||||
Day: days[table],
|
||||
Week: getTextContent(tableData[0]),
|
||||
Start: start,
|
||||
End: end,
|
||||
Name: name,
|
||||
EventType: getTextContent(tableData[4]),
|
||||
Notes: getTextContent(tableData[5]),
|
||||
Prof: getTextContent(tableData[6]),
|
||||
Rooms: getTextContent(tableData[8]),
|
||||
BookedAt: getTextContent(tableData[10]),
|
||||
Course: strings.TrimSpace(course),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return events
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package functions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Filter_number(t *testing.T) {
|
||||
type args struct {
|
||||
ss []int
|
||||
test func(int) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRet []int
|
||||
}{
|
||||
{
|
||||
"filter even numbers",
|
||||
args{
|
||||
[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
func(i int) bool {
|
||||
return i%2 == 0
|
||||
},
|
||||
},
|
||||
[]int{2, 4, 6, 8, 10},
|
||||
},
|
||||
{
|
||||
"filter smaller than 5",
|
||||
args{
|
||||
[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
func(i int) bool {
|
||||
return i < 5
|
||||
},
|
||||
},
|
||||
[]int{1, 2, 3, 4},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotRet := Filter(tt.args.ss, tt.args.test); !reflect.DeepEqual(gotRet, tt.wantRet) {
|
||||
t.Errorf("filter() = %v, want %v", gotRet, tt.wantRet)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Filter_string(t *testing.T) {
|
||||
type args struct {
|
||||
ss []string
|
||||
test func(string) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRet []string
|
||||
}{
|
||||
{
|
||||
"filter contains a",
|
||||
args{
|
||||
[]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"},
|
||||
func(i string) bool {
|
||||
return strings.Contains(i, "a")
|
||||
},
|
||||
},
|
||||
[]string{"a"},
|
||||
},
|
||||
{
|
||||
"filter starts with prefix 'a'",
|
||||
args{
|
||||
[]string{"alpha", "beta", "a", "ab", "ac", "delta"},
|
||||
func(i string) bool {
|
||||
return strings.HasPrefix(i, "a")
|
||||
},
|
||||
},
|
||||
[]string{"alpha", "a", "ab", "ac"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotRet := Filter(tt.args.ss, tt.args.test); !reflect.DeepEqual(gotRet, tt.wantRet) {
|
||||
t.Errorf("filter() = %v, want %v", gotRet, tt.wantRet)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package ical
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"htwkalender/model"
|
||||
)
|
||||
|
||||
//update ical feed json
|
||||
//add uuid field
|
||||
//remove module name field
|
||||
|
||||
func MigrateFeedJson(app *pocketbase.PocketBase) error {
|
||||
|
||||
records, err := app.Dao().FindRecordsByFilter("feeds", "1=1", "-created", 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, feed := range records {
|
||||
|
||||
var modules []model.FeedCollection
|
||||
|
||||
err := json.Unmarshal([]byte(feed.GetString("modules")), &modules)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var uuidFeedCollections []model.FeedCollection
|
||||
|
||||
for _, module := range modules {
|
||||
uuid := searchUUIDForModule(app, module)
|
||||
|
||||
if uuid != "" {
|
||||
uuidFeedCollections = append(uuidFeedCollections, model.FeedCollection{UUID: uuid, Name: module.Name, Course: module.Course, UserDefinedName: module.UserDefinedName})
|
||||
}
|
||||
}
|
||||
|
||||
jsonModules, _ := json.Marshal(uuidFeedCollections)
|
||||
feed.Set("modules", string(jsonModules))
|
||||
|
||||
err = app.Dao().SaveRecord(feed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func searchUUIDForModule(app *pocketbase.PocketBase, module model.FeedCollection) string {
|
||||
var event model.Event
|
||||
err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("Name = {:name} AND course = {:course}", dbx.Params{"name": module.Name, "course": module.Course})).One(&event)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return event.UUID
|
||||
}
|
@ -15,13 +15,22 @@
|
||||
#along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
services:
|
||||
htwkalender-backend:
|
||||
image: DOCKER_REGISTRY_REPO-backend # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||
command: "--http=0.0.0.0:8090 --dir=/htwkalender/data/pb_data"
|
||||
htwkalender-data-manager:
|
||||
image: DOCKER_REGISTRY_REPO-data-manager # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||
command: "--http=0.0.0.0:8090 --dir=/htwkalender-data-manager/data/pb_data"
|
||||
pull_policy: always
|
||||
restart: always
|
||||
volumes:
|
||||
- pb_data:/htwkalender/data
|
||||
- pb_data:/htwkalender-data-manager/data
|
||||
networks:
|
||||
- "net"
|
||||
|
||||
htwkalender-ical:
|
||||
image: DOCKER_REGISTRY_REPO-ical # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||
pull_policy: always
|
||||
restart: always
|
||||
environment:
|
||||
- DATA_MANAGER_URL=htwkalender-data-manager
|
||||
networks:
|
||||
- "net"
|
||||
|
||||
@ -30,7 +39,8 @@ services:
|
||||
pull_policy: always
|
||||
restart: always
|
||||
depends_on:
|
||||
- htwkalender-backend
|
||||
- htwkalender-data-manager
|
||||
- htwkalender-ical
|
||||
networks:
|
||||
- "net"
|
||||
|
||||
@ -42,8 +52,9 @@ services:
|
||||
- ./dev_htwkalender_de.pem:/opt/bitnami/nginx/conf/dev_htwkalender_de.pem
|
||||
- ./dev_htwkalender_de.key.pem:/opt/bitnami/nginx/conf/dev_htwkalender_de.key.pem
|
||||
depends_on:
|
||||
- htwkalender-backend
|
||||
- htwkalender-data-manager
|
||||
- htwkalender-frontend
|
||||
- htwkalender-ical
|
||||
ports:
|
||||
- "443:443"
|
||||
- "80:80"
|
||||
|
@ -15,13 +15,22 @@
|
||||
#along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
services:
|
||||
htwkalender-backend:
|
||||
image: DOCKER_REGISTRY_REPO-backend # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||
command: "--http=0.0.0.0:8090 --dir=/htwkalender/data/pb_data"
|
||||
htwkalender-data-manager:
|
||||
image: DOCKER_REGISTRY_REPO-data-manager # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||
command: "--http=0.0.0.0:8090 --dir=/htwkalender-data-manager/data/pb_data"
|
||||
pull_policy: always
|
||||
restart: always
|
||||
volumes:
|
||||
- pb_data:/htwkalender/data
|
||||
- pb_data:/htwkalender-data-manager/data
|
||||
networks:
|
||||
- "net"
|
||||
|
||||
htwkalender-ical:
|
||||
image: DOCKER_REGISTRY_REPO-ical # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||
pull_policy: always
|
||||
restart: always
|
||||
environment:
|
||||
- DATA_MANAGER_URL=htwkalender-data-manager
|
||||
networks:
|
||||
- "net"
|
||||
|
||||
@ -30,7 +39,7 @@ services:
|
||||
pull_policy: always
|
||||
restart: always
|
||||
depends_on:
|
||||
- htwkalender-backend
|
||||
- htwkalender-data-manager
|
||||
networks:
|
||||
- "net"
|
||||
|
||||
@ -44,7 +53,7 @@ services:
|
||||
- ./cal.htwk-leipzig.de.pem:/opt/bitnami/nginx/conf/cal.htwk-leipzig.de.pem
|
||||
- ./cal.htwk-leipzig.de.key.pem:/opt/bitnami/nginx/conf/cal.htwk-leipzig.de.key.pem
|
||||
depends_on:
|
||||
- htwkalender-backend
|
||||
- htwkalender-data-manager
|
||||
- htwkalender-frontend
|
||||
ports:
|
||||
- "443:443"
|
||||
|
@ -15,17 +15,26 @@
|
||||
#along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
services:
|
||||
htwkalender-backend:
|
||||
htwkalender-data-manager:
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
context: ./backend
|
||||
dockerfile: ./data-manager/Dockerfile
|
||||
context: ./services
|
||||
target: dev # prod
|
||||
command: "--http=0.0.0.0:8090 --dir=/htwkalender/data/pb_data"
|
||||
command: "--http=0.0.0.0:8090 --dir=/htwkalender-data-manager/data/pb_data"
|
||||
#ports:
|
||||
# - "8090:8090"
|
||||
volumes:
|
||||
- pb_data:/htwkalender/data # for production with volume
|
||||
# - ./backend:/htwkalender/data # for development with bind mount from project directory
|
||||
- pb_data:/htwkalender-data-manager/data # for production with volume
|
||||
# - ./data-manager:/htwkalender/data # for development with bind mount from project directory
|
||||
user: "ical"
|
||||
|
||||
htwkalender-ical:
|
||||
build:
|
||||
dockerfile: ./ical/Dockerfile
|
||||
context: ./services
|
||||
target: dev # prod
|
||||
environment:
|
||||
- DATA_MANAGER_URL=htwkalender-data-manager
|
||||
|
||||
htwkalender-frontend:
|
||||
build:
|
||||
@ -46,7 +55,7 @@ services:
|
||||
- ./local-cert/dev_htwkalender_de.pem:/opt/bitnami/nginx/conf/dev_htwkalender_de.pem
|
||||
- ./local-cert/dev_htwkalender_de.key.pem:/opt/bitnami/nginx/conf/dev_htwkalender_de.key.pem
|
||||
depends_on:
|
||||
- htwkalender-backend
|
||||
- htwkalender-data-manager
|
||||
- htwkalender-frontend
|
||||
ports:
|
||||
- "80:80"
|
||||
|
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@ -10,6 +10,7 @@ lerna-debug.log*
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
.vite-ssg-temp
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@ -22,9 +22,10 @@
|
||||
rel="stylesheet"
|
||||
href="/themes/lara-light-blue/theme.css"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -50,7 +50,7 @@ http {
|
||||
index index.html index.htm;
|
||||
|
||||
#necessary to display vue subpage
|
||||
try_files $uri $uri/ /index.html;
|
||||
try_files $uri $uri.html $uri/ /index.html;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
|
6098
frontend/package-lock.json
generated
6098
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"build": "vue-tsc && vite-ssg build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
|
||||
"lint-no-fix": "eslint --ext .js,.vue --ignore-path .gitignore src",
|
||||
@ -20,7 +20,6 @@
|
||||
"@fullcalendar/timegrid": "^6.1.13",
|
||||
"@fullcalendar/vue3": "^6.1.13",
|
||||
"@tanstack/vue-query": "^5.37.1",
|
||||
"@tanstack/vue-query-devtools": "^5.37.1",
|
||||
"@types/ical": "^0.8.3",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"bson": "^5.5.1",
|
||||
@ -39,20 +38,29 @@
|
||||
"vue-router": "^4.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@types/node": "^20.12.12",
|
||||
"@vitejs/plugin-basic-ssl": "^1.1.0",
|
||||
"@unhead/vue": "^1.9.15",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@tanstack/vue-query-devtools": "^5.28.10",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"prettier": "3.2.1",
|
||||
"sass": "^1.77.2",
|
||||
"sass-loader": "^13.3.3",
|
||||
"terser": "^5.31.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.11",
|
||||
"vite-plugin-pwa": "^0.20.0",
|
||||
"vite-plugin-vue-devtools": "^7.3.1",
|
||||
"vite-ssg": "^0.23.7",
|
||||
"vite-ssg-sitemap": "^0.7.1",
|
||||
"vitest": "^1.6.0",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue-tsc": "^1.8.27"
|
||||
}
|
||||
}
|
||||
|
BIN
frontend/public/img/banner-image.png
Executable file
BIN
frontend/public/img/banner-image.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
BIN
frontend/public/promo/htwkarte.png
Normal file
BIN
frontend/public/promo/htwkarte.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
BIN
frontend/public/promo/mensa.png
Normal file
BIN
frontend/public/promo/mensa.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -18,15 +18,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MenuBar from "./components/MenuBar.vue";
|
||||
import { RouteRecordName, RouterView } from "vue-router";
|
||||
import { RouteRecordName, RouterView, useRoute, useRouter } from "vue-router";
|
||||
import { useHead, useServerHead, useServerSeoMeta } from "@unhead/vue";
|
||||
import CalendarPreview from "./components/CalendarPreview.vue";
|
||||
import moduleStore from "./store/moduleStore.ts";
|
||||
import { onMounted, provide, ref } from "vue";
|
||||
import { computed, provide, ref } from "vue";
|
||||
import { VueQueryDevtools } from "@tanstack/vue-query-devtools";
|
||||
import settingsStore from "@/store/settingsStore.ts";
|
||||
import { setTheme } from "@/helpers/theme.ts";
|
||||
import { usePrimeVue } from "primevue/config";
|
||||
const primeVue = usePrimeVue();
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const disabledPages = [
|
||||
"room-finder",
|
||||
@ -40,6 +44,56 @@ const disabledPages = [
|
||||
"room-schedule"
|
||||
];
|
||||
|
||||
// Provide canonical link for SEO
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const domain = "cal.htwk-leipzig.de"
|
||||
provide('domain', domain);
|
||||
const baseUri = "https://" + domain;
|
||||
const canonical = computed(() => `${baseUri}${router.resolve(route.name ? { name: route.name } : route).path}`);
|
||||
const title = computed(() => route.meta.label?
|
||||
`HTWKalender - ${t(String(route.meta.label))}`:
|
||||
"HTWKalender"
|
||||
);
|
||||
const description = computed(() => route.meta.description?
|
||||
t(String(route.meta.description)):
|
||||
t("description")
|
||||
);
|
||||
|
||||
useHead({
|
||||
title: title,
|
||||
link: [
|
||||
{ rel: "canonical", href: canonical},
|
||||
],
|
||||
meta: [
|
||||
{ name: "description", content: description},
|
||||
{ property: "og:description", content: description},
|
||||
]
|
||||
});
|
||||
|
||||
// SEO optimization
|
||||
useServerHead({
|
||||
title: title
|
||||
});
|
||||
useServerSeoMeta(
|
||||
{
|
||||
title: title,
|
||||
description: description,
|
||||
keywords: "HTWK Leipzig, Stundenplan, iCal, freie Räume, Lerngruppen, Sport, Prüfungen",
|
||||
// openGraph
|
||||
ogTitle: title,
|
||||
ogDescription: description,
|
||||
ogImage: `${baseUri}/img/banner-image.png`,
|
||||
ogImageType: "image/png",
|
||||
ogLocale: "de_DE",
|
||||
ogUrl: canonical,
|
||||
// twitter
|
||||
twitterCard: "summary_large_image",
|
||||
twitterSite: "@HTWKLeipzig",
|
||||
}
|
||||
);
|
||||
|
||||
const store = moduleStore();
|
||||
const mobilePage = ref(true);
|
||||
provide("mobilePage", mobilePage);
|
||||
@ -49,11 +103,12 @@ const isDisabled = (routeName: RouteRecordName | null | undefined) => {
|
||||
};
|
||||
|
||||
const updateMobile = () => {
|
||||
if (import.meta.env.SSR) return;
|
||||
mobilePage.value = window.innerWidth <= 992;
|
||||
};
|
||||
|
||||
updateMobile();
|
||||
window.addEventListener("resize", updateMobile);
|
||||
if (!import.meta.env.SSR)window.addEventListener("resize", updateMobile);
|
||||
|
||||
const settings = settingsStore;
|
||||
const emit = defineEmits(["dark-mode-toggled"]);
|
||||
@ -71,7 +126,7 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<MenuBar />
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<RouterView v-slot="{ Component, route }" class="mb-8">
|
||||
<transition mode="out-in" name="scale">
|
||||
<div :key="route.name ?? ''" class="origin-near-top">
|
||||
<component :is="Component" />
|
||||
|
@ -17,8 +17,11 @@
|
||||
import { Module } from "../model/module.ts";
|
||||
|
||||
export async function createIndividualFeed(modules: Module[]): Promise<string> {
|
||||
if (import.meta.env.SSR) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
const response = await fetch("/api/createFeed", {
|
||||
const response = await fetch("/api/feed", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@ -62,6 +65,9 @@ export async function saveIndividualFeed(
|
||||
token: string,
|
||||
modules: Module[],
|
||||
): Promise<string> {
|
||||
if (import.meta.env.SSR) {
|
||||
return "";
|
||||
}
|
||||
await fetch("/api/collections/feeds/records/" + token, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
@ -81,6 +87,9 @@ export async function saveIndividualFeed(
|
||||
}
|
||||
|
||||
export async function deleteIndividualFeed(token: string): Promise<void> {
|
||||
if (import.meta.env.SSR) {
|
||||
return;
|
||||
}
|
||||
await fetch("/api/feed?token=" + token, {
|
||||
method: "DELETE",
|
||||
})
|
||||
|
@ -19,6 +19,9 @@
|
||||
import { Module } from "../model/module.ts";
|
||||
|
||||
export async function fetchCourse(): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const courses: string[] = [];
|
||||
await fetch("/api/courses")
|
||||
.then((response) => {
|
||||
@ -39,6 +42,9 @@ export async function fetchCourse(): Promise<string[]> {
|
||||
export async function fetchCourseBySemester(
|
||||
semester: string,
|
||||
): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const courses: string[] = [];
|
||||
await fetch("/api/courses/events?semester=" + semester)
|
||||
.then((response) => {
|
||||
@ -60,6 +66,9 @@ export async function fetchModulesByCourseAndSemester(
|
||||
course: string,
|
||||
semester: string,
|
||||
): Promise<Module[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const modules: Module[] = [];
|
||||
await fetch("/api/course/modules?course=" + course + "&semester=" + semester)
|
||||
.then((response) => {
|
||||
@ -86,6 +95,9 @@ export async function fetchModulesByCourseAndSemester(
|
||||
}
|
||||
|
||||
export async function fetchAllModules(): Promise<Module[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const modules: Module[] = [];
|
||||
await fetch("/api/modules")
|
||||
.then((response) => {
|
||||
|
36
frontend/src/api/fetchEvents.ts
Normal file
36
frontend/src/api/fetchEvents.ts
Normal file
@ -0,0 +1,36 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// function to fetch course data from the API
|
||||
|
||||
export async function fetchEventTypes(): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const eventTypes: string[] = [];
|
||||
await fetch("/api/events/types")
|
||||
.then((response) => {
|
||||
return response.json() as Promise<string[]>;
|
||||
})
|
||||
.then((responseModules: string[]) => {
|
||||
responseModules.forEach((eventType: string) => {
|
||||
eventTypes.push(
|
||||
eventType,
|
||||
);
|
||||
});
|
||||
});
|
||||
return eventTypes;
|
||||
}
|
@ -17,7 +17,10 @@
|
||||
import { Module } from "../model/module";
|
||||
|
||||
export async function fetchModule(module: Module): Promise<Module> {
|
||||
// request to the backend on /api/module with query parameters name as the module name
|
||||
if (import.meta.env.SSR) {
|
||||
return new Module("", "", "", "", "", "", "", false, []);
|
||||
}
|
||||
// request to the data-manager on /api/module with query parameters name as the module name
|
||||
const request = new Request("/api/module?uuid=" + module.uuid);
|
||||
|
||||
return await fetch(request)
|
||||
|
@ -17,6 +17,9 @@
|
||||
import { AnonymizedEventDTO } from "../model/event.ts";
|
||||
|
||||
export async function fetchRoom(): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const rooms: string[] = [];
|
||||
await fetch("/api/rooms")
|
||||
.then((response) => {
|
||||
@ -33,6 +36,9 @@ export async function fetchEventsByRoomAndDuration(
|
||||
from_date: string,
|
||||
to_date: string,
|
||||
): Promise<AnonymizedEventDTO[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const events: AnonymizedEventDTO[] = [];
|
||||
await fetch(
|
||||
"/api/schedule?room=" + room + "&from=" + from_date + "&to=" + to_date,
|
||||
|
@ -18,7 +18,10 @@ import { Module } from "../model/module";
|
||||
import { Calendar } from "../model/calendar";
|
||||
|
||||
export async function getCalender(token: string): Promise<Module[]> {
|
||||
const request = new Request("/api/collections/feeds/records/" + token, {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const request = new Request("/api/feeds/records/" + token, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
|
@ -14,11 +14,14 @@
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// load free rooms as a list of strings form the backend
|
||||
// load free rooms as a list of strings form the data-manager
|
||||
export async function requestFreeRooms(
|
||||
from: string,
|
||||
to: string,
|
||||
): Promise<string[]> {
|
||||
if (import.meta.env.SSR) {
|
||||
return [];
|
||||
}
|
||||
const rooms: string[] = [];
|
||||
await fetch("/api/rooms/free?from=" + from + "&to=" + to)
|
||||
.then((response) => {
|
||||
|
@ -27,9 +27,10 @@ import {
|
||||
DataTableRowUnselectEvent,
|
||||
} from "primevue/datatable";
|
||||
import { useDialog } from "primevue/usedialog";
|
||||
import router from "../router";
|
||||
import { router } from "@/main";
|
||||
import { fetchModule } from "../api/fetchModule.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { fetchEventTypes } from "../api/fetchEvents.ts";
|
||||
|
||||
const dialog = useDialog();
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
@ -39,6 +40,9 @@ if (store.isEmpty()) {
|
||||
router.replace("/");
|
||||
}
|
||||
|
||||
const eventTypes: Ref<string[]> = ref([]);
|
||||
|
||||
|
||||
const mobilePage = inject("mobilePage") as Ref<boolean>;
|
||||
const filters = ref({
|
||||
course: {
|
||||
@ -51,7 +55,7 @@ const filters = ref({
|
||||
},
|
||||
eventType: {
|
||||
value: null,
|
||||
matchMode: FilterMatchMode.CONTAINS,
|
||||
matchMode: FilterMatchMode.IN,
|
||||
},
|
||||
prof: {
|
||||
value: null,
|
||||
@ -63,7 +67,7 @@ const loadedModules: Ref<Module[]> = ref(new Array(10));
|
||||
|
||||
const loadingData = ref(true);
|
||||
|
||||
onMounted(() => {
|
||||
onMounted( () => {
|
||||
fetchAllModules()
|
||||
.then(
|
||||
(data) =>
|
||||
@ -74,6 +78,10 @@ onMounted(() => {
|
||||
.finally(() => {
|
||||
loadingData.value = false;
|
||||
});
|
||||
|
||||
fetchEventTypes().then((data) => {
|
||||
eventTypes.value = data;
|
||||
});
|
||||
});
|
||||
|
||||
const ModuleInformation = defineAsyncComponent(
|
||||
@ -184,16 +192,20 @@ function unselectModule(event: DataTableRowUnselectEvent) {
|
||||
</Column>
|
||||
<Column
|
||||
field="eventType"
|
||||
filter-field="eventType"
|
||||
:filter-menu-style="{ width: '10rem' }"
|
||||
style="min-width: 10rem"
|
||||
:header="$t('additionalModules.eventType')"
|
||||
:show-clear-button="false"
|
||||
:show-filter-menu="false"
|
||||
>
|
||||
<template #filter="{ filterModel, filterCallback }">
|
||||
<InputText
|
||||
<MultiSelect
|
||||
v-model="filterModel.value"
|
||||
type="text"
|
||||
:options="eventTypes"
|
||||
class="p-column-filter max-w-10rem"
|
||||
@input="filterCallback()"
|
||||
style="min-width: 10rem"
|
||||
@change="filterCallback()"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="loadingData" #body>
|
||||
|
@ -110,13 +110,14 @@ const items = computed(() => [
|
||||
"
|
||||
v-bind="props.action"
|
||||
@click="navigate"
|
||||
:href="item.route"
|
||||
>
|
||||
<span :class="item.icon" />
|
||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||
</a>
|
||||
</router-link>
|
||||
<a
|
||||
v-else
|
||||
v-else-if="item.url"
|
||||
:class="
|
||||
$route.path.includes(item.info)
|
||||
? 'flex align-items-center active'
|
||||
@ -128,6 +129,18 @@ const items = computed(() => [
|
||||
<span :class="item.icon" />
|
||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||
</a>
|
||||
<span
|
||||
v-else
|
||||
:class="
|
||||
$route.path.includes(item.info)
|
||||
? 'flex align-items-center active'
|
||||
: 'flex align-items-center'
|
||||
"
|
||||
v-bind="props.action"
|
||||
>
|
||||
<span :class="item.icon" />
|
||||
<span class="ml-2 p-menuitem-label">{{ item.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template #end>
|
||||
<div class="flex align-items-stretch justify-content-center">
|
||||
|
@ -26,7 +26,7 @@ import { CalendarOptions, DatesSetArg, EventInput } from "@fullcalendar/core";
|
||||
import { fetchEventsByRoomAndDuration } from "../api/fetchRoom.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import allLocales from "@fullcalendar/core/locales-all";
|
||||
import router from "@/router";
|
||||
import { router } from "@/main";
|
||||
import { formatYearMonthDay } from "@/helpers/dates";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { watch } from "vue";
|
||||
|
@ -28,7 +28,7 @@ function setup() {
|
||||
_i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: settingsStore().locale,
|
||||
fallbackLocale: "en",
|
||||
fallbackLocale: "de",
|
||||
messages: {
|
||||
en,
|
||||
de,
|
||||
|
@ -8,6 +8,7 @@
|
||||
"faq": "FAQ",
|
||||
"imprint": "Impressum",
|
||||
"privacy": "Datenschutz",
|
||||
"description": "Dein individueller Stundenplan mit Sportevents und Prüfungen. Finde kommende Veranstaltungen oder freie Räume zum Lernen und Arbeiten.",
|
||||
"english": "Englisch",
|
||||
"german": "Deutsch",
|
||||
"japanese": "Japanisch",
|
||||
@ -29,6 +30,7 @@
|
||||
"roomSchedule": "Raumbelegung",
|
||||
"headline": "Raumbelegung",
|
||||
"detail": "Bitte wähle einen Raum aus, um die Belegung einzusehen",
|
||||
"description": "Möchtest du schnell checken, ob ein Raum frei ist? Hier kannst du die Belegung der Räume an der HTWK Leipzig einsehen.",
|
||||
"dropDownSelect": "Bitte wähle einen Raum aus",
|
||||
"noRoomsAvailable": "Keine Räume verfügbar",
|
||||
"available": "verfügbar",
|
||||
@ -38,6 +40,7 @@
|
||||
"freeRooms": {
|
||||
"freeRooms": "Freie Räume",
|
||||
"detail": "Bitte wähle einen Zeitraum aus, um alle Räume ohne Belegung anzuzeigen.",
|
||||
"description": "Freier Lerngruppenraum gesucht? Hier kannst du alle freien Räume in einem bestimmten Zeitraum an der HTWK Leipzig einsehen.",
|
||||
"searchByRoom": "Suche nach Räumen",
|
||||
"pleaseSelectDate": "Bitte wähle ein Datum aus",
|
||||
"room": "Raum",
|
||||
@ -54,7 +57,7 @@
|
||||
},
|
||||
"moduleInformation": {
|
||||
"course": "Kurs",
|
||||
"person": "Dozent",
|
||||
"person": "Dozent*in",
|
||||
"semester": "Semester",
|
||||
"module": "Modul",
|
||||
"notes": "Hinweis",
|
||||
@ -76,6 +79,7 @@
|
||||
}
|
||||
},
|
||||
"editCalendarView": {
|
||||
"description": "Mit deinem Token kannst du deinen HTWKalender jederzeit bearbeiten und sogar ins nächste Semester mitnehmen",
|
||||
"error": "Fehler",
|
||||
"invalidToken": "Ungültiger Token",
|
||||
"headline": "Bearbeite deinen HTWKalender",
|
||||
@ -114,7 +118,8 @@
|
||||
"to": " bis ",
|
||||
"of": " von insgesamt "
|
||||
},
|
||||
"eventType": "Ereignistyp"
|
||||
"eventType": "Ereignistyp",
|
||||
"scrollToTop": "Nach oben scrollen"
|
||||
},
|
||||
"renameModules": {
|
||||
"reminder": "Erinnerung",
|
||||
@ -157,17 +162,19 @@
|
||||
},
|
||||
"faqView": {
|
||||
"headline": "Fragen und Antworten",
|
||||
"description": "Falls du Fragen zum HTWKalender hast, findest du hier Antworten auf häufig gestellte Fragen und unsere Kontaktdaten.",
|
||||
"firstQuestion": "Wie funktioniert das Kalender erstellen mit dem HTWKalender?",
|
||||
"firstAnswer": "Die Webseite ermöglicht es deinen HTWK-Stundenplan in eines deiner bevorzugten Kalender-Verwaltungs-Programme (Outlook, Google Kalender, etc.) einzubinden. ",
|
||||
"secondQuestion": "Wie genau funktioniert das alles?",
|
||||
"secondAnswer": "Du wählst deinen Studiengang und das gewünschte Semester aus, danach kannst du aus den dazugehörigen Modulen wählen. Dabei kannst du einzelne Module an/abwählen wie \"Feiertage\" oder Wahlpflichtmodule, die du nicht belegst. Im letzten Schritt wird dir der Link mit dem entsprechenden Token für den von dir erstellenten Kalenders angezeigt. Mit diesem Link kannst du den Kalender abonnieren oder herunterladen. ",
|
||||
"secondAnswer": "Du wählst deinen Studiengang und das gewünschte Semester aus, danach kannst du aus den dazugehörigen Modulen wählen. Dabei kannst du einzelne Module an/abwählen wie \"Feiertage\" oder Wahlpflichtmodule, die du nicht belegst. Im letzten Schritt wird dir der Link mit dem entsprechenden Token für den von dir erstellten Kalenders angezeigt. Mit diesem Link kannst du den Kalender abonnieren oder herunterladen. ",
|
||||
"thirdQuestion": "Wie kann ich den Kalender abonnieren?",
|
||||
"thirdAnswer": {
|
||||
"tabTitle": "Google Calendar",
|
||||
"google": {
|
||||
"first": "Erstelle deinen Kalender und kopiere den Link.",
|
||||
"second": "Bei Google Kalender selbst hast du in der linken Seitenleiste eine Sektion namens \"Weitere Kalender\". Dort klickst du das kleine Pfeil-Icon rechts neben dem Schritzug. Im daraufhin erscheinenden Menü gibt es einen Punkt \"Über URL hinzufügen\", den du anklicken musst. ",
|
||||
"third": "Füge den kopierten Kalenderlink ein, klicke auf \"Kalender hinzufügen\" und du bist fertig. "
|
||||
"third": "Füge den kopierten Kalenderlink ein, klicke auf \"Kalender hinzufügen\" und du bist fertig. ",
|
||||
"fourth": "Der Kalender sollte sich nun in deiner Kalenderübersicht befinden und sich automatisch aktualisieren. Falls nicht, lade die Seite neu um den Kalender zu aktualisieren, oder überprüfe die Einstellungen des Kalenders. "
|
||||
},
|
||||
"microsoft_outlook": {
|
||||
"title": "Mircosoft Outlook",
|
||||
@ -243,9 +250,23 @@
|
||||
"seventhQuestion": "Wie lange ist mein Stundenplan bzw. der Link dorthin gültig?",
|
||||
"seventhAnswer": "Studenpläne sind erstmal nur für die ausgewählten Semester gültig, da durch Wahlpflichtmodule oder deine Planung sich Veränderungen ergeben können. Der Link ist jedoch unbegrenzt gültig und du kannst zu jedem Zeitpunkt deinen Stundenplan aktualisieren.",
|
||||
"eighthQuestion": "Preis und Entwicklung?",
|
||||
"eighthAnswer": "Die Entwicklung soll als aktives Git Projekt auch durch die Community verwaltet werden. Eine freie Version des HTWKalenders wird intern auf den Servern des FSR IMN gehostet und ist für alle HTWK-Studenten kostenlos.",
|
||||
"eighthAnswer": "Die Entwicklung soll als aktives Git Projekt durch die Community verwaltet werden. Eine freie Version des HTWKalenders wird intern auf den Servern des FSR IM gehostet und ist für alle HTWK-Studierenden kostenlos.",
|
||||
"ninthQuestion": "Wo kann ich den Quellcode einsehen und mitwirken?",
|
||||
"ninthAnswer": "Wenn du dich für die Entwicklung und den Quelltext interessierst, kannst du jederzeit als HTWK-Student daran mitarbeiten. Quelltext und weitere Informationen findest du im ",
|
||||
"ninthAnswer": "Wenn du dich für die Entwicklung und den Quelltext interessierst, kannst du jederzeit als HTWK-Student*in daran mitarbeiten. Quelltext und weitere Informationen findest du im ",
|
||||
"crossPromoQuestion": "Weitere studentische Projekte.",
|
||||
"crossPromo": {
|
||||
"teaser": "Der HTWKalender arbeitet mit anderen studentischen Projekten zusammen, um dir das Studium zu erleichtern. Vermutlich gibt es noch mehr, als die hier gelisteten. Schau doch mal dort vorbei!",
|
||||
"mensa": {
|
||||
"title": "HTWK Mensa Mate",
|
||||
"description": "Finde den Sitzplatz deiner Kommilitonen in der HTWK Mensa.",
|
||||
"link": "https://mensa.heylinus.de/"
|
||||
},
|
||||
"htwkarte": {
|
||||
"title": "HTWKarte",
|
||||
"description": "Finde dich auf dem Campus zurecht und suche nach Räumen. (in Entwicklung)",
|
||||
"link": "https://htwkarte.de/"
|
||||
}
|
||||
},
|
||||
"notFound": "Nicht gefunden, wonach du suchst?",
|
||||
"contact": "Kontakt aufnehmen"
|
||||
},
|
||||
|
@ -8,6 +8,7 @@
|
||||
"faq": "faq",
|
||||
"imprint": "imprint",
|
||||
"privacy": "privacy",
|
||||
"description": "Your individual timetable with sports events and exams. Find upcoming events or free rooms for studying and working.",
|
||||
"english": "English",
|
||||
"german": "German",
|
||||
"japanese": "Japanese",
|
||||
@ -29,6 +30,7 @@
|
||||
"roomSchedule": "room occupancy",
|
||||
"headline": "room occupancy plan",
|
||||
"detail": "Please select a room to view the occupancy.",
|
||||
"description": "Would you like to quickly check whether a room is available? Here you can see the occupancy of the rooms at HTWK Leipzig.",
|
||||
"dropDownSelect": "please select a room",
|
||||
"noRoomsAvailable": "no rooms listed",
|
||||
"available": "available",
|
||||
@ -38,6 +40,7 @@
|
||||
"freeRooms": {
|
||||
"freeRooms": "free rooms",
|
||||
"detail": "Please select a time period to display rooms that have no occupancy.",
|
||||
"description": "Looking for a free study group room? Here you can see all available rooms at HTWK Leipzig in a specific period.",
|
||||
"searchByRoom": "search by room",
|
||||
"pleaseSelectDate": "please select a date",
|
||||
"room": "room",
|
||||
@ -76,6 +79,7 @@
|
||||
}
|
||||
},
|
||||
"editCalendarView": {
|
||||
"description": "With your token, you can edit your HTW calendar at any time and even take it with you into the next semester",
|
||||
"error": "error",
|
||||
"invalidToken": "invalid token",
|
||||
"headline": "edit your HTWKalender",
|
||||
@ -114,7 +118,8 @@
|
||||
"to": " to ",
|
||||
"of": " of "
|
||||
},
|
||||
"eventType": "event type"
|
||||
"eventType": "event type",
|
||||
"scrollToTop": "scroll to top"
|
||||
},
|
||||
"renameModules": {
|
||||
"reminder": "reminder",
|
||||
@ -157,6 +162,7 @@
|
||||
},
|
||||
"faqView": {
|
||||
"headline": "faq",
|
||||
"description": "If you have any questions about the HTWKalender, you will find answers to frequently asked questions and our contact details here.",
|
||||
"firstQuestion": "How does calendar creation work with HTWKalender?",
|
||||
"firstAnswer": "The website allows you to integrate your HTWK timetable into one of your preferred calendar management programs (Outlook, Google Calendar, etc.).",
|
||||
"secondQuestion": "How does it all work exactly?",
|
||||
@ -167,7 +173,8 @@
|
||||
"google": {
|
||||
"first": "Create your calendar and copy the link.",
|
||||
"second": "In Google Calendar itself, in the left sidebar, there is a section called 'Other calendars.' There, click the small arrow icon to the right of the text. In the menu that appears, there is an option 'Add by URL' that you need to click.",
|
||||
"third": "Paste the copied calendar link, click 'Add Calendar,' and you're done."
|
||||
"third": "Paste the copied calendar link, click 'Add Calendar,' and you're done.",
|
||||
"fourth": "The calendar should now be in your calendar overview and update automatically. If not, reload the page to refresh the calendar or check the calendar settings."
|
||||
},
|
||||
"microsoft_outlook": {
|
||||
"title": "Microsoft Outlook",
|
||||
@ -246,6 +253,20 @@
|
||||
"eighthAnswer": "The development should also be managed by the community as an active Git project. A free version of the HTW calendar is hosted internally on the servers of the FSR IMN and is free of charge for all HTWK students.",
|
||||
"ninthQuestion": "Where could i find the source code?",
|
||||
"ninthAnswer": "If you want to contribute, you can do so at any time if you are a HTWK student. The source code is available on ",
|
||||
"crossPromoQuestion": "More student projects.",
|
||||
"crossPromo": {
|
||||
"teaser": "The HTWKalender collaborates with other student projects to make your studies easier. There are probably more than those listed here. Check them out!",
|
||||
"mensa": {
|
||||
"title": "HTWK Mensa Mate",
|
||||
"description": "Find the seating location of your fellow students in the HTWK Mensa Academica.",
|
||||
"link": "https://mensa.heylinus.de/"
|
||||
},
|
||||
"htwkarte": {
|
||||
"title": "HTWKarte",
|
||||
"description": "Find your way around the campus and search for rooms. (in development)",
|
||||
"link": "https://htwkarte.de/"
|
||||
}
|
||||
},
|
||||
"notFound": "Not finding what you're looking for?",
|
||||
"contact": "Get in touch"
|
||||
},
|
||||
|
@ -16,10 +16,11 @@
|
||||
|
||||
import "source-sans/source-sans-3.css";
|
||||
|
||||
import { createApp } from "vue";
|
||||
import { ViteSSG } from "vite-ssg";
|
||||
import "./style.css";
|
||||
import App from "./App.vue";
|
||||
import PrimeVue from "primevue/config";
|
||||
import Avatar from "primevue/avatar";
|
||||
import Badge from "primevue/badge";
|
||||
import Button from "primevue/button";
|
||||
import Dropdown from "primevue/dropdown";
|
||||
@ -35,7 +36,7 @@ import OverlayPanel from "primevue/overlaypanel";
|
||||
import ToggleButton from "primevue/togglebutton";
|
||||
import "primeicons/primeicons.css";
|
||||
import "primeflex/primeflex.css";
|
||||
import router from "./router";
|
||||
import routes from "./router";
|
||||
import SpeedDial from "primevue/speeddial";
|
||||
import TabView from "primevue/tabview";
|
||||
import TabPanel from "primevue/tabpanel";
|
||||
@ -56,13 +57,46 @@ import Skeleton from "primevue/skeleton";
|
||||
import Calendar from "primevue/calendar";
|
||||
import i18n from "./i18n";
|
||||
import { VueQueryPlugin } from "@tanstack/vue-query";
|
||||
import { Router } from "vue-router";
|
||||
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||
|
||||
polyfillCountryFlagEmojis();
|
||||
|
||||
const app = createApp(App);
|
||||
const pinia = createPinia();
|
||||
var router : Router;
|
||||
|
||||
export const createApp = ViteSSG(
|
||||
App,
|
||||
{
|
||||
base: import.meta.env.BASE_URL,
|
||||
routes: routes.routes,
|
||||
},
|
||||
(ctx) => {
|
||||
const { app } = ctx;
|
||||
const pinia = createPinia();
|
||||
app.use(pinia);
|
||||
|
||||
router = ctx.router;
|
||||
|
||||
router.beforeEach(async (to, from) => {
|
||||
if (import.meta.env.SSR) {
|
||||
return;
|
||||
}
|
||||
|
||||
// External redirect
|
||||
if (to.matched.some((record) => record.meta.redirect)) {
|
||||
window.location.replace(to.meta.redirect as string);
|
||||
return;
|
||||
}
|
||||
|
||||
const newLocale = to.params.locale;
|
||||
const prevLocale = from.params.locale;
|
||||
// If the locale hasn't changed, do nothing
|
||||
if (newLocale === prevLocale) {
|
||||
return;
|
||||
}
|
||||
i18n.setLocale(newLocale);
|
||||
});
|
||||
|
||||
pinia.use(piniaPluginPersistedstate);
|
||||
|
||||
@ -83,6 +117,7 @@ app.use(pinia);
|
||||
app.use(DialogService);
|
||||
i18n.setup();
|
||||
app.use(i18n.vueI18n);
|
||||
app.component("Avatar", Avatar);
|
||||
app.component("Badge", Badge);
|
||||
app.component("Button", Button);
|
||||
app.component("Menu", Menu);
|
||||
@ -111,5 +146,7 @@ app.component("Checkbox", Checkbox);
|
||||
app.component("Skeleton", Skeleton);
|
||||
app.component("Calendar", Calendar);
|
||||
app.component("OverlayPanel", OverlayPanel);
|
||||
},
|
||||
)
|
||||
|
||||
app.mount("#app");
|
||||
export {router}
|
@ -14,7 +14,7 @@
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { createMemoryHistory, RouterOptions, createWebHistory } from "vue-router";
|
||||
|
||||
const Faq = () => import("../components/FaqPage.vue");
|
||||
const AdditionalModules = () => import("../view/AdditionalModules.vue");
|
||||
@ -31,13 +31,22 @@ const FreeRooms = () => import("../view/FreeRooms.vue");
|
||||
const CalenderViewer = () => import("../view/UserCalendar.vue");
|
||||
const SettingsView = () => import("../view/SettingsView.vue");
|
||||
const NotFound = () => import("../view/NotFound.vue");
|
||||
const AdditionalModules = () => import("../view/create/AdditionalModules.vue");
|
||||
const CalendarLink = () => import("../view/CalendarLink.vue");
|
||||
const RenameModules = () => import("../view/create/RenameModules.vue");
|
||||
const RoomFinder = () => import("../view/rooms/RoomFinder.vue");
|
||||
const FreeRooms = () => import("../view/rooms/FreeRooms.vue");
|
||||
const EditCalendarView = () => import("../view/edit/EditCalendar.vue");
|
||||
const EditAdditionalModules = () =>
|
||||
import("../view/edit/EditAdditionalModules.vue");
|
||||
const EditModules = () => import("../view/edit/EditModules.vue");
|
||||
const FaqView = () => import("../view/FaqView.vue");
|
||||
|
||||
import i18n from "../i18n";
|
||||
import settingsStore from "@/store/settingsStore.ts";
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
const routes : RouterOptions = {
|
||||
history: import.meta.env.SSR ? createMemoryHistory(import.meta.env.BASE_URL) : createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
@ -53,11 +62,18 @@ const router = createRouter({
|
||||
path: "/calendar/create",
|
||||
name: "calendar-create",
|
||||
component: CourseSelection,
|
||||
meta: {
|
||||
label: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/rooms/occupancy",
|
||||
name: "room-schedule",
|
||||
component: RoomFinder,
|
||||
meta: {
|
||||
label: "roomFinderPage.roomSchedule",
|
||||
description: "roomFinderPage.description",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/rooms/occupancy/offline",
|
||||
@ -68,6 +84,10 @@ const router = createRouter({
|
||||
path: "/rooms/free",
|
||||
name: "free-rooms",
|
||||
component: FreeRooms,
|
||||
meta: {
|
||||
label: "freeRooms.freeRooms",
|
||||
description: "freeRooms.description",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/calendar/view",
|
||||
@ -77,55 +97,80 @@ const router = createRouter({
|
||||
{
|
||||
path: "/faq",
|
||||
name: "faq",
|
||||
component: Faq,
|
||||
component: FaqView,
|
||||
meta: {
|
||||
label: "faq",
|
||||
description: "faqView.description",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/additional-modules",
|
||||
name: "additional-modules",
|
||||
component: AdditionalModules,
|
||||
meta: {
|
||||
label: "createCalendar",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/edit-additional-modules",
|
||||
name: "edit-additional-modules",
|
||||
component: EditAdditionalModules,
|
||||
meta: {
|
||||
label: "editCalendar",
|
||||
description: "editCalendarView.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/edit-calendar",
|
||||
name: "edit-calendar",
|
||||
component: EditModules,
|
||||
meta: {
|
||||
label: "editCalendar",
|
||||
description: "editCalendarView.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/calendar-link",
|
||||
name: "calendar-link",
|
||||
component: CalendarLink,
|
||||
meta: {
|
||||
label: "createCalendar"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/calendar/edit",
|
||||
name: "edit",
|
||||
component: EditCalendarView,
|
||||
meta: {
|
||||
label: "editCalendar",
|
||||
description: "editCalendarView.description"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/privacy-policy",
|
||||
name: "privacy-policy",
|
||||
component: Faq,
|
||||
beforeEnter() {
|
||||
window.location.href =
|
||||
"https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/";
|
||||
},
|
||||
component: {},
|
||||
meta: {
|
||||
label: "privacy",
|
||||
redirect: "https://www.htwk-leipzig.de/hochschule/kontakt/datenschutzerklaerung/",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/imprint",
|
||||
name: "imprint",
|
||||
component: Faq,
|
||||
beforeEnter() {
|
||||
window.location.href =
|
||||
"https://www.htwk-leipzig.de/hochschule/kontakt/impressum/";
|
||||
},
|
||||
component: {},
|
||||
meta: {
|
||||
label: "imprint",
|
||||
redirect: "https://www.htwk-leipzig.de/hochschule/kontakt/impressum/",
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/rename-modules",
|
||||
name: "rename-modules",
|
||||
component: RenameModules,
|
||||
meta: {
|
||||
label: "createCalendar"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
@ -138,24 +183,6 @@ const router = createRouter({
|
||||
component: NotFound, // Replace with your NotFound component
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const newLocale = to.params.locale;
|
||||
const prevLocale = from.params.locale;
|
||||
// If the locale hasn't changed, do nothing
|
||||
if (!(newLocale === prevLocale)) {
|
||||
i18n.setLocale(newLocale);
|
||||
}
|
||||
|
||||
const userSettings = settingsStore();
|
||||
const defaultPath = userSettings.defaultPage || "/home";
|
||||
|
||||
if (to.path === "/") {
|
||||
next(defaultPath.value);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default routes;
|
||||
|
@ -17,16 +17,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import tokenStore from "../store/tokenStore.ts";
|
||||
import tokenStore from "@/store/tokenStore.ts";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { computed, onMounted } from "vue";
|
||||
import router from "../router";
|
||||
import { computed, inject, onMounted } from "vue";
|
||||
import { router } from "@/main";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const domain = window.location.hostname;
|
||||
const domain = import.meta.env.SSR ? inject<string>("domain")! : window.location.hostname;
|
||||
|
||||
const getLink = () =>
|
||||
"https://" + domain + "/api/feed?token=" + tokenStore().token;
|
||||
@ -36,7 +36,7 @@ const show = () => {
|
||||
severity: "info",
|
||||
summary: t("calendarLink.copyToastSummary"),
|
||||
detail: t("calendarLink.copyToastNotification"),
|
||||
life: 3000,
|
||||
life: 3000
|
||||
});
|
||||
};
|
||||
|
||||
@ -57,7 +57,7 @@ function copyToClipboard() {
|
||||
severity: "error",
|
||||
summary: t("calendarLink.copyToastError"),
|
||||
detail: t("calendarLink.copyToastErrorDetail"),
|
||||
life: 3000,
|
||||
life: 3000
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -65,14 +65,14 @@ function copyToClipboard() {
|
||||
const forwardToGoogle = () => {
|
||||
window.open(
|
||||
"https://calendar.google.com/calendar/u/0/r?cid=" +
|
||||
encodeURI(getLink().replace("https://", "http://")),
|
||||
encodeURI(getLink().replace("https://", "http://"))
|
||||
);
|
||||
};
|
||||
|
||||
const forwardToMicrosoft = () => {
|
||||
window.open(
|
||||
"https://outlook.live.com/owa?path=/calendar/action/compose&rru=addsubscription&name=HTWK%20Kalender&url=" +
|
||||
encodeURI(getLink()),
|
||||
encodeURI(getLink())
|
||||
);
|
||||
};
|
||||
|
||||
@ -88,22 +88,22 @@ const actions = computed(() => [
|
||||
{
|
||||
label: t("calendarLink.copyToClipboard"),
|
||||
icon: "pi pi-copy",
|
||||
command: copyToClipboard,
|
||||
command: copyToClipboard
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toGoogleCalendar"),
|
||||
icon: "pi pi-google",
|
||||
command: forwardToGoogle,
|
||||
command: forwardToGoogle
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toMicrosoftCalendar"),
|
||||
icon: "pi pi-microsoft",
|
||||
command: forwardToMicrosoft,
|
||||
command: forwardToMicrosoft
|
||||
},
|
||||
{
|
||||
label: t("calendarLink.toHTWKalendar"),
|
||||
icon: "pi pi-home",
|
||||
command: forwardToHTWKalendar,
|
||||
command: forwardToHTWKalendar
|
||||
},
|
||||
]);
|
||||
</script>
|
@ -21,13 +21,13 @@ import { computed, ComputedRef, Ref, ref, watch } from "vue";
|
||||
import {
|
||||
fetchCourseBySemester,
|
||||
fetchModulesByCourseAndSemester,
|
||||
} from "../api/fetchCourse";
|
||||
import DynamicPage from "./DynamicPage.vue";
|
||||
import ModuleSelection from "../components/ModuleSelection.vue";
|
||||
import { Module } from "../model/module.ts";
|
||||
} from "@/api/fetchCourse";
|
||||
import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import ModuleSelection from "@/components/ModuleSelection.vue";
|
||||
import { Module } from "@/model/module.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import moduleStore from "../store/moduleStore";
|
||||
import router from "../router";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { router } from "@/main";
|
||||
|
||||
async function getModules() {
|
||||
modules.value = await fetchModulesByCourseAndSemester(
|
||||
|
@ -53,7 +53,7 @@ const hasContent = computed(() => {
|
||||
<i v-if="icon" :class="icon" style="font-size: 2rem"></i>
|
||||
</div>
|
||||
<div v-if="subTitle" class="flex justify-content-center">
|
||||
<h5 class="text-2xl m-2">{{ subTitle }}</h5>
|
||||
<p class="subtitle text-2xl m-2">{{ subTitle }}</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap mx-0 gap-2 my-4 w-full lg:w-8">
|
||||
<slot name="selection" flex-specs="flex-1 m-0"></slot>
|
||||
@ -86,6 +86,18 @@ const hasContent = computed(() => {
|
||||
]"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
<div
|
||||
v-if="button"
|
||||
class="flex flex-wrap my-3 gap-2 align-items-center justify-content-end"
|
||||
>
|
||||
<Button
|
||||
:disabled="button.disabled"
|
||||
class="col-12 md:col-4"
|
||||
:icon="button.icon"
|
||||
:label="button.label"
|
||||
@click="button.onClick()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -94,4 +106,8 @@ const hasContent = computed(() => {
|
||||
.transition-rolldown {
|
||||
transition: margin-top 0.5s ease-in-out;
|
||||
}
|
||||
.subtitle {
|
||||
font-weight: 100;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -16,7 +16,8 @@ You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex align-items-center justify-content-center flex-column">
|
||||
@ -53,6 +54,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<li>
|
||||
{{ $t("faqView.thirdAnswer.google.third") }}
|
||||
</li>
|
||||
<li>
|
||||
{{ $t("faqView.thirdAnswer.google.fourth") }}
|
||||
</li>
|
||||
</ol>
|
||||
</AccordionTab>
|
||||
<AccordionTab
|
||||
@ -230,10 +234,39 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<div class="col">{{ $t("faqView.ninthQuestion") }}</div>
|
||||
<div class="col">
|
||||
{{ $t("faqView.ninthAnswer") }}
|
||||
<a href="https://git.imn.htwk-leipzig.de/ekresse/htwkalender"
|
||||
<a href="https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender"
|
||||
>Gitlab</a
|
||||
>
|
||||
.
|
||||
>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid my-2">
|
||||
<div class="col">{{ $t("faqView.crossPromoQuestion") }}</div>
|
||||
<div class="col">
|
||||
{{ $t("faqView.crossPromo.teaser") }}
|
||||
<div class="flex flex-column gap-3 my-3">
|
||||
<Card v-for="promoPage in new Array('mensa', 'htwkarte')" :key="promoPage">
|
||||
<template #title>
|
||||
<div class="flex flex-row align-items-start">
|
||||
<Avatar :image="'/promo/' + promoPage + '.png'" class="mr-2 flex-shrink-0" size="xlarge" />
|
||||
<div class="flex flex-column gap-1">
|
||||
<div>
|
||||
{{$t("faqView.crossPromo." + promoPage + ".title") }}
|
||||
</div>
|
||||
<div class="p-card-subtitle text-base">
|
||||
<a :href="$t('faqView.crossPromo.' + promoPage + '.link')">
|
||||
{{$t("faqView.crossPromo." + promoPage + ".link") }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<p class="m-0">
|
||||
{{ $t("faqView.crossPromo." + promoPage + ".description") }}
|
||||
</p>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
@ -17,35 +17,50 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import moduleStore from "../store/moduleStore";
|
||||
import router from "../router";
|
||||
import AdditionalModuleTable from "../components/AdditionalModuleTable.vue";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import {router} from "@/main";
|
||||
import AdditionalModuleTable from "@/components/AdditionalModuleTable.vue";
|
||||
|
||||
const store = moduleStore();
|
||||
|
||||
function topFunction() {
|
||||
window.scrollTo({top: 0, behavior: 'smooth'});
|
||||
}
|
||||
|
||||
async function nextStep() {
|
||||
await router.push("/rename-modules");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-column align-items-center w-full mb-7">
|
||||
<div class="flex align-items-center justify-content-center m-2">
|
||||
<div class="flex flex-column align-items-center w-full mb-8">
|
||||
<div class="flex align-items-center justify-content-center">
|
||||
<h3>
|
||||
{{ $t("additionalModules.subTitle") }}
|
||||
</h3>
|
||||
</div>
|
||||
<AdditionalModuleTable />
|
||||
<div
|
||||
class="flex align-items-center justify-content-end h-4rem m-2 w-full lg:w-10"
|
||||
class="flex align-items-center justify-content-end h-4rem mb-2 w-full lg:w-10"
|
||||
>
|
||||
<Button
|
||||
:disabled="store.isEmpty()"
|
||||
class="col-12 md:col-4 mb-3 align-self-end"
|
||||
class="col-12 md:col-4 align-self-end"
|
||||
icon="pi pi-arrow-right"
|
||||
:label="$t('additionalModules.nextStep')"
|
||||
@click="nextStep()"
|
||||
/>
|
||||
</div>
|
||||
<AdditionalModuleTable />
|
||||
<div
|
||||
class="flex align-items-center justify-content-end mt-2 w-full lg:w-10"
|
||||
>
|
||||
<Button
|
||||
class="col-12 md:col-4 mb-3 align-self-end"
|
||||
severity="secondary"
|
||||
icon="pi pi-arrow-up"
|
||||
:label="$t('additionalModules.scrollToTop')"
|
||||
@click="topFunction()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -17,13 +17,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import moduleStore from "../store/moduleStore.ts";
|
||||
import { createIndividualFeed } from "../api/createFeed.ts";
|
||||
import router from "../router";
|
||||
import tokenStore from "../store/tokenStore.ts";
|
||||
import moduleStore from "@/store/moduleStore.ts";
|
||||
import { createIndividualFeed } from "@/api/createFeed.ts";
|
||||
import { router } from "@/main";
|
||||
import tokenStore from "@/store/tokenStore.ts";
|
||||
import { Ref, computed, inject, ref, onMounted } from "vue";
|
||||
import ModuleTemplateDialog from "./ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "../helpers/strings.ts";
|
||||
import ModuleTemplateDialog from "@/components/ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "@/helpers/strings.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Module } from "@/model/module.ts";
|
||||
import { useToast } from "primevue/usetoast";
|
@ -18,12 +18,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import moduleStore from "../../store/moduleStore";
|
||||
import router from "../../router";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { router } from "@/main";
|
||||
|
||||
const store = moduleStore();
|
||||
const AdditionalModuleTable = defineAsyncComponent(
|
||||
() => import("../../components/AdditionalModuleTable.vue"),
|
||||
() => import("@/components/AdditionalModuleTable.vue"),
|
||||
);
|
||||
|
||||
async function nextStep() {
|
@ -18,14 +18,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref } from "vue";
|
||||
import { Module } from "../model/module";
|
||||
import moduleStore from "../store/moduleStore";
|
||||
import { getCalender } from "../api/loadCalendar";
|
||||
import router from "../router";
|
||||
import tokenStore from "../store/tokenStore";
|
||||
import { Module } from "@/model/module";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { getCalender } from "@/api/loadCalendar";
|
||||
import { router } from "@/main";
|
||||
import tokenStore from "@/store/tokenStore";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import DynamicPage from "./DynamicPage.vue";
|
||||
import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import { extractToken, isToken } from "@/helpers/token.ts";
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
@ -19,12 +19,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, Ref, ref } from "vue";
|
||||
import { Module } from "@/model/module.ts";
|
||||
import moduleStore from "../../store/moduleStore";
|
||||
import moduleStore from "@/store/moduleStore";
|
||||
import { fetchAllModules } from "@/api/fetchCourse.ts";
|
||||
import { deleteIndividualFeed, saveIndividualFeed } from "@/api/createFeed.ts";
|
||||
import tokenStore from "../../store/tokenStore";
|
||||
import router from "@/router";
|
||||
import ModuleTemplateDialog from "../../components/ModuleTemplateDialog.vue";
|
||||
import tokenStore from "@/store/tokenStore";
|
||||
import { router } from "@/main";
|
||||
import ModuleTemplateDialog from "@/components/ModuleTemplateDialog.vue";
|
||||
import { onlyWhitespace } from "@/helpers/strings.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useToast } from "primevue/usetoast";
|
@ -155,7 +155,7 @@ import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import { requestFreeRooms } from "@/api/requestFreeRooms.ts";
|
||||
import { FilterMatchMode } from "primevue/api";
|
||||
import { padStart } from "@fullcalendar/core/internal";
|
||||
import router from "@/router";
|
||||
import { router } from "@/main";
|
||||
import { formatYearMonthDay } from "@/helpers/dates";
|
||||
|
||||
const mobilePage = inject("mobilePage") as Ref<boolean>;
|
@ -18,11 +18,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Ref, computed, ref, watch } from "vue";
|
||||
import { fetchRoom } from "../api/fetchRoom.ts";
|
||||
import DynamicPage from "./DynamicPage.vue";
|
||||
import RoomOccupation from "../components/RoomOccupation.vue";
|
||||
import { fetchRoom } from "@/api/fetchRoom.ts";
|
||||
import DynamicPage from "@/view/DynamicPage.vue";
|
||||
import RoomOccupation from "@/components/RoomOccupation.vue";
|
||||
import { computedAsync } from "@vueuse/core";
|
||||
import router from "@/router";
|
||||
import { router } from "@/main";
|
||||
|
||||
type Room = {
|
||||
name: string;
|
@ -20,7 +20,8 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": ["./src/*"],
|
||||
"primevue/*": ["./node_modules/primevue/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
|
@ -19,10 +19,21 @@ import vue from "@vitejs/plugin-vue";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
import basicSsl from "@vitejs/plugin-basic-ssl";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import {resolve as pathResolver} from "path";
|
||||
import terser from "@rollup/plugin-terser";
|
||||
import ViteSSGOptions from "vite-ssg";
|
||||
import generateSitemap from 'vite-ssg-sitemap'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
const hostname = "https://cal.htwk-leipzig.de";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
resolve(),
|
||||
terser(),
|
||||
vueDevTools(),
|
||||
basicSsl(),
|
||||
VitePWA({
|
||||
mode: "development",
|
||||
@ -120,8 +131,27 @@ export default defineConfig({
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
alias:
|
||||
{
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
'primevue' : pathResolver(__dirname, 'node_modules/primevue'),
|
||||
},
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.css', '.scss'],
|
||||
},
|
||||
ssgOptions: {
|
||||
script: "async",
|
||||
formatting: "minify",
|
||||
format: "esm",
|
||||
onFinished: () => {
|
||||
generateSitemap({
|
||||
hostname: hostname,
|
||||
exclude: [
|
||||
'/additional-modules',
|
||||
'/edit-additional-modules',
|
||||
'/edit-calendar',
|
||||
'/rename-modules',
|
||||
]
|
||||
})
|
||||
},
|
||||
},
|
||||
server: {
|
||||
@ -150,4 +180,24 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
sourcemap: true,
|
||||
minify: "terser",
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: true,
|
||||
},
|
||||
},
|
||||
cssMinify: "esbuild",
|
||||
cssCodeSplit: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (id.includes("node_modules")) {
|
||||
return id.toString().split("node_modules/")[1].split("/")[0].toString()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -26,6 +26,8 @@ events {
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
# Cloudflare IP Ranges (https://www.cloudflare.com/ips/)
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
@ -91,6 +93,13 @@ http {
|
||||
proxy_temp_file_write_size 64k;
|
||||
proxy_max_temp_file_size 1024m;
|
||||
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Server $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
geo $admin {
|
||||
default 1;
|
||||
10.0.0.0/8 0; # Private Network
|
||||
@ -105,16 +114,58 @@ http {
|
||||
1 $binary_remote_addr;
|
||||
}
|
||||
|
||||
# Different rate limits for different request methods
|
||||
map $request_method $ratelimit_key {
|
||||
POST $binary_remote_addr;
|
||||
default "";
|
||||
}
|
||||
|
||||
limit_req_zone $ratelimit_key zone=createFeed:10m rate=1r/m;
|
||||
# Limit the number of requests per IP
|
||||
limit_req_zone $limit_key zone=feed:20m rate=20r/m;
|
||||
limit_req_zone $limit_key zone=createFeed:10m rate=1r/m;
|
||||
limit_req_zone $limit_key zone=modules:10m rate=30r/m;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
http2 on;
|
||||
server_name pwa.htwk-leipzig.de;
|
||||
|
||||
location /api/feed {
|
||||
limit_req zone=createFeed nodelay;
|
||||
limit_req zone=feed burst=10 nodelay;
|
||||
proxy_pass http://htwkalender-ical:8091;
|
||||
client_max_body_size 2m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
limit_req_status 429;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
http2 on;
|
||||
server_name pwa.htwkalender.de;
|
||||
|
||||
location /api/feed {
|
||||
limit_req zone=createFeed nodelay;
|
||||
limit_req zone=feed burst=10 nodelay;
|
||||
proxy_pass http://htwkalender-ical:8091;
|
||||
client_max_body_size 2m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
limit_req_status 429;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
@ -123,23 +174,37 @@ http {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name pwa.htwkalender.de;
|
||||
|
||||
ssl_certificate htwkalender.de.pem;
|
||||
ssl_certificate_key htwkalender.de.key.pem;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
location /_ {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
# if user is not 0 in admin list, return 404
|
||||
if ($admin) {
|
||||
return 404 "Not Found";
|
||||
}
|
||||
# Increase upload file size
|
||||
client_max_body_size 100m;
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name pwa.htwk-leipzig.de;
|
||||
|
||||
ssl_certificate cal.htwk-leipzig.de.pem;
|
||||
ssl_certificate_key cal.htwk-leipzig.de.key.pem;
|
||||
|
||||
location /api/feed {
|
||||
limit_req zone=createFeed nodelay;
|
||||
limit_req zone=feed burst=10 nodelay;
|
||||
proxy_pass http://htwkalender-ical:8091;
|
||||
client_max_body_size 2m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
limit_req_status 429;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -149,7 +214,7 @@ http {
|
||||
|
||||
# Cache only specific URI
|
||||
location /api/modules {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -166,8 +231,26 @@ http {
|
||||
limit_req zone=modules burst=5 nodelay;
|
||||
}
|
||||
|
||||
location /api/events/types {
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
proxy_cache_bypass 0;
|
||||
proxy_no_cache 0;
|
||||
proxy_cache mcache; # mcache=RAM
|
||||
proxy_cache_valid 200 301 302 10m;
|
||||
proxy_cache_valid 403 404 5m;
|
||||
proxy_cache_lock on;
|
||||
proxy_cache_use_stale timeout updating;
|
||||
add_header X-Proxy-Cache $upstream_cache_status;
|
||||
limit_req zone=modules burst=10 nodelay;
|
||||
}
|
||||
|
||||
location /api/rooms {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -185,7 +268,7 @@ http {
|
||||
}
|
||||
|
||||
location /api/schedule {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -203,7 +286,7 @@ http {
|
||||
}
|
||||
|
||||
location /api/courses {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -220,26 +303,14 @@ http {
|
||||
limit_req zone=modules burst=5 nodelay;
|
||||
}
|
||||
|
||||
location /api/feed {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
client_max_body_size 2m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
limit_req zone=feed burst=10 nodelay;
|
||||
}
|
||||
|
||||
location /api/createFeed {
|
||||
limit_req zone=createFeed nodelay;
|
||||
# return limit request error
|
||||
limit_req_status 429;
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
client_max_body_size 2m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
location /_ {
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
# if user is not 0 in admin list, return 404
|
||||
if ($admin) {
|
||||
return 404 "Not Found";
|
||||
}
|
||||
# Increase upload file size
|
||||
client_max_body_size 100m;
|
||||
}
|
||||
|
||||
location / {
|
||||
|
@ -27,6 +27,9 @@ http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
# Cloudflare IP Ranges (https://www.cloudflare.com/ips/)
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@ -91,6 +94,13 @@ http {
|
||||
proxy_temp_file_write_size 64k;
|
||||
proxy_max_temp_file_size 1024m;
|
||||
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Server $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
geo $admin {
|
||||
default 1;
|
||||
10.0.0.0/8 0; # Private Network
|
||||
@ -105,14 +115,22 @@ http {
|
||||
1 $binary_remote_addr;
|
||||
}
|
||||
|
||||
# Different rate limits for different request methods
|
||||
map $request_method $ratelimit_key {
|
||||
POST $binary_remote_addr;
|
||||
default "";
|
||||
}
|
||||
|
||||
limit_req_zone $ratelimit_key zone=createFeed:10m rate=1r/m;
|
||||
|
||||
# Limit the number of requests per IP
|
||||
limit_req_zone $limit_key zone=feed:20m rate=20r/m;
|
||||
limit_req_zone $limit_key zone=createFeed:10m rate=1r/m;
|
||||
limit_req_zone $limit_key zone=modules:10m rate=30r/m;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
http2 on;
|
||||
server_name dev.htwkalender.de;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
@ -120,22 +138,25 @@ http {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name dev.htwkalender.de;
|
||||
ssl_certificate dev_htwkalender_de.pem;
|
||||
ssl_certificate_key dev_htwkalender_de.key.pem;
|
||||
|
||||
location /api/feed {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
limit_req zone=createFeed nodelay;
|
||||
limit_req zone=feed burst=10 nodelay;
|
||||
proxy_pass http://htwkalender-ical:8091;
|
||||
client_max_body_size 2m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
limit_req zone=feed burst=10 nodelay;
|
||||
limit_req_status 429;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -145,7 +166,7 @@ http {
|
||||
|
||||
# Cache only specific URI
|
||||
location /api/modules {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -163,7 +184,7 @@ http {
|
||||
}
|
||||
|
||||
location /api/rooms {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -181,7 +202,7 @@ http {
|
||||
}
|
||||
|
||||
location /api/schedule {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -199,7 +220,7 @@ http {
|
||||
}
|
||||
|
||||
location /api/courses {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -216,20 +237,8 @@ http {
|
||||
limit_req zone=modules burst=5 nodelay;
|
||||
}
|
||||
|
||||
location /api/createFeed {
|
||||
limit_req zone=createFeed nodelay;
|
||||
# return limit request error
|
||||
limit_req_status 429;
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
client_max_body_size 2m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
}
|
||||
|
||||
location /_ {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
# if user is not 0 in admin list, return 404
|
||||
if ($admin) {
|
||||
return 404 "Not Found";
|
||||
|
@ -1,22 +1,6 @@
|
||||
#Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
#Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
#You should have received a copy of the GNU Affero General Public License
|
||||
#along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
worker_processes 4;
|
||||
|
||||
error_log /opt/bitnami/nginx/logs/error.log;
|
||||
error_log /opt/bitnami/nginx/logs/error.log debug;
|
||||
pid /opt/bitnami/nginx/tmp/nginx.pid;
|
||||
|
||||
events {
|
||||
@ -27,43 +11,31 @@ http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
access_log /opt/bitnami/nginx/logs/proxy_access.log;
|
||||
error_log /opt/bitnami/nginx/logs/proxy_error.log;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 180s;
|
||||
send_timeout 180s;
|
||||
|
||||
client_body_temp_path /opt/bitnami/nginx/tmp/client_temp;
|
||||
proxy_temp_path /opt/bitnami/nginx/tmp/proxy_temp_path;
|
||||
fastcgi_temp_path /opt/bitnami/nginx/tmp/fastcgi_temp;
|
||||
uwsgi_temp_path /opt/bitnami/nginx/tmp/uwsgi_temp;
|
||||
scgi_temp_path /opt/bitnami/nginx/tmp/scgi_temp;
|
||||
|
||||
proxy_buffering on;
|
||||
proxy_buffers 8 16k;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_busy_buffers_size 64k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
proxy_max_temp_file_size 1024m;
|
||||
map $request_method $ratelimit_key {
|
||||
POST $binary_remote_addr;
|
||||
default "";
|
||||
}
|
||||
|
||||
limit_req_zone $ratelimit_key zone=createFeed:10m rate=1r/m;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name dev.htwkalender.de;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
http2 on;
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
|
||||
ssl_certificate /opt/bitnami/nginx/conf/dev_htwkalender_de.pem;
|
||||
ssl_certificate_key /opt/bitnami/nginx/conf/dev_htwkalender_de.key.pem;
|
||||
location /api/feed {
|
||||
limit_req zone=createFeed nodelay;
|
||||
proxy_pass http://htwkalender-ical:8091;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
limit_req_status 429;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 20m;
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
@ -72,13 +44,16 @@ http {
|
||||
}
|
||||
|
||||
location /_ {
|
||||
proxy_pass http://htwkalender-backend:8090;
|
||||
# Increase upload file size
|
||||
proxy_pass http://htwkalender-data-manager:8090;
|
||||
client_max_body_size 100m;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass https://htwkalender-frontend:8000;
|
||||
}
|
||||
|
||||
location /__devtools__ {
|
||||
proxy_pass https://htwkalender-frontend:8000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
services/Makefile
Normal file
16
services/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
run-data-manager:
|
||||
@go run data-manager/main.go serve
|
||||
|
||||
run-ical:
|
||||
@go run ical/main.go
|
||||
|
||||
build-ical:
|
||||
@go build ical/main.go
|
||||
|
||||
gen:
|
||||
@protoc \
|
||||
--proto_path=protobuf "protobuf/*" \
|
||||
--go_out="common/genproto/modules" \
|
||||
--go_opt=paths=source_relative \
|
||||
--go-grpc_out="common/genproto/modules" \
|
||||
--go-grpc_opt=paths=source_relative
|
6
services/README.md
Normal file
6
services/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
To execute the protobuf gen script, install the protobuf compiler and the python protobuf library. Then run the following command:
|
||||
|
||||
```bash
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
|
||||
```
|
322
services/common/genproto/modules/feeds.pb.go
Normal file
322
services/common/genproto/modules/feeds.pb.go
Normal file
@ -0,0 +1,322 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v5.27.1
|
||||
// source: feeds.proto
|
||||
|
||||
package modules
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Feed struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Modules string `protobuf:"bytes,2,opt,name=modules,proto3" json:"modules,omitempty"`
|
||||
Retrieved string `protobuf:"bytes,3,opt,name=retrieved,proto3" json:"retrieved,omitempty"`
|
||||
Created string `protobuf:"bytes,4,opt,name=created,proto3" json:"created,omitempty"`
|
||||
Updated string `protobuf:"bytes,5,opt,name=updated,proto3" json:"updated,omitempty"`
|
||||
Deleted bool `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Feed) Reset() {
|
||||
*x = Feed{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_feeds_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Feed) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Feed) ProtoMessage() {}
|
||||
|
||||
func (x *Feed) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_feeds_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Feed.ProtoReflect.Descriptor instead.
|
||||
func (*Feed) Descriptor() ([]byte, []int) {
|
||||
return file_feeds_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Feed) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Feed) GetModules() string {
|
||||
if x != nil {
|
||||
return x.Modules
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Feed) GetRetrieved() string {
|
||||
if x != nil {
|
||||
return x.Retrieved
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Feed) GetCreated() string {
|
||||
if x != nil {
|
||||
return x.Created
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Feed) GetUpdated() string {
|
||||
if x != nil {
|
||||
return x.Updated
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Feed) GetDeleted() bool {
|
||||
if x != nil {
|
||||
return x.Deleted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type GetFeedRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetFeedRequest) Reset() {
|
||||
*x = GetFeedRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_feeds_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetFeedRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetFeedRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetFeedRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_feeds_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetFeedRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetFeedRequest) Descriptor() ([]byte, []int) {
|
||||
return file_feeds_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *GetFeedRequest) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetFeedResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Feed *Feed `protobuf:"bytes,1,opt,name=feed,proto3" json:"feed,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetFeedResponse) Reset() {
|
||||
*x = GetFeedResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_feeds_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetFeedResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetFeedResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetFeedResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_feeds_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetFeedResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetFeedResponse) Descriptor() ([]byte, []int) {
|
||||
return file_feeds_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GetFeedResponse) GetFeed() *Feed {
|
||||
if x != nil {
|
||||
return x.Feed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_feeds_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_feeds_proto_rawDesc = []byte{
|
||||
0x0a, 0x0b, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x01,
|
||||
0x0a, 0x04, 0x46, 0x65, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
|
||||
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
|
||||
0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x64, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x64, 0x12, 0x18,
|
||||
0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61,
|
||||
0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x20, 0x0a, 0x0e,
|
||||
0x47, 0x65, 0x74, 0x46, 0x65, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e,
|
||||
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2c,
|
||||
0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x65, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x19, 0x0a, 0x04, 0x66, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x05, 0x2e, 0x46, 0x65, 0x65, 0x64, 0x52, 0x04, 0x66, 0x65, 0x65, 0x64, 0x32, 0x3d, 0x0a, 0x0b,
|
||||
0x46, 0x65, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x47,
|
||||
0x65, 0x74, 0x46, 0x65, 0x65, 0x64, 0x12, 0x0f, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, 0x65, 0x64,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, 0x65,
|
||||
0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1c, 0x5a, 0x1a, 0x68,
|
||||
0x74, 0x77, 0x6b, 0x61, 0x6c, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_feeds_proto_rawDescOnce sync.Once
|
||||
file_feeds_proto_rawDescData = file_feeds_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_feeds_proto_rawDescGZIP() []byte {
|
||||
file_feeds_proto_rawDescOnce.Do(func() {
|
||||
file_feeds_proto_rawDescData = protoimpl.X.CompressGZIP(file_feeds_proto_rawDescData)
|
||||
})
|
||||
return file_feeds_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_feeds_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_feeds_proto_goTypes = []interface{}{
|
||||
(*Feed)(nil), // 0: Feed
|
||||
(*GetFeedRequest)(nil), // 1: GetFeedRequest
|
||||
(*GetFeedResponse)(nil), // 2: GetFeedResponse
|
||||
}
|
||||
var file_feeds_proto_depIdxs = []int32{
|
||||
0, // 0: GetFeedResponse.feed:type_name -> Feed
|
||||
1, // 1: FeedService.GetFeed:input_type -> GetFeedRequest
|
||||
2, // 2: FeedService.GetFeed:output_type -> GetFeedResponse
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_feeds_proto_init() }
|
||||
func file_feeds_proto_init() {
|
||||
if File_feeds_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_feeds_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Feed); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_feeds_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetFeedRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_feeds_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetFeedResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_feeds_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_feeds_proto_goTypes,
|
||||
DependencyIndexes: file_feeds_proto_depIdxs,
|
||||
MessageInfos: file_feeds_proto_msgTypes,
|
||||
}.Build()
|
||||
File_feeds_proto = out.File
|
||||
file_feeds_proto_rawDesc = nil
|
||||
file_feeds_proto_goTypes = nil
|
||||
file_feeds_proto_depIdxs = nil
|
||||
}
|
105
services/common/genproto/modules/feeds_grpc.pb.go
Normal file
105
services/common/genproto/modules/feeds_grpc.pb.go
Normal file
@ -0,0 +1,105 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v5.27.1
|
||||
// source: feeds.proto
|
||||
|
||||
package modules
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// FeedServiceClient is the client API for FeedService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type FeedServiceClient interface {
|
||||
GetFeed(ctx context.Context, in *GetFeedRequest, opts ...grpc.CallOption) (*GetFeedResponse, error)
|
||||
}
|
||||
|
||||
type feedServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewFeedServiceClient(cc grpc.ClientConnInterface) FeedServiceClient {
|
||||
return &feedServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *feedServiceClient) GetFeed(ctx context.Context, in *GetFeedRequest, opts ...grpc.CallOption) (*GetFeedResponse, error) {
|
||||
out := new(GetFeedResponse)
|
||||
err := c.cc.Invoke(ctx, "/FeedService/GetFeed", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// FeedServiceServer is the server API for FeedService service.
|
||||
// All implementations must embed UnimplementedFeedServiceServer
|
||||
// for forward compatibility
|
||||
type FeedServiceServer interface {
|
||||
GetFeed(context.Context, *GetFeedRequest) (*GetFeedResponse, error)
|
||||
mustEmbedUnimplementedFeedServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedFeedServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedFeedServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedFeedServiceServer) GetFeed(context.Context, *GetFeedRequest) (*GetFeedResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetFeed not implemented")
|
||||
}
|
||||
func (UnimplementedFeedServiceServer) mustEmbedUnimplementedFeedServiceServer() {}
|
||||
|
||||
// UnsafeFeedServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to FeedServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeFeedServiceServer interface {
|
||||
mustEmbedUnimplementedFeedServiceServer()
|
||||
}
|
||||
|
||||
func RegisterFeedServiceServer(s grpc.ServiceRegistrar, srv FeedServiceServer) {
|
||||
s.RegisterService(&FeedService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _FeedService_GetFeed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetFeedRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(FeedServiceServer).GetFeed(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/FeedService/GetFeed",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(FeedServiceServer).GetFeed(ctx, req.(*GetFeedRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// FeedService_ServiceDesc is the grpc.ServiceDesc for FeedService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var FeedService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "FeedService",
|
||||
HandlerType: (*FeedServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetFeed",
|
||||
Handler: _FeedService_GetFeed_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "feeds.proto",
|
||||
}
|
712
services/common/genproto/modules/modules.pb.go
Normal file
712
services/common/genproto/modules/modules.pb.go
Normal file
@ -0,0 +1,712 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v5.27.1
|
||||
// source: modules.proto
|
||||
|
||||
package modules
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"`
|
||||
Day string `protobuf:"bytes,2,opt,name=day,proto3" json:"day,omitempty"`
|
||||
Week string `protobuf:"bytes,3,opt,name=week,proto3" json:"week,omitempty"`
|
||||
Start string `protobuf:"bytes,4,opt,name=start,proto3" json:"start,omitempty"`
|
||||
End string `protobuf:"bytes,5,opt,name=end,proto3" json:"end,omitempty"`
|
||||
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
|
||||
EventType string `protobuf:"bytes,7,opt,name=eventType,proto3" json:"eventType,omitempty"`
|
||||
Compulsory string `protobuf:"bytes,8,opt,name=compulsory,proto3" json:"compulsory,omitempty"`
|
||||
Prof string `protobuf:"bytes,9,opt,name=prof,proto3" json:"prof,omitempty"`
|
||||
Rooms string `protobuf:"bytes,10,opt,name=rooms,proto3" json:"rooms,omitempty"`
|
||||
Notes string `protobuf:"bytes,11,opt,name=notes,proto3" json:"notes,omitempty"`
|
||||
BookedAt string `protobuf:"bytes,12,opt,name=bookedAt,proto3" json:"bookedAt,omitempty"`
|
||||
Course string `protobuf:"bytes,13,opt,name=course,proto3" json:"course,omitempty"`
|
||||
Semester string `protobuf:"bytes,14,opt,name=semester,proto3" json:"semester,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Event) Reset() {
|
||||
*x = Event{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_modules_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Event) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Event) ProtoMessage() {}
|
||||
|
||||
func (x *Event) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_modules_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Event.ProtoReflect.Descriptor instead.
|
||||
func (*Event) Descriptor() ([]byte, []int) {
|
||||
return file_modules_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Event) GetUuid() string {
|
||||
if x != nil {
|
||||
return x.Uuid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetDay() string {
|
||||
if x != nil {
|
||||
return x.Day
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetWeek() string {
|
||||
if x != nil {
|
||||
return x.Week
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetStart() string {
|
||||
if x != nil {
|
||||
return x.Start
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetEnd() string {
|
||||
if x != nil {
|
||||
return x.End
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetEventType() string {
|
||||
if x != nil {
|
||||
return x.EventType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetCompulsory() string {
|
||||
if x != nil {
|
||||
return x.Compulsory
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetProf() string {
|
||||
if x != nil {
|
||||
return x.Prof
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetRooms() string {
|
||||
if x != nil {
|
||||
return x.Rooms
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetNotes() string {
|
||||
if x != nil {
|
||||
return x.Notes
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetBookedAt() string {
|
||||
if x != nil {
|
||||
return x.BookedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetCourse() string {
|
||||
if x != nil {
|
||||
return x.Course
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Event) GetSemester() string {
|
||||
if x != nil {
|
||||
return x.Semester
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Prof string `protobuf:"bytes,3,opt,name=prof,proto3" json:"prof,omitempty"`
|
||||
Course string `protobuf:"bytes,4,opt,name=course,proto3" json:"course,omitempty"`
|
||||
Semester string `protobuf:"bytes,5,opt,name=semester,proto3" json:"semester,omitempty"`
|
||||
Events []*Event `protobuf:"bytes,6,rep,name=events,proto3" json:"events,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Module) Reset() {
|
||||
*x = Module{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_modules_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Module) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Module) ProtoMessage() {}
|
||||
|
||||
func (x *Module) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_modules_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Module.ProtoReflect.Descriptor instead.
|
||||
func (*Module) Descriptor() ([]byte, []int) {
|
||||
return file_modules_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Module) GetUuid() string {
|
||||
if x != nil {
|
||||
return x.Uuid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Module) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Module) GetProf() string {
|
||||
if x != nil {
|
||||
return x.Prof
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Module) GetCourse() string {
|
||||
if x != nil {
|
||||
return x.Course
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Module) GetSemester() string {
|
||||
if x != nil {
|
||||
return x.Semester
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Module) GetEvents() []*Event {
|
||||
if x != nil {
|
||||
return x.Events
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetModuleRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Uuid string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetModuleRequest) Reset() {
|
||||
*x = GetModuleRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_modules_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetModuleRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetModuleRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetModuleRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_modules_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetModuleRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetModuleRequest) Descriptor() ([]byte, []int) {
|
||||
return file_modules_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GetModuleRequest) GetUuid() string {
|
||||
if x != nil {
|
||||
return x.Uuid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetModulesRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Uuids []string `protobuf:"bytes,1,rep,name=uuids,proto3" json:"uuids,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetModulesRequest) Reset() {
|
||||
*x = GetModulesRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_modules_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetModulesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetModulesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetModulesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_modules_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetModulesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetModulesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_modules_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *GetModulesRequest) GetUuids() []string {
|
||||
if x != nil {
|
||||
return x.Uuids
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetModuleResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Module *Module `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetModuleResponse) Reset() {
|
||||
*x = GetModuleResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_modules_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetModuleResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetModuleResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetModuleResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_modules_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetModuleResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetModuleResponse) Descriptor() ([]byte, []int) {
|
||||
return file_modules_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *GetModuleResponse) GetModule() *Module {
|
||||
if x != nil {
|
||||
return x.Module
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetModulesResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Modules []*Module `protobuf:"bytes,1,rep,name=modules,proto3" json:"modules,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetModulesResponse) Reset() {
|
||||
*x = GetModulesResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_modules_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetModulesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetModulesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetModulesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_modules_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetModulesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetModulesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_modules_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *GetModulesResponse) GetModules() []*Module {
|
||||
if x != nil {
|
||||
return x.Modules
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetEventsResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Events []*Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetEventsResponse) Reset() {
|
||||
*x = GetEventsResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_modules_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetEventsResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetEventsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetEventsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_modules_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetEventsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetEventsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_modules_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *GetEventsResponse) GetEvents() []*Event {
|
||||
if x != nil {
|
||||
return x.Events
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_modules_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_modules_proto_rawDesc = []byte{
|
||||
0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
|
||||
0xcb, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x64, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x61, 0x79, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x77, 0x65, 0x65, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x77,
|
||||
0x65, 0x65, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a,
|
||||
0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x6c, 0x73, 0x6f, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x6c, 0x73, 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x70, 0x72, 0x6f, 0x66, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x72, 0x6f,
|
||||
0x66, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6f, 0x6d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x05, 0x72, 0x6f, 0x6f, 0x6d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73,
|
||||
0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x62, 0x6f, 0x6f, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x08, 0x62, 0x6f, 0x6f, 0x6b, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x75,
|
||||
0x72, 0x73, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x75, 0x72, 0x73,
|
||||
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x0e, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, 0x72, 0x22, 0x98, 0x01,
|
||||
0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x70, 0x72, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x70, 0x72, 0x6f, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x73, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x73, 0x65, 0x6d, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e,
|
||||
0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74,
|
||||
0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x26, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d,
|
||||
0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64,
|
||||
0x22, 0x29, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x75, 0x75, 0x69, 0x64, 0x73, 0x22, 0x34, 0x0a, 0x11, 0x47,
|
||||
0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x1f, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x07, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
|
||||
0x65, 0x22, 0x37, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
|
||||
0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
|
||||
0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x33, 0x0a, 0x11, 0x47, 0x65,
|
||||
0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x1e, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x06, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x32,
|
||||
0xbf, 0x01, 0x0a, 0x0d, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x12, 0x34, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x11,
|
||||
0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x12, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x6f,
|
||||
0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x12, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
|
||||
0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x47, 0x65, 0x74, 0x4d,
|
||||
0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
||||
0x12, 0x3f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x6f, 0x72,
|
||||
0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x12, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64,
|
||||
0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x00, 0x42, 0x1c, 0x5a, 0x1a, 0x68, 0x74, 0x77, 0x6b, 0x61, 0x6c, 0x65, 0x6e, 0x64, 0x65, 0x72,
|
||||
0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_modules_proto_rawDescOnce sync.Once
|
||||
file_modules_proto_rawDescData = file_modules_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_modules_proto_rawDescGZIP() []byte {
|
||||
file_modules_proto_rawDescOnce.Do(func() {
|
||||
file_modules_proto_rawDescData = protoimpl.X.CompressGZIP(file_modules_proto_rawDescData)
|
||||
})
|
||||
return file_modules_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_modules_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_modules_proto_goTypes = []interface{}{
|
||||
(*Event)(nil), // 0: Event
|
||||
(*Module)(nil), // 1: Module
|
||||
(*GetModuleRequest)(nil), // 2: GetModuleRequest
|
||||
(*GetModulesRequest)(nil), // 3: GetModulesRequest
|
||||
(*GetModuleResponse)(nil), // 4: GetModuleResponse
|
||||
(*GetModulesResponse)(nil), // 5: GetModulesResponse
|
||||
(*GetEventsResponse)(nil), // 6: GetEventsResponse
|
||||
}
|
||||
var file_modules_proto_depIdxs = []int32{
|
||||
0, // 0: Module.events:type_name -> Event
|
||||
1, // 1: GetModuleResponse.module:type_name -> Module
|
||||
1, // 2: GetModulesResponse.modules:type_name -> Module
|
||||
0, // 3: GetEventsResponse.events:type_name -> Event
|
||||
2, // 4: ModuleService.GetModule:input_type -> GetModuleRequest
|
||||
3, // 5: ModuleService.GetModules:input_type -> GetModulesRequest
|
||||
3, // 6: ModuleService.GetEventsForModules:input_type -> GetModulesRequest
|
||||
4, // 7: ModuleService.GetModule:output_type -> GetModuleResponse
|
||||
5, // 8: ModuleService.GetModules:output_type -> GetModulesResponse
|
||||
6, // 9: ModuleService.GetEventsForModules:output_type -> GetEventsResponse
|
||||
7, // [7:10] is the sub-list for method output_type
|
||||
4, // [4:7] is the sub-list for method input_type
|
||||
4, // [4:4] is the sub-list for extension type_name
|
||||
4, // [4:4] is the sub-list for extension extendee
|
||||
0, // [0:4] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_modules_proto_init() }
|
||||
func file_modules_proto_init() {
|
||||
if File_modules_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_modules_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Event); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_modules_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Module); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_modules_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetModuleRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_modules_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetModulesRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_modules_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetModuleResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_modules_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetModulesResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_modules_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetEventsResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_modules_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_modules_proto_goTypes,
|
||||
DependencyIndexes: file_modules_proto_depIdxs,
|
||||
MessageInfos: file_modules_proto_msgTypes,
|
||||
}.Build()
|
||||
File_modules_proto = out.File
|
||||
file_modules_proto_rawDesc = nil
|
||||
file_modules_proto_goTypes = nil
|
||||
file_modules_proto_depIdxs = nil
|
||||
}
|
177
services/common/genproto/modules/modules_grpc.pb.go
Normal file
177
services/common/genproto/modules/modules_grpc.pb.go
Normal file
@ -0,0 +1,177 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v5.27.1
|
||||
// source: modules.proto
|
||||
|
||||
package modules
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// ModuleServiceClient is the client API for ModuleService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type ModuleServiceClient interface {
|
||||
GetModule(ctx context.Context, in *GetModuleRequest, opts ...grpc.CallOption) (*GetModuleResponse, error)
|
||||
GetModules(ctx context.Context, in *GetModulesRequest, opts ...grpc.CallOption) (*GetModulesResponse, error)
|
||||
GetEventsForModules(ctx context.Context, in *GetModulesRequest, opts ...grpc.CallOption) (*GetEventsResponse, error)
|
||||
}
|
||||
|
||||
type moduleServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewModuleServiceClient(cc grpc.ClientConnInterface) ModuleServiceClient {
|
||||
return &moduleServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *moduleServiceClient) GetModule(ctx context.Context, in *GetModuleRequest, opts ...grpc.CallOption) (*GetModuleResponse, error) {
|
||||
out := new(GetModuleResponse)
|
||||
err := c.cc.Invoke(ctx, "/ModuleService/GetModule", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *moduleServiceClient) GetModules(ctx context.Context, in *GetModulesRequest, opts ...grpc.CallOption) (*GetModulesResponse, error) {
|
||||
out := new(GetModulesResponse)
|
||||
err := c.cc.Invoke(ctx, "/ModuleService/GetModules", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *moduleServiceClient) GetEventsForModules(ctx context.Context, in *GetModulesRequest, opts ...grpc.CallOption) (*GetEventsResponse, error) {
|
||||
out := new(GetEventsResponse)
|
||||
err := c.cc.Invoke(ctx, "/ModuleService/GetEventsForModules", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ModuleServiceServer is the server API for ModuleService service.
|
||||
// All implementations must embed UnimplementedModuleServiceServer
|
||||
// for forward compatibility
|
||||
type ModuleServiceServer interface {
|
||||
GetModule(context.Context, *GetModuleRequest) (*GetModuleResponse, error)
|
||||
GetModules(context.Context, *GetModulesRequest) (*GetModulesResponse, error)
|
||||
GetEventsForModules(context.Context, *GetModulesRequest) (*GetEventsResponse, error)
|
||||
mustEmbedUnimplementedModuleServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedModuleServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedModuleServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedModuleServiceServer) GetModule(context.Context, *GetModuleRequest) (*GetModuleResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetModule not implemented")
|
||||
}
|
||||
func (UnimplementedModuleServiceServer) GetModules(context.Context, *GetModulesRequest) (*GetModulesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetModules not implemented")
|
||||
}
|
||||
func (UnimplementedModuleServiceServer) GetEventsForModules(context.Context, *GetModulesRequest) (*GetEventsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetEventsForModules not implemented")
|
||||
}
|
||||
func (UnimplementedModuleServiceServer) mustEmbedUnimplementedModuleServiceServer() {}
|
||||
|
||||
// UnsafeModuleServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ModuleServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeModuleServiceServer interface {
|
||||
mustEmbedUnimplementedModuleServiceServer()
|
||||
}
|
||||
|
||||
func RegisterModuleServiceServer(s grpc.ServiceRegistrar, srv ModuleServiceServer) {
|
||||
s.RegisterService(&ModuleService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _ModuleService_GetModule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetModuleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ModuleServiceServer).GetModule(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ModuleService/GetModule",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ModuleServiceServer).GetModule(ctx, req.(*GetModuleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ModuleService_GetModules_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetModulesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ModuleServiceServer).GetModules(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ModuleService/GetModules",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ModuleServiceServer).GetModules(ctx, req.(*GetModulesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ModuleService_GetEventsForModules_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetModulesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ModuleServiceServer).GetEventsForModules(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ModuleService/GetEventsForModules",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ModuleServiceServer).GetEventsForModules(ctx, req.(*GetModulesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ModuleService_ServiceDesc is the grpc.ServiceDesc for ModuleService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var ModuleService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ModuleService",
|
||||
HandlerType: (*ModuleServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetModule",
|
||||
Handler: _ModuleService_GetModule_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetModules",
|
||||
Handler: _ModuleService_GetModules_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetEventsForModules",
|
||||
Handler: _ModuleService_GetEventsForModules_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "modules.proto",
|
||||
}
|
81
services/data-manager/Dockerfile
Normal file
81
services/data-manager/Dockerfile
Normal file
@ -0,0 +1,81 @@
|
||||
#Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
#Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
#You should have received a copy of the GNU Affero General Public License
|
||||
#along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# build stage
|
||||
FROM golang:alpine AS build
|
||||
|
||||
WORKDIR /htwkalender-data-manager
|
||||
|
||||
RUN apk add --no-cache --update go gcc g++
|
||||
# Copy the source from the current directory to the Working Directory inside the container
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY data-manager/. ./data-manager
|
||||
COPY common/. ./common
|
||||
RUN CGO_ENABLED=1 GOOS=linux go build -o /htwkalender-data-manager data-manager/main.go
|
||||
|
||||
# production stage
|
||||
FROM alpine:3.20.1 AS prod
|
||||
|
||||
WORKDIR /htwkalender-data-manager
|
||||
|
||||
ARG USER=ical
|
||||
RUN adduser -Ds /bin/sh "$USER" && \
|
||||
chown "$USER":"$USER" ./
|
||||
|
||||
USER $USER
|
||||
RUN mkdir -p data
|
||||
|
||||
# copies executable from build container
|
||||
COPY --chown=$USER:$USER --chmod=644 --from=build /htwkalender-data-manager ./
|
||||
RUN chmod +x main
|
||||
|
||||
# Expose port 8090 to the outside world
|
||||
EXPOSE 8090
|
||||
|
||||
ENTRYPOINT ["./main", "serve"]
|
||||
|
||||
|
||||
FROM golang:1.21.6 AS dev
|
||||
|
||||
# Set the Current Working Directory inside the container
|
||||
WORKDIR /htwkalender-data-manager
|
||||
|
||||
ARG USER=ical
|
||||
RUN adduser "$USER" && \
|
||||
chown "$USER":"$USER" ./ \
|
||||
&& mkdir -p /htwkalender-data-manager/data \
|
||||
&& chown "$USER":"$USER" /htwkalender-data-manager/data
|
||||
|
||||
# Copy go mod and sum files
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy the source from the current directory to the Working Directory inside the container
|
||||
COPY data-manager/. ./data-manager
|
||||
COPY common/. ./common
|
||||
|
||||
# Build the Go app
|
||||
RUN CGO_ENABLED=1 GOOS=linux go build -o /htwkalender-data-manager data-manager/main.go
|
||||
|
||||
# Expose port 8091 to the outside world
|
||||
EXPOSE 8091
|
||||
|
||||
USER $USER
|
||||
|
||||
# Entry point
|
||||
ENTRYPOINT ["./main", "serve"]
|
@ -19,28 +19,47 @@ package main
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/plugins/migratecmd"
|
||||
_ "htwkalender/migrations"
|
||||
"htwkalender/service"
|
||||
_ "htwkalender/data-manager/migrations"
|
||||
"htwkalender/data-manager/model/serviceModel"
|
||||
"htwkalender/data-manager/service"
|
||||
"htwkalender/data-manager/service/events"
|
||||
"htwkalender/data-manager/service/grpc"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
func setupApp() *pocketbase.PocketBase {
|
||||
app := pocketbase.New()
|
||||
courseService := events.NewPocketBaseCourseService(app)
|
||||
eventService := events.NewPocketBaseEventService(app)
|
||||
|
||||
services := serviceModel.Service{
|
||||
CourseService: courseService,
|
||||
EventService: eventService,
|
||||
App: app,
|
||||
}
|
||||
|
||||
// loosely check if it was executed using "go run"
|
||||
isGoRun := strings.HasPrefix(os.Args[0], os.TempDir())
|
||||
|
||||
//start grpc server
|
||||
go grpc.StartGRPCServer(app)
|
||||
|
||||
migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{
|
||||
// enable auto creation of migration files when making collection changes in the Admin UI
|
||||
// (the isGoRun check is to enable it only during development)
|
||||
Automigrate: isGoRun,
|
||||
})
|
||||
service.AddRoutes(app)
|
||||
service.AddSchedules(app)
|
||||
service.AddRoutes(services)
|
||||
service.AddSchedules(services)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := setupApp()
|
||||
if err := app.Start(); err != nil {
|
||||
slog.Error("Failed to start app: %v", "error", err)
|
||||
slog.Error("Failed to start app: ", "error", err)
|
||||
}
|
||||
}
|
42
services/data-manager/main_test.go
Normal file
42
services/data-manager/main_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetupApp(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "Test setupApp",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
app := setupApp()
|
||||
go func() {
|
||||
if err := app.Start(); err != nil {
|
||||
t.Errorf("Failed to start app: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,461 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
m.Register(func(db dbx.Builder) error {
|
||||
jsonData := `[
|
||||
{
|
||||
"id": "cfq9mqlmd97v8z5",
|
||||
"created": "2023-09-19 17:31:15.957Z",
|
||||
"updated": "2024-07-13 11:37:49.151Z",
|
||||
"name": "groups",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "85msl21p",
|
||||
"name": "university",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "2sii4dtp",
|
||||
"name": "shortcut",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "uiwgo28f",
|
||||
"name": "groupId",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "y0l1lrzs",
|
||||
"name": "course",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": 2,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "kr62mhbz",
|
||||
"name": "faculty",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "ya6znpez",
|
||||
"name": "facultyId",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "bdhcrksy",
|
||||
"name": "semester",
|
||||
"type": "text",
|
||||
"required": true,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": 2,
|
||||
"max": 2,
|
||||
"pattern": "ws|ss"
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
"CREATE UNIQUE INDEX ` + "`" + `idx_rcaN2Oq` + "`" + ` ON ` + "`" + `groups` + "`" + ` (\n ` + "`" + `course` + "`" + `,\n ` + "`" + `semester` + "`" + `\n)"
|
||||
],
|
||||
"listRule": null,
|
||||
"viewRule": null,
|
||||
"createRule": null,
|
||||
"updateRule": null,
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "d65h4wh7zk13gxp",
|
||||
"created": "2023-09-19 17:31:15.957Z",
|
||||
"updated": "2024-07-13 11:37:49.145Z",
|
||||
"name": "feeds",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "cowxjfmc",
|
||||
"name": "modules",
|
||||
"type": "json",
|
||||
"required": true,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"maxSize": 2000000
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "wmmney8x",
|
||||
"name": "retrieved",
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": "",
|
||||
"max": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"listRule": null,
|
||||
"viewRule": "",
|
||||
"createRule": null,
|
||||
"updateRule": "",
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "7her4515qsmrxe8",
|
||||
"created": "2023-09-19 17:31:15.958Z",
|
||||
"updated": "2024-07-13 11:37:49.145Z",
|
||||
"name": "events",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "m8ne8e3m",
|
||||
"name": "Day",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "xnsxqp7j",
|
||||
"name": "Week",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "aeuskrjo",
|
||||
"name": "Name",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "klrzqyw0",
|
||||
"name": "EventType",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "5zltexoy",
|
||||
"name": "Prof",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "gy3nvfmx",
|
||||
"name": "Rooms",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "hn7b8dfy",
|
||||
"name": "Notes",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "axskpwm8",
|
||||
"name": "BookedAt",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "vyyefxp7",
|
||||
"name": "course",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "vlbpm9fz",
|
||||
"name": "semester",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "0kahthzr",
|
||||
"name": "uuid",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "6hkjwgb4",
|
||||
"name": "start",
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": "",
|
||||
"max": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "szbefpjf",
|
||||
"name": "end",
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": "",
|
||||
"max": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "nlnnxu7x",
|
||||
"name": "Compulsory",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
"CREATE INDEX ` + "`" + `idx_4vOTAiC` + "`" + ` ON ` + "`" + `events` + "`" + ` (\n ` + "`" + `Name` + "`" + `,\n ` + "`" + `course` + "`" + `,\n ` + "`" + `start` + "`" + `,\n ` + "`" + `end` + "`" + `,\n ` + "`" + `semester` + "`" + `,\n ` + "`" + `EventType` + "`" + `,\n ` + "`" + `Compulsory` + "`" + `\n)"
|
||||
],
|
||||
"listRule": null,
|
||||
"viewRule": null,
|
||||
"createRule": null,
|
||||
"updateRule": null,
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "_pb_users_auth_",
|
||||
"created": "2024-07-13 11:37:48.913Z",
|
||||
"updated": "2024-07-13 11:37:49.145Z",
|
||||
"name": "users",
|
||||
"type": "auth",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "users_name",
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "users_avatar",
|
||||
"name": "avatar",
|
||||
"type": "file",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"mimeTypes": [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/svg+xml",
|
||||
"image/gif",
|
||||
"image/webp"
|
||||
],
|
||||
"thumbs": null,
|
||||
"maxSelect": 1,
|
||||
"maxSize": 5242880,
|
||||
"protected": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"listRule": "id = @request.auth.id",
|
||||
"viewRule": "id = @request.auth.id",
|
||||
"createRule": "",
|
||||
"updateRule": "id = @request.auth.id",
|
||||
"deleteRule": "id = @request.auth.id",
|
||||
"options": {
|
||||
"allowEmailAuth": true,
|
||||
"allowOAuth2Auth": true,
|
||||
"allowUsernameAuth": true,
|
||||
"exceptEmailDomains": null,
|
||||
"manageRule": null,
|
||||
"minPasswordLength": 8,
|
||||
"onlyEmailDomains": null,
|
||||
"onlyVerified": false,
|
||||
"requireEmail": false
|
||||
}
|
||||
}
|
||||
]`
|
||||
|
||||
collections := []*models.Collection{}
|
||||
if err := json.Unmarshal([]byte(jsonData), &collections); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return daos.New(db).ImportCollections(collections, true, nil)
|
||||
}, func(db dbx.Builder) error {
|
||||
return nil
|
||||
})
|
||||
}
|
51
services/data-manager/migrations/1720871405_updated_feeds.go
Normal file
51
services/data-manager/migrations/1720871405_updated_feeds.go
Normal file
@ -0,0 +1,51 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
)
|
||||
|
||||
func init() {
|
||||
m.Register(func(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
collection, err := dao.FindCollectionByNameOrId("d65h4wh7zk13gxp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add
|
||||
new_deleted := &schema.SchemaField{}
|
||||
if err := json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "5d7vjjgo",
|
||||
"name": "deleted",
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {}
|
||||
}`), new_deleted); err != nil {
|
||||
return err
|
||||
}
|
||||
collection.Schema.AddField(new_deleted)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
}, func(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
collection, err := dao.FindCollectionByNameOrId("d65h4wh7zk13gxp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove
|
||||
collection.Schema.RemoveField("5d7vjjgo")
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
})
|
||||
}
|
@ -57,6 +57,10 @@ type Event struct {
|
||||
models.BaseModel
|
||||
}
|
||||
|
||||
type EventType struct {
|
||||
EventType string `db:"EventType" json:"eventType"`
|
||||
}
|
||||
|
||||
func (e *Event) Equals(event Event) bool {
|
||||
return e.Day == event.Day &&
|
||||
e.Week == event.Week &&
|
||||
@ -79,7 +83,7 @@ func (e *Event) SetCourse(course string) Event {
|
||||
return *e
|
||||
}
|
||||
|
||||
// Creates an AnonymizedEventDTO from an Event hiding all sensitive data
|
||||
// AnonymizeEvent Creates an AnonymizedEventDTO from an Event hiding all sensitive data
|
||||
func (e *Event) AnonymizeEvent() AnonymizedEventDTO {
|
||||
return AnonymizedEventDTO{
|
||||
Day: e.Day,
|
@ -24,7 +24,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
func TestEvents_Contains(t *testing.T) {
|
||||
func TestEventsContains(t *testing.T) {
|
||||
specificTime, _ := types.ParseDateTime("2020-01-01 12:00:00.000Z")
|
||||
|
||||
type args struct {
|
||||
@ -37,25 +37,25 @@ func TestEvents_Contains(t *testing.T) {
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty events",
|
||||
name: "event contains empty events",
|
||||
m: Events{},
|
||||
args: args{event: Event{}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "one event",
|
||||
name: "event contains one event",
|
||||
m: Events{{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}},
|
||||
args: args{event: Event{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "two events",
|
||||
name: "event contains two events",
|
||||
m: Events{{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}, {Day: "test2", Week: "test2", Start: specificTime, End: specificTime, Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}},
|
||||
args: args{event: Event{Day: "test2", Week: "test2", Start: specificTime, End: specificTime, Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "two events with different values",
|
||||
name: "event contains two events with different values",
|
||||
m: Events{{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test", UUID: "439ßu56rf8u9ijn4f4-2345345"}, {Day: "test2", Week: "test2", Start: specificTime, End: specificTime, Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2", UUID: "432a39ßu545349ijn4f4-23dsa45"}},
|
||||
args: args{event: Event{Day: "test3", Week: "test3", Start: specificTime, End: specificTime, Name: "test3", Course: "test3", Prof: "test3", Rooms: "test3", EventType: "test3", UUID: "934mf43r34f-g68h7655tg3"}},
|
||||
want: false,
|
||||
@ -70,7 +70,7 @@ func TestEvents_Contains(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_Equals(t *testing.T) {
|
||||
func TestEventEquals(t *testing.T) {
|
||||
specificTime, _ := types.ParseDateTime("2020-01-01 12:00:00.000Z")
|
||||
|
||||
type fields struct {
|
||||
@ -99,25 +99,25 @@ func TestEvent_Equals(t *testing.T) {
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty events",
|
||||
name: "event equals empty events",
|
||||
fields: fields{},
|
||||
args: args{event: Event{}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "one empty one not",
|
||||
name: "event equals one empty one not",
|
||||
fields: fields{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"},
|
||||
args: args{event: Event{}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "one event",
|
||||
name: "event equals one event",
|
||||
fields: fields{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"},
|
||||
args: args{event: Event{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "two events",
|
||||
name: "event equals two events",
|
||||
fields: fields{Day: "test", Week: "test", Start: specificTime, End: specificTime, Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"},
|
||||
args: args{event: Event{Day: "test2", Week: "test2", Start: specificTime, End: specificTime, Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}},
|
||||
want: false,
|
||||
@ -148,7 +148,7 @@ func TestEvent_Equals(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvent_AnonymizeEvent(t *testing.T) {
|
||||
func TestEventAnonymizeEvent(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Day string
|
||||
@ -172,22 +172,22 @@ func TestEvent_AnonymizeEvent(t *testing.T) {
|
||||
want AnonymizedEventDTO
|
||||
}{
|
||||
{
|
||||
name: "empty event",
|
||||
name: "event anonymize empty event",
|
||||
fields: fields{},
|
||||
want: AnonymizedEventDTO{Day: "", Week: "", Start: types.DateTime{}, End: types.DateTime{}, Rooms: "", Free: false},
|
||||
},
|
||||
{
|
||||
name: "one event",
|
||||
name: "event anonymize one event",
|
||||
fields: fields{Name: "Event", Day: "test", Week: "test", Rooms: "test"},
|
||||
want: AnonymizedEventDTO{Day: "test", Week: "test", Start: types.DateTime{}, End: types.DateTime{}, Rooms: "test", Free: false},
|
||||
},
|
||||
{
|
||||
name: "one event with free",
|
||||
name: "event anonymize one event with free",
|
||||
fields: fields{Name: "Räume zur freien Verfügung", Day: "test", Week: "test", Rooms: "test", Course: "test"},
|
||||
want: AnonymizedEventDTO{Day: "test", Week: "test", Start: types.DateTime{}, End: types.DateTime{}, Rooms: "test", Free: true},
|
||||
},
|
||||
{
|
||||
name: "another free event",
|
||||
name: "event anonymize another free event",
|
||||
fields: fields{Name: "Zur freien Verfügung", Day: "Montag", Week: "5", Start: types.DateTime{}, End: types.DateTime{}, Rooms: "TR_A1.28-S", Course: "42INM-3"},
|
||||
want: AnonymizedEventDTO{Day: "Montag", Week: "5", Start: types.DateTime{}, End: types.DateTime{}, Rooms: "TR_A1.28-S", Free: true},
|
||||
},
|
||||
@ -217,3 +217,264 @@ func TestEvent_AnonymizeEvent(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventGetName(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Day string
|
||||
Week string
|
||||
Start types.DateTime
|
||||
End types.DateTime
|
||||
Name string
|
||||
EventType string
|
||||
Compulsory string
|
||||
Prof string
|
||||
Rooms string
|
||||
Notes string
|
||||
BookedAt string
|
||||
Course string
|
||||
Semester string
|
||||
BaseModel models.BaseModel
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "event get name - empty event",
|
||||
fields: fields{},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "event get name - one event",
|
||||
fields: fields{Name: "Event"},
|
||||
want: "Event",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e := &Event{
|
||||
UUID: tt.fields.UUID,
|
||||
Day: tt.fields.Day,
|
||||
Week: tt.fields.Week,
|
||||
Start: tt.fields.Start,
|
||||
End: tt.fields.End,
|
||||
Name: tt.fields.Name,
|
||||
EventType: tt.fields.EventType,
|
||||
Compulsory: tt.fields.Compulsory,
|
||||
Prof: tt.fields.Prof,
|
||||
Rooms: tt.fields.Rooms,
|
||||
Notes: tt.fields.Notes,
|
||||
BookedAt: tt.fields.BookedAt,
|
||||
Course: tt.fields.Course,
|
||||
Semester: tt.fields.Semester,
|
||||
BaseModel: tt.fields.BaseModel,
|
||||
}
|
||||
if got := e.GetName(); got != tt.want {
|
||||
t.Errorf("GetName() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventSetCourse(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Day string
|
||||
Week string
|
||||
Start types.DateTime
|
||||
End types.DateTime
|
||||
Name string
|
||||
EventType string
|
||||
Compulsory string
|
||||
Prof string
|
||||
Rooms string
|
||||
Notes string
|
||||
BookedAt string
|
||||
Course string
|
||||
Semester string
|
||||
BaseModel models.BaseModel
|
||||
}
|
||||
type args struct {
|
||||
course string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want Event
|
||||
}{
|
||||
{
|
||||
name: "set course",
|
||||
fields: fields{},
|
||||
args: args{course: "test"},
|
||||
want: Event{Course: "test"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e := &Event{
|
||||
UUID: tt.fields.UUID,
|
||||
Day: tt.fields.Day,
|
||||
Week: tt.fields.Week,
|
||||
Start: tt.fields.Start,
|
||||
End: tt.fields.End,
|
||||
Name: tt.fields.Name,
|
||||
EventType: tt.fields.EventType,
|
||||
Compulsory: tt.fields.Compulsory,
|
||||
Prof: tt.fields.Prof,
|
||||
Rooms: tt.fields.Rooms,
|
||||
Notes: tt.fields.Notes,
|
||||
BookedAt: tt.fields.BookedAt,
|
||||
Course: tt.fields.Course,
|
||||
Semester: tt.fields.Semester,
|
||||
BaseModel: tt.fields.BaseModel,
|
||||
}
|
||||
if got := e.SetCourse(tt.args.course); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("SetCourse() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventSetName(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Day string
|
||||
Week string
|
||||
Start types.DateTime
|
||||
End types.DateTime
|
||||
Name string
|
||||
EventType string
|
||||
Compulsory string
|
||||
Prof string
|
||||
Rooms string
|
||||
Notes string
|
||||
BookedAt string
|
||||
Course string
|
||||
Semester string
|
||||
BaseModel models.BaseModel
|
||||
}
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "set name",
|
||||
fields: fields{
|
||||
Name: "name",
|
||||
},
|
||||
args: args{
|
||||
name: "name",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e := &Event{
|
||||
UUID: tt.fields.UUID,
|
||||
Day: tt.fields.Day,
|
||||
Week: tt.fields.Week,
|
||||
Start: tt.fields.Start,
|
||||
End: tt.fields.End,
|
||||
Name: tt.fields.Name,
|
||||
EventType: tt.fields.EventType,
|
||||
Compulsory: tt.fields.Compulsory,
|
||||
Prof: tt.fields.Prof,
|
||||
Rooms: tt.fields.Rooms,
|
||||
Notes: tt.fields.Notes,
|
||||
BookedAt: tt.fields.BookedAt,
|
||||
Course: tt.fields.Course,
|
||||
Semester: tt.fields.Semester,
|
||||
BaseModel: tt.fields.BaseModel,
|
||||
}
|
||||
e.SetName(tt.args.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventTableName(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Day string
|
||||
Week string
|
||||
Start types.DateTime
|
||||
End types.DateTime
|
||||
Name string
|
||||
EventType string
|
||||
Compulsory string
|
||||
Prof string
|
||||
Rooms string
|
||||
Notes string
|
||||
BookedAt string
|
||||
Course string
|
||||
Semester string
|
||||
BaseModel models.BaseModel
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "table name",
|
||||
fields: fields{},
|
||||
want: "events",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e := &Event{
|
||||
UUID: tt.fields.UUID,
|
||||
Day: tt.fields.Day,
|
||||
Week: tt.fields.Week,
|
||||
Start: tt.fields.Start,
|
||||
End: tt.fields.End,
|
||||
Name: tt.fields.Name,
|
||||
EventType: tt.fields.EventType,
|
||||
Compulsory: tt.fields.Compulsory,
|
||||
Prof: tt.fields.Prof,
|
||||
Rooms: tt.fields.Rooms,
|
||||
Notes: tt.fields.Notes,
|
||||
BookedAt: tt.fields.BookedAt,
|
||||
Course: tt.fields.Course,
|
||||
Semester: tt.fields.Semester,
|
||||
BaseModel: tt.fields.BaseModel,
|
||||
}
|
||||
if got := e.TableName(); got != tt.want {
|
||||
t.Errorf("TableName() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventsContains1(t *testing.T) {
|
||||
type args struct {
|
||||
event Event
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
m Events
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "event contains - empty events",
|
||||
m: Events{},
|
||||
args: args{event: Event{}},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.m.Contains(tt.args.event); got != tt.want {
|
||||
t.Errorf("Contains() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -24,9 +24,14 @@ import (
|
||||
type Feed struct {
|
||||
Modules string `db:"modules" json:"modules"`
|
||||
Retrieved types.DateTime `db:"retrieved" json:"retrieved"`
|
||||
Deleted bool `db:"deleted" json:"deleted"`
|
||||
models.BaseModel
|
||||
}
|
||||
|
||||
func (f *Feed) TableName() string {
|
||||
return "feeds"
|
||||
}
|
||||
|
||||
// SetModules set modules field
|
||||
func (f *Feed) SetModules(modules string) {
|
||||
f.Modules = modules
|
45
services/data-manager/model/feedModel_test.go
Normal file
45
services/data-manager/model/feedModel_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFeedSetModules(t *testing.T) {
|
||||
type fields struct {
|
||||
Modules string
|
||||
Retrieved types.DateTime
|
||||
BaseModel models.BaseModel
|
||||
}
|
||||
type args struct {
|
||||
modules string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "set modules",
|
||||
fields: fields{
|
||||
Modules: "",
|
||||
Retrieved: types.DateTime{},
|
||||
BaseModel: models.BaseModel{},
|
||||
},
|
||||
args: args{
|
||||
modules: "modules",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f := &Feed{
|
||||
Modules: tt.fields.Modules,
|
||||
Retrieved: tt.fields.Retrieved,
|
||||
BaseModel: tt.fields.BaseModel,
|
||||
}
|
||||
f.SetModules(tt.args.modules)
|
||||
})
|
||||
}
|
||||
}
|
126
services/data-manager/model/moduleModel_test.go
Normal file
126
services/data-manager/model/moduleModel_test.go
Normal file
@ -0,0 +1,126 @@
|
||||
package model
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestModuleDTOGetName(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Name string
|
||||
Prof string
|
||||
Course string
|
||||
Semester string
|
||||
EventType string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "get name",
|
||||
fields: fields{
|
||||
Name: "name",
|
||||
},
|
||||
want: "name",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
m := &ModuleDTO{
|
||||
UUID: tt.fields.UUID,
|
||||
Name: tt.fields.Name,
|
||||
Prof: tt.fields.Prof,
|
||||
Course: tt.fields.Course,
|
||||
Semester: tt.fields.Semester,
|
||||
EventType: tt.fields.EventType,
|
||||
}
|
||||
if got := m.GetName(); got != tt.want {
|
||||
t.Errorf("GetName() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestModuleDTOSetName(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Name string
|
||||
Prof string
|
||||
Course string
|
||||
Semester string
|
||||
EventType string
|
||||
}
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "set name",
|
||||
fields: fields{
|
||||
Name: "name",
|
||||
},
|
||||
args: args{
|
||||
name: "name",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
m := &ModuleDTO{
|
||||
UUID: tt.fields.UUID,
|
||||
Name: tt.fields.Name,
|
||||
Prof: tt.fields.Prof,
|
||||
Course: tt.fields.Course,
|
||||
Semester: tt.fields.Semester,
|
||||
EventType: tt.fields.EventType,
|
||||
}
|
||||
m.SetName(tt.args.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestModuleSetName(t *testing.T) {
|
||||
type fields struct {
|
||||
UUID string
|
||||
Name string
|
||||
Prof string
|
||||
Course string
|
||||
Semester string
|
||||
Events Events
|
||||
}
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "set name",
|
||||
fields: fields{
|
||||
Name: "name",
|
||||
},
|
||||
args: args{
|
||||
name: "name",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
m := &Module{
|
||||
UUID: tt.fields.UUID,
|
||||
Name: tt.fields.Name,
|
||||
Prof: tt.fields.Prof,
|
||||
Course: tt.fields.Course,
|
||||
Semester: tt.fields.Semester,
|
||||
Events: tt.fields.Events,
|
||||
}
|
||||
m.SetName(tt.args.name)
|
||||
})
|
||||
}
|
||||
}
|
0
services/data-manager/model/roomOccupancyModel.go
Normal file
0
services/data-manager/model/roomOccupancyModel.go
Normal file
12
services/data-manager/model/serviceModel/serviceModel.go
Normal file
12
services/data-manager/model/serviceModel/serviceModel.go
Normal file
@ -0,0 +1,12 @@
|
||||
package serviceModel
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"htwkalender/data-manager/service/events"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
App *pocketbase.PocketBase
|
||||
EventService events.EventService
|
||||
CourseService events.CourseService
|
||||
}
|
78
services/data-manager/service/addCalDavRoutes.go
Normal file
78
services/data-manager/service/addCalDavRoutes.go
Normal file
@ -0,0 +1,78 @@
|
||||
//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format.
|
||||
//Copyright (C) 2024 HTWKalender support@htwkalender.de
|
||||
|
||||
//This program is free software: you can redistribute it and/or modify
|
||||
//it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
|
||||
//You should have received a copy of the GNU Affero General Public License
|
||||
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"htwkalender/data-manager/service/feed"
|
||||
"htwkalender/data-manager/service/ical"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func addFeedRoutes(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/feed",
|
||||
Handler: func(c echo.Context) error {
|
||||
requestBody, _ := io.ReadAll(c.Request().Body)
|
||||
result, err := ical.CreateIndividualFeed(requestBody, app)
|
||||
if err != nil {
|
||||
slog.Error("Failed to create individual feed", "error", err)
|
||||
return c.JSON(http.StatusInternalServerError, "Failed to create individual feed")
|
||||
}
|
||||
return c.JSON(http.StatusOK, result)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/api/feed",
|
||||
Handler: func(c echo.Context) error {
|
||||
token := c.QueryParam("token")
|
||||
err := feed.MarkFeedForDeletion(app.Dao(), token)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusNotFound, err)
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, "Feed deleted")
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
@ -17,18 +17,17 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"htwkalender/service/events"
|
||||
"htwkalender/service/fetch/sport"
|
||||
v1 "htwkalender/service/fetch/v1"
|
||||
v2 "htwkalender/service/fetch/v2"
|
||||
"htwkalender/service/functions/time"
|
||||
"htwkalender/service/ical"
|
||||
"htwkalender/service/room"
|
||||
"htwkalender/data-manager/model/serviceModel"
|
||||
"htwkalender/data-manager/service/course"
|
||||
"htwkalender/data-manager/service/fetch/sport"
|
||||
v1 "htwkalender/data-manager/service/fetch/v1"
|
||||
v2 "htwkalender/data-manager/service/fetch/v2"
|
||||
"htwkalender/data-manager/service/functions/time"
|
||||
"htwkalender/data-manager/service/room"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
@ -36,23 +35,23 @@ import (
|
||||
|
||||
const RoomOccupancyGranularity = 15
|
||||
|
||||
func AddRoutes(app *pocketbase.PocketBase) {
|
||||
func AddRoutes(services serviceModel.Service) {
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/fetch/events",
|
||||
Handler: func(c echo.Context) error {
|
||||
savedEvents, err := v2.ParseEventsFromRemote(app)
|
||||
savedEvents, err := v2.ParseEventsFromRemote(services.App)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse events from remote: %v", "error", err)
|
||||
slog.Error("Failed to parse events from remote: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to parse events from remote")
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, savedEvents)
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
@ -62,19 +61,38 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/fetch/daily/events",
|
||||
Handler: func(c echo.Context) error {
|
||||
course.UpdateCourse(services)
|
||||
return c.JSON(http.StatusOK, "Daily events fetched")
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(services.App),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/fetch/groups",
|
||||
Handler: func(c echo.Context) error {
|
||||
groups, err := v1.FetchSeminarGroups(app)
|
||||
groups, err := v1.FetchSeminarGroups(services.App)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, "Failed to fetch seminar groups")
|
||||
}
|
||||
return c.JSON(http.StatusOK, groups)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
@ -84,20 +102,20 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/fetch/sports",
|
||||
Handler: func(c echo.Context) error {
|
||||
|
||||
sportEvents, err := sport.FetchAndUpdateSportEvents(app)
|
||||
sportEvents, err := sport.FetchAndUpdateSportEvents(services.App)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, "Failed to fetch sport events")
|
||||
}
|
||||
return c.JSON(http.StatusOK, sportEvents)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
@ -107,19 +125,19 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/api/modules",
|
||||
Handler: func(c echo.Context) error {
|
||||
err := events.DeleteAllEvents(app)
|
||||
err := services.EventService.DeleteAllEvents()
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, "Failed to delete events")
|
||||
}
|
||||
return c.JSON(http.StatusOK, "Events deleted")
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
@ -129,19 +147,19 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/rooms",
|
||||
Handler: func(c echo.Context) error {
|
||||
rooms, err := room.GetRooms(app)
|
||||
rooms, err := room.GetRooms(services.App)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get rooms")
|
||||
}
|
||||
return c.JSON(http.StatusOK, rooms)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -151,22 +169,22 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
})
|
||||
|
||||
// API Endpoint to get all events for a specific room on a specific day
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/schedule/day",
|
||||
Handler: func(c echo.Context) error {
|
||||
roomParam := c.QueryParam("room")
|
||||
date := c.QueryParam("date")
|
||||
roomSchedule, err := room.GetRoomScheduleForDay(app, roomParam, date)
|
||||
roomSchedule, err := room.GetRoomScheduleForDay(services.App, roomParam, date)
|
||||
if err != nil {
|
||||
slog.Error("Failed to get room schedule for day: %v", "error", err)
|
||||
slog.Error("Failed to get room schedule for day: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get room schedule for day")
|
||||
}
|
||||
return c.JSON(http.StatusOK, roomSchedule)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -210,7 +228,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
})
|
||||
|
||||
// API Endpoint to create a new iCal feed
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/schedule",
|
||||
@ -218,15 +236,15 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
roomParam := c.QueryParam("room")
|
||||
to := c.QueryParam("to")
|
||||
from := c.QueryParam("from")
|
||||
roomSchedule, err := room.GetRoomSchedule(app, roomParam, from, to)
|
||||
roomSchedule, err := room.GetRoomSchedule(services.App, roomParam, from, to)
|
||||
if err != nil {
|
||||
slog.Error("Failed to get room schedule: %v", "error", err)
|
||||
slog.Error("Failed to get room schedule:", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get room schedule")
|
||||
}
|
||||
return c.JSON(http.StatusOK, roomSchedule)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -236,30 +254,30 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
})
|
||||
|
||||
// API Endpoint to get all rooms that have no events in a specific time frame
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/rooms/free",
|
||||
Handler: func(c echo.Context) error {
|
||||
from, err := time.ParseTime(c.QueryParam("from"))
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse time: %v", "error", err)
|
||||
slog.Error("Failed to parse time: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to parse time")
|
||||
}
|
||||
to, err := time.ParseTime(c.QueryParam("to"))
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse time: %v", "error", err)
|
||||
slog.Error("Failed to parse time: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to parse time")
|
||||
}
|
||||
rooms, err := room.GetFreeRooms(app, from, to)
|
||||
rooms, err := room.GetFreeRooms(services.App, from, to)
|
||||
if err != nil {
|
||||
slog.Error("Failed to get free rooms: %v", "error", err)
|
||||
slog.Error("Failed to get free rooms: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get free rooms")
|
||||
}
|
||||
return c.JSON(http.StatusOK, rooms)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -268,26 +286,27 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
addFeedRoutes(app)
|
||||
addFeedRoutes(services.App)
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/course/modules",
|
||||
Handler: func(c echo.Context) error {
|
||||
course := c.QueryParam("course")
|
||||
semester := c.QueryParam("semester")
|
||||
modules, err := events.GetModulesForCourseDistinct(app, course, semester)
|
||||
modules, err := services.EventService.GetModulesForCourseDistinct(
|
||||
c.QueryParam("course"),
|
||||
c.QueryParam("semester"),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
slog.Error("Failed to get modules for course and semester: %v", "error", err)
|
||||
slog.Error("Failed to get modules for course and semester: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get modules for course and semester")
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, modules)
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -296,20 +315,20 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/modules",
|
||||
Handler: func(c echo.Context) error {
|
||||
modules, err := events.GetAllModulesDistinct(app)
|
||||
modules, err := services.EventService.GetAllModulesDistinct()
|
||||
if err != nil {
|
||||
slog.Error("Failed to get modules: %v", "error", err)
|
||||
slog.Error("Failed to get modules: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get modules")
|
||||
}
|
||||
return c.JSON(http.StatusOK, modules)
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -318,22 +337,22 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/module",
|
||||
Handler: func(c echo.Context) error {
|
||||
requestModule := c.QueryParam("uuid")
|
||||
module, err := events.GetModuleByUUID(app, requestModule)
|
||||
module, err := services.EventService.GetModuleByUUID(requestModule)
|
||||
if err != nil {
|
||||
slog.Error("Failed to get module: %v", "error", err)
|
||||
slog.Error("Failed to get module: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get module")
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, module)
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -342,22 +361,26 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/courses",
|
||||
Handler: func(c echo.Context) error {
|
||||
semester := c.QueryParam("semester")
|
||||
if semester == "" {
|
||||
courses := events.GetAllCourses(app)
|
||||
courses := services.CourseService.GetAllCourses()
|
||||
return c.JSON(200, courses)
|
||||
} else {
|
||||
courses := events.GetAllCoursesForSemester(app, semester)
|
||||
return c.JSON(200, courses)
|
||||
seminarGroups := services.CourseService.GetAllCoursesForSemester(semester)
|
||||
courseStringList := make([]string, 0)
|
||||
for _, seminarGroup := range seminarGroups {
|
||||
courseStringList = append(courseStringList, seminarGroup.Course)
|
||||
}
|
||||
return c.JSON(200, courseStringList)
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -367,23 +390,22 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
})
|
||||
|
||||
// api end point to get all courses for a specific semester with courses that have events
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/courses/events",
|
||||
Handler: func(c echo.Context) error {
|
||||
semester := c.QueryParam("semester")
|
||||
courses, err := events.GetAllCoursesForSemesterWithEvents(app, semester)
|
||||
|
||||
courses, err := services.CourseService.GetAllCoursesForSemesterWithEvents(semester)
|
||||
if err != nil {
|
||||
slog.Error("Failed to get courses for semester with events: %v", "error", err)
|
||||
slog.Error("Failed to get courses for semester with events: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get courses for semester with events")
|
||||
} else {
|
||||
return c.JSON(200, courses)
|
||||
return c.JSON(http.StatusOK, courses)
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -392,48 +414,48 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
// API Endpoint to get all eventTypes from the database distinct
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/events/types",
|
||||
Handler: func(c echo.Context) error {
|
||||
eventTypes, err := services.EventService.GetEventTypes()
|
||||
if err != nil {
|
||||
slog.Error("Failed to get event types", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to get event types")
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, eventTypes)
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(services.App),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
services.App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/api/events",
|
||||
Handler: func(c echo.Context) error {
|
||||
course := c.QueryParam("course")
|
||||
semester := c.QueryParam("semester")
|
||||
err := events.DeleteAllEventsByCourseAndSemester(app, course, semester)
|
||||
err := services.EventService.DeleteAllEventsByCourseAndSemester(
|
||||
c.QueryParam("course"),
|
||||
c.QueryParam("semester"),
|
||||
)
|
||||
if err != nil {
|
||||
slog.Error("Failed to delete events: %v", "error", err)
|
||||
slog.Error("Failed to delete events: ", "error", err)
|
||||
return c.JSON(http.StatusBadRequest, "Failed to delete events")
|
||||
} else {
|
||||
return c.JSON(http.StatusBadRequest, "Events deleted")
|
||||
return c.JSON(http.StatusOK, "Events deleted")
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/feeds/migrate",
|
||||
Handler: func(c echo.Context) error {
|
||||
err := ical.MigrateFeedJson(app)
|
||||
|
||||
if err != nil {
|
||||
slog.Error("Failed to migrate feeds: %v", "error", err)
|
||||
return c.JSON(http.StatusInternalServerError, "Failed to migrate feeds")
|
||||
} else {
|
||||
return c.JSON(http.StatusOK, "Migrated")
|
||||
}
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.ActivityLogger(services.App),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user