feat:#7 added new folder structure and updated api in ical

This commit is contained in:
Elmar Kresse
2024-05-26 11:59:32 +02:00
parent 2f55076e36
commit cb76b5c188
113 changed files with 250 additions and 266 deletions

View File

@ -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>

View File

@ -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
})
}

View File

@ -14,13 +14,11 @@
#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/>.
version: "3.9"
services:
htwkalender-backend:
build:
dockerfile: Dockerfile
context: ./backend
context: ./services/backend
target: dev # prod
command: "--http=0.0.0.0:8090 --dir=/htwkalender/data/pb_data"
ports:
@ -29,6 +27,13 @@ services:
- pb_data:/htwkalender/data # for production with volume
# - ./backend:/htwkalender/data # for development with bind mount from project directory
htwkalender-ical:
build:
dockerfile: Dockerfile
context: ./services/ical
target: dev # prod
htwkalender-frontend:
build:
dockerfile: Dockerfile

View File

@ -18,7 +18,7 @@ import { Module } from "../model/module.ts";
export async function createIndividualFeed(modules: Module[]): Promise<string> {
try {
const response = await fetch("/api/createFeed", {
const response = await fetch("/api/feed", {
method: "POST",
headers: {
"Content-Type": "application/json",

View File

@ -52,6 +52,15 @@ http {
listen 80;
listen [::]:80;
location /api/feed {
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;
}
location /api {
proxy_pass http://htwkalender-backend:8090;
client_max_body_size 20m;

View File

@ -0,0 +1,76 @@
//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/feed",
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.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
})
}

View File

@ -17,60 +17,13 @@
package ical
import (
"bytes"
"encoding/json"
"htwkalender/model"
"htwkalender/service/db"
"htwkalender/service/feed"
"time"
"github.com/jordic/goics"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"htwkalender/model"
"htwkalender/service/db"
)
const expirationTime = 5 * time.Minute
func Feed(app *pocketbase.PocketBase, token string) (string, error) {
var result string
icalFeed, err := db.FindFeedByToken(token, app)
if icalFeed == nil && err != nil {
return "", err
}
modules := make(map[string]model.FeedCollection)
var modulesArray []model.FeedCollection
_ = json.Unmarshal([]byte(icalFeed.Modules), &modulesArray)
for _, module := range modulesArray {
modules[module.UUID] = module
}
newFeed, createFeedErr := createFeedForToken(app, modules)
if createFeedErr != nil {
return "", createFeedErr
}
result = newFeed.Content
return result, nil
}
func createFeedForToken(app *pocketbase.PocketBase, modules map[string]model.FeedCollection) (*model.FeedModel, error) {
events, err := db.GetPlanForModules(app, modules)
if err != nil {
return nil, apis.NewNotFoundError("Could not fetch events", err)
}
// Combine Events
events = feed.CombineEventsInFeed(events)
b := bytes.Buffer{}
goics.NewICalEncode(&b).Encode(IcalModel{Events: events, Mapping: modules})
icalFeed := &model.FeedModel{Content: b.String(), ExpiresAt: time.Now().Add(expirationTime)}
return icalFeed, nil
}
func CreateIndividualFeed(requestBody []byte, app *pocketbase.PocketBase) (string, error) {
var modules []model.FeedCollection

70
services/ical/Dockerfile Normal file
View File

@ -0,0 +1,70 @@
#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 /app
# Copy the source from the current directory to the Working Directory inside the container
COPY . ./
# download needed modules
RUN apk add --no-cache --update go gcc g++ && \
go mod download && \
CGO_ENABLED=1 GOOS=linux go build -o /htwkalender-ical
# production stage
FROM alpine:latest AS prod
WORKDIR /htwkalender-ical
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 --from=build /htwkalender-ical ./
# Expose port 8090 to the outside world
EXPOSE 8091
ENTRYPOINT ["./htwkalender-ical"]
FROM golang:1.21.6 AS dev
# Set the Current Working Directory inside the container
WORKDIR /htwkalender-ical
# 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 *.go ./
COPY . .
# Build the Go app
RUN CGO_ENABLED=1 GOOS=linux go build -o /htwkalender-ical
# Expose port 8090 to the outside world
EXPOSE 8090
# Entry point
ENTRYPOINT ["./htwkalender-ical"]

View File

@ -22,16 +22,24 @@ import (
"htwkalender-ical/service"
)
// main function for the backend-ical service
// main function for the ical service
// uses rest api to get the data from the backend
// exposes rest api endpoints with fiber to serve the data for clients
func main() {
// Initialize a new Fiber app
app := fiber.New()
webdavRequestMethods := []string{"PROPFIND", "MKCOL", "COPY", "MOVE"}
app := fiber.New(fiber.Config{
CaseSensitive: true,
StrictRouting: true,
ServerHeader: "Fiber",
AppName: "App Name",
RequestMethods: append(fiber.DefaultMethods[:], webdavRequestMethods...),
})
// Add routes to the app instance for the backend ical service
service.AddFeedRoutes(app)
log.Fatal(app.Listen(":3000"))
log.Fatal(app.Listen(":8091"))
}

View File

@ -38,6 +38,15 @@ func parseResponse(response []byte) (model.FeedRecord, error) {
return feedRecord, nil
}
func DeleteFeedRecord(token string) error {
err := DeleteRequestApi("/api/feed?token=" + token)
if err != nil {
return err
}
return nil
}
func GetModuleWithEvents(module model.FeedModule) (model.Module, error) {
var modules model.Module

View File

@ -7,7 +7,7 @@ import (
func RequestApi(path string) (*client.Response, error) {
var host = "http://localhost"
var host = "http://htwkalender-backend:8090"
cc := client.New()
cc.SetTimeout(5 * time.Second)
@ -20,3 +20,19 @@ func RequestApi(path string) (*client.Response, error) {
return response, nil
}
func DeleteRequestApi(path string) error {
var host = "http://htwkalender-backend:8090"
cc := client.New()
cc.SetTimeout(5 * time.Second)
// set retry to 0
_, err := cc.Delete(host + path)
if err != nil {
return err
}
return nil
}

Some files were not shown because too many files have changed in this diff Show More