mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-07-16 17:48:49 +02:00
Merge branch 'refs/heads/development' into 52-limit-rproxy-logs
# Conflicts: # frontend/nginx.conf # reverseproxy.conf
This commit is contained in:
@ -33,6 +33,8 @@ services:
|
|||||||
- DATA_MANAGER_URL=htwkalender-data-manager
|
- DATA_MANAGER_URL=htwkalender-data-manager
|
||||||
networks:
|
networks:
|
||||||
- "net"
|
- "net"
|
||||||
|
depends_on:
|
||||||
|
- htwkalender-data-manager
|
||||||
|
|
||||||
htwkalender-frontend:
|
htwkalender-frontend:
|
||||||
image: DOCKER_REGISTRY_REPO-frontend # DOCKER_REGISTRY_REPO will be replaced by CI
|
image: DOCKER_REGISTRY_REPO-frontend # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||||
|
@ -33,6 +33,8 @@ services:
|
|||||||
- DATA_MANAGER_URL=htwkalender-data-manager
|
- DATA_MANAGER_URL=htwkalender-data-manager
|
||||||
networks:
|
networks:
|
||||||
- "net"
|
- "net"
|
||||||
|
depends_on:
|
||||||
|
- htwkalender-data-manager
|
||||||
|
|
||||||
htwkalender-frontend:
|
htwkalender-frontend:
|
||||||
image: DOCKER_REGISTRY_REPO-frontend # DOCKER_REGISTRY_REPO will be replaced by CI
|
image: DOCKER_REGISTRY_REPO-frontend # DOCKER_REGISTRY_REPO will be replaced by CI
|
||||||
|
@ -35,6 +35,8 @@ services:
|
|||||||
target: dev # prod
|
target: dev # prod
|
||||||
environment:
|
environment:
|
||||||
- DATA_MANAGER_URL=htwkalender-data-manager
|
- DATA_MANAGER_URL=htwkalender-data-manager
|
||||||
|
depends_on:
|
||||||
|
- htwkalender-data-manager
|
||||||
|
|
||||||
htwkalender-frontend:
|
htwkalender-frontend:
|
||||||
build:
|
build:
|
||||||
|
@ -27,11 +27,10 @@ http {
|
|||||||
include mime.types;
|
include mime.types;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
|
|
||||||
# Define a custom log format for anonymizing logs
|
|
||||||
log_format anonymized '[$time_local] "$request" $status $body_bytes_sent "$http_referer"';
|
log_format anonymized '[$time_local] "$request" $status $body_bytes_sent "$http_referer"';
|
||||||
|
|
||||||
access_log /opt/bitnami/nginx/logs/proxy_access.log anonymized;
|
access_log /opt/bitnami/nginx/logs/proxy_access.log anonymized;
|
||||||
error_log /opt/bitnami/nginx/logs/proxy_error.log;
|
error_log /opt/bitnami/nginx/logs/proxy_error.log error;
|
||||||
|
|
||||||
sendfile on;
|
sendfile on;
|
||||||
keepalive_timeout 180s;
|
keepalive_timeout 180s;
|
||||||
|
@ -59,11 +59,10 @@ http {
|
|||||||
|
|
||||||
real_ip_header CF-Connecting-IP;
|
real_ip_header CF-Connecting-IP;
|
||||||
|
|
||||||
# Define a custom log format for anonymizing logs
|
|
||||||
log_format anonymized '[$time_local] "$request" $status $body_bytes_sent "$http_referer"';
|
log_format anonymized '[$time_local] "$request" $status $body_bytes_sent "$http_referer"';
|
||||||
|
|
||||||
access_log /opt/bitnami/nginx/logs/proxy_access.log anonymized;
|
access_log /opt/bitnami/nginx/logs/proxy_access.log anonymized;
|
||||||
error_log /opt/bitnami/nginx/logs/proxy_error.log;
|
error_log /opt/bitnami/nginx/logs/proxy_error.log error;
|
||||||
|
|
||||||
sendfile on;
|
sendfile on;
|
||||||
keepalive_timeout 180s;
|
keepalive_timeout 180s;
|
||||||
|
@ -60,8 +60,10 @@ http {
|
|||||||
|
|
||||||
real_ip_header CF-Connecting-IP;
|
real_ip_header CF-Connecting-IP;
|
||||||
|
|
||||||
access_log /opt/bitnami/nginx/logs/proxy_access.log;
|
log_format anonymized '[$time_local] "$request" $status $body_bytes_sent "$http_referer"';
|
||||||
error_log /opt/bitnami/nginx/logs/proxy_error.log;
|
|
||||||
|
access_log /opt/bitnami/nginx/logs/proxy_access.log anonymized;
|
||||||
|
error_log /opt/bitnami/nginx/logs/proxy_error.log error;
|
||||||
|
|
||||||
sendfile on;
|
sendfile on;
|
||||||
keepalive_timeout 180s;
|
keepalive_timeout 180s;
|
||||||
|
@ -10,6 +10,12 @@ events {
|
|||||||
http {
|
http {
|
||||||
include mime.types;
|
include mime.types;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
|
gzip on;
|
||||||
|
|
||||||
|
log_format anonymized '[$time_local] "$request" $status $body_bytes_sent "$http_referer"';
|
||||||
|
|
||||||
|
access_log /opt/bitnami/nginx/logs/proxy_access.log anonymized;
|
||||||
|
error_log /opt/bitnami/nginx/logs/proxy_error.log error;
|
||||||
|
|
||||||
map $request_method $ratelimit_key {
|
map $request_method $ratelimit_key {
|
||||||
POST $binary_remote_addr;
|
POST $binary_remote_addr;
|
||||||
|
@ -2,8 +2,10 @@ package grpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
pb "htwkalender/common/genproto/modules"
|
pb "htwkalender/common/genproto/modules"
|
||||||
@ -14,7 +16,20 @@ func StartGRPCServer(app *pocketbase.PocketBase) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to listen: %v", err)
|
log.Fatalf("failed to listen: %v", err)
|
||||||
}
|
}
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer(
|
||||||
|
grpc.KeepaliveParams(keepalive.ServerParameters{
|
||||||
|
MaxConnectionIdle: 5 * time.Minute, // Idle timeout before closing connection
|
||||||
|
MaxConnectionAge: 30 * time.Minute, // Max time before connection is closed
|
||||||
|
MaxConnectionAgeGrace: 5 * time.Minute, // Allow grace period before closing
|
||||||
|
Time: 2 * time.Minute, // Ping the client every 2 minutes
|
||||||
|
Timeout: 20 * time.Second, // Wait 20 seconds for ping ack
|
||||||
|
}),
|
||||||
|
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
|
||||||
|
MinTime: 1 * time.Minute, // Minimum time between pings from clients
|
||||||
|
PermitWithoutStream: true, // Don't allow pings when there are no active RPCs
|
||||||
|
}),
|
||||||
|
grpc.MaxConcurrentStreams(0),
|
||||||
|
)
|
||||||
|
|
||||||
pb.RegisterModuleServiceServer(s, &ModuleServiceHandler{
|
pb.RegisterModuleServiceServer(s, &ModuleServiceHandler{
|
||||||
app: app,
|
app: app,
|
||||||
|
@ -39,8 +39,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
grpcClient := grpc.ConnectGRPCServer(host)
|
grpcClient := grpc.ConnectGRPCServer(host)
|
||||||
|
|
||||||
|
// Close the grpc connection when the main function ends
|
||||||
defer grpc.CloseGRPCServer(grpcClient)
|
defer grpc.CloseGRPCServer(grpcClient)
|
||||||
|
|
||||||
|
// Log the grpc connection
|
||||||
|
// Test the connection to the grpc server
|
||||||
|
grpcClient.Connect()
|
||||||
|
slog.Info("GRPC connection state", "state", grpcClient.GetState())
|
||||||
|
|
||||||
// Initialize a new Fiber app
|
// Initialize a new Fiber app
|
||||||
webdavRequestMethods := []string{"PROPFIND", "MKCOL", "COPY", "MOVE"}
|
webdavRequestMethods := []string{"PROPFIND", "MKCOL", "COPY", "MOVE"}
|
||||||
|
|
||||||
|
@ -37,6 +37,14 @@ func (events Events) Sort() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (events Events) String() string {
|
||||||
|
var str strings.Builder
|
||||||
|
for _, event := range events {
|
||||||
|
str.WriteString(event.String())
|
||||||
|
}
|
||||||
|
return str.String()
|
||||||
|
}
|
||||||
|
|
||||||
type AnonymizedEventDTO struct {
|
type AnonymizedEventDTO struct {
|
||||||
Day string `db:"Day" json:"day"`
|
Day string `db:"Day" json:"day"`
|
||||||
Week string `db:"Week" json:"week"`
|
Week string `db:"Week" json:"week"`
|
||||||
@ -109,3 +117,7 @@ func (e *Event) GetName() string {
|
|||||||
func (e *Event) SetName(name string) {
|
func (e *Event) SetName(name string) {
|
||||||
e.Name = name
|
e.Name = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Event) String() string {
|
||||||
|
return e.UUID + e.Day + e.Week + e.Start.String() + e.End.String() + e.Name + e.EventType + e.Compulsory + e.Prof + e.Rooms + e.Notes + e.BookedAt + e.Course + e.Semester
|
||||||
|
}
|
||||||
|
@ -92,3 +92,7 @@ func ToJSONTime(timeString string) JSONTime {
|
|||||||
}
|
}
|
||||||
return JSONTime(t)
|
return JSONTime(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j JSONTime) String() string {
|
||||||
|
return time.Time(j).Format(DefaultDateLayout)
|
||||||
|
}
|
||||||
|
@ -19,14 +19,31 @@ package grpc
|
|||||||
import (
|
import (
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var conn *grpc.ClientConn
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
func ConnectGRPCServer(host string) *grpc.ClientConn {
|
func ConnectGRPCServer(host string) *grpc.ClientConn {
|
||||||
conn, err := grpc.NewClient(host+":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
once.Do(func() {
|
||||||
|
var err error
|
||||||
|
conn, err = grpc.NewClient(
|
||||||
|
host+":50051",
|
||||||
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||||
|
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||||
|
Time: 2 * time.Minute,
|
||||||
|
Timeout: 20 * time.Second,
|
||||||
|
PermitWithoutStream: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("could not connect to grpc server", "error", err)
|
slog.Error("could not connect to grpc server", "error", err)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"htwkalender/ical/model"
|
"htwkalender/ical/model"
|
||||||
"htwkalender/ical/service/connector"
|
"htwkalender/ical/service/connector"
|
||||||
htwkalenderGrpc "htwkalender/ical/service/connector/grpc"
|
htwkalenderGrpc "htwkalender/ical/service/connector/grpc"
|
||||||
|
"htwkalender/ical/service/functions"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -31,7 +32,7 @@ const expirationTime = 5 * time.Minute
|
|||||||
|
|
||||||
var FeedDeletedError = fmt.Errorf("feed deleted")
|
var FeedDeletedError = fmt.Errorf("feed deleted")
|
||||||
|
|
||||||
func Feed(app model.AppType, token string) (string, error) {
|
func Feed(app model.AppType, token string) (string, string, error) {
|
||||||
|
|
||||||
var events model.Events
|
var events model.Events
|
||||||
modules := map[string]model.FeedCollection{}
|
modules := map[string]model.FeedCollection{}
|
||||||
@ -47,17 +48,17 @@ func Feed(app model.AppType, token string) (string, error) {
|
|||||||
// get feed by token
|
// get feed by token
|
||||||
feed, err := htwkalenderGrpc.GetFeed(token, app.GrpcClient)
|
feed, err := htwkalenderGrpc.GetFeed(token, app.GrpcClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if feed.Deleted {
|
if feed.Deleted {
|
||||||
return "", FeedDeletedError
|
return "", "", FeedDeletedError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all events for modules
|
// Get all events for modules
|
||||||
events, err = htwkalenderGrpc.GetEvents(feed.Modules, app.GrpcClient)
|
events, err = htwkalenderGrpc.GetEvents(feed.Modules, app.GrpcClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort events by start date
|
// Sort events by start date
|
||||||
@ -68,11 +69,14 @@ func Feed(app model.AppType, token string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate one Hash for E-TAG from all events and modules
|
||||||
|
etag := functions.HashString(events.String() + fmt.Sprint(modules))
|
||||||
|
|
||||||
b := bytes.Buffer{}
|
b := bytes.Buffer{}
|
||||||
goics.NewICalEncode(&b).Encode(IcalModel{Events: events, Mapping: modules})
|
goics.NewICalEncode(&b).Encode(IcalModel{Events: events, Mapping: modules})
|
||||||
icalFeed := &model.FeedModel{Content: b.String(), ExpiresAt: model.JSONTime(time.Now().Add(expirationTime))}
|
icalFeed := &model.FeedModel{Content: b.String(), ExpiresAt: model.JSONTime(time.Now().Add(expirationTime))}
|
||||||
|
|
||||||
return icalFeed.Content, nil
|
return icalFeed.Content, etag, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FeedRecord(app model.AppType, token string) (model.FeedRecord, error) {
|
func FeedRecord(app model.AppType, token string) (model.FeedRecord, error) {
|
||||||
|
@ -34,7 +34,8 @@ func AddFeedRoutes(app model.AppType) {
|
|||||||
app.Fiber.Get("/api/feed", func(c fiber.Ctx) error {
|
app.Fiber.Get("/api/feed", func(c fiber.Ctx) error {
|
||||||
|
|
||||||
token := c.Query("token")
|
token := c.Query("token")
|
||||||
results, err := ical.Feed(app, token)
|
ifNoneMatch := c.Get("If-None-Match")
|
||||||
|
results, eTag, err := ical.Feed(app, token)
|
||||||
|
|
||||||
if errors.Is(err, ical.FeedDeletedError) {
|
if errors.Is(err, ical.FeedDeletedError) {
|
||||||
return c.SendStatus(fiber.StatusGone)
|
return c.SendStatus(fiber.StatusGone)
|
||||||
@ -44,10 +45,16 @@ func AddFeedRoutes(app model.AppType) {
|
|||||||
slog.Error("Failed to get feed", "error", err, "token", token)
|
slog.Error("Failed to get feed", "error", err, "token", token)
|
||||||
return c.SendStatus(fiber.StatusNotFound)
|
return c.SendStatus(fiber.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ifNoneMatch == eTag && ifNoneMatch != "" {
|
||||||
|
return c.SendStatus(fiber.StatusNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
c.Response().Header.Set("Content-type", "text/calendar")
|
c.Response().Header.Set("Content-type", "text/calendar")
|
||||||
c.Response().Header.Set("charset", "utf-8")
|
c.Response().Header.Set("charset", "utf-8")
|
||||||
c.Response().Header.Set("Content-Disposition", "inline")
|
c.Response().Header.Set("Content-Disposition", "inline")
|
||||||
c.Response().Header.Set("filename", "calendar.ics")
|
c.Response().Header.Set("filename", "calendar.ics")
|
||||||
|
c.Response().Header.Set("ETag", eTag)
|
||||||
|
|
||||||
return c.SendString(results)
|
return c.SendString(results)
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user