diff --git a/frontend/src/api/loadCalendar.ts b/frontend/src/api/loadCalendar.ts index 87bfdda..1cfe705 100644 --- a/frontend/src/api/loadCalendar.ts +++ b/frontend/src/api/loadCalendar.ts @@ -21,7 +21,7 @@ export async function getCalender(token: string): Promise { if (import.meta.env.SSR) { return []; } - const request = new Request("/api/collections/feeds/records/" + token, { + const request = new Request("/api/feeds/records/" + token, { method: "GET", }); diff --git a/services/common/genproto/modules/feeds.pb.go b/services/common/genproto/modules/feeds.pb.go index 97aaa32..7dfdaf7 100644 --- a/services/common/genproto/modules/feeds.pb.go +++ b/services/common/genproto/modules/feeds.pb.go @@ -30,6 +30,7 @@ type Feed struct { 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() { @@ -99,6 +100,13 @@ func (x *Feed) GetUpdated() string { return "" } +func (x *Feed) GetDeleted() bool { + if x != nil { + return x.Deleted + } + return false +} + type GetFeedRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -196,7 +204,7 @@ func (x *GetFeedResponse) GetFeed() *Feed { 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, 0x82, 0x01, + 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, @@ -205,18 +213,20 @@ var file_feeds_proto_rawDesc = []byte{ 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, 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, + 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 ( diff --git a/services/data-manager/migrations/1720871249_collections_snapshot.go b/services/data-manager/migrations/1720871249_collections_snapshot.go new file mode 100644 index 0000000..4075f65 --- /dev/null +++ b/services/data-manager/migrations/1720871249_collections_snapshot.go @@ -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 + }) +} diff --git a/services/data-manager/migrations/1720871405_updated_feeds.go b/services/data-manager/migrations/1720871405_updated_feeds.go new file mode 100644 index 0000000..1a9e561 --- /dev/null +++ b/services/data-manager/migrations/1720871405_updated_feeds.go @@ -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) + }) +} diff --git a/services/data-manager/model/feedModel.go b/services/data-manager/model/feedModel.go index 1306ab1..106d38e 100644 --- a/services/data-manager/model/feedModel.go +++ b/services/data-manager/model/feedModel.go @@ -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 diff --git a/services/data-manager/service/addCalDavRoutes.go b/services/data-manager/service/addCalDavRoutes.go index f487031..18271f2 100644 --- a/services/data-manager/service/addCalDavRoutes.go +++ b/services/data-manager/service/addCalDavRoutes.go @@ -21,7 +21,7 @@ import ( "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/apis" "github.com/pocketbase/pocketbase/core" - "htwkalender/data-manager/service/db" + "htwkalender/data-manager/service/feed" "htwkalender/data-manager/service/ical" "io" "log/slog" @@ -58,7 +58,7 @@ func addFeedRoutes(app *pocketbase.PocketBase) { Path: "/api/feed", Handler: func(c echo.Context) error { token := c.QueryParam("token") - err := db.DeleteFeed(app.Dao(), token) + err := feed.MarkFeedForDeletion(app.Dao(), token) if err != nil { return c.JSON(http.StatusNotFound, err) } else { diff --git a/services/data-manager/service/db/dbFeeds.go b/services/data-manager/service/db/dbFeeds.go index 2ae46e1..3def9b0 100644 --- a/services/data-manager/service/db/dbFeeds.go +++ b/services/data-manager/service/db/dbFeeds.go @@ -48,11 +48,13 @@ func FindFeedByToken(app *pocketbase.PocketBase, token string) (*model.Feed, err var feed model.Feed feed.Modules = record.GetString("modules") feed.Retrieved = record.GetDateTime("retrieved") + feed.Deleted = record.GetBool("deleted") - //update retrieved time - record.Set("retrieved", time.Now()) - - err = app.Dao().SaveRecord(record) + //update retrieved time if the is not marked as deleted + if !record.GetBool("deleted") { + record.Set("retrieved", time.Now()) + err = app.Dao().SaveRecord(record) + } return &feed, err } @@ -84,3 +86,22 @@ func GetAllFeeds(db *daos.Dao) ([]model.Feed, error) { } return feeds, nil } + +func MarkForDelete(db *daos.Dao, token string) error { + // get record from db + feed := model.Feed{} + err := db.DB().Select("*").From("feeds").Where(dbx.NewExp("id = {:id}", dbx.Params{"id": token})).One(&feed) + if err != nil { + return err + } + // set delete flag + feed.Deleted = true + feed.Modules = "[\n {\n \"uuid\": \"\",\n \"name\": \"Deleted\",\n \"course\": \"\",\n \"userDefinedName\": \"Deleted\",\n \"reminder\": false\n }\n]" + + // save record + err = db.Save(&feed) + if err != nil { + return err + } + return nil +} diff --git a/services/data-manager/service/feed/feedFunctions.go b/services/data-manager/service/feed/feedFunctions.go index 6c3da36..6c16c7e 100644 --- a/services/data-manager/service/feed/feedFunctions.go +++ b/services/data-manager/service/feed/feedFunctions.go @@ -17,8 +17,6 @@ package feed import ( - "database/sql" - "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/daos" "htwkalender/data-manager/model" database "htwkalender/data-manager/service/db" @@ -41,13 +39,9 @@ func ClearFeeds(db *daos.Dao, months int, clock localTime.Clock) { if feedRetrievedTime.Before(timeShift) { // delete feed - var sqlResult sql.Result - sqlResult, err = db.DB().Delete("feeds", dbx.NewExp("id = {:id}", dbx.Params{"id": feed.GetId()})).Execute() - if err != nil { - slog.Error("CleanFeeds: delete feed "+feed.GetId()+" failed", "error", err) - slog.Error("SQL Result: ", "error", sqlResult) - } else { - slog.Info("CleanFeeds: delete feed " + feed.GetId() + " successful") + feedErr := database.DeleteFeed(db, feed.GetId()) + if feedErr != nil { + slog.Error("CleanFeeds: failed to delete feed: "+feed.GetId(), "error", feedErr) } } } @@ -124,3 +118,7 @@ func combineRooms(events model.Events, index1 int, combinedEvents model.Events, } return combinedEvents[index2].Rooms } + +func MarkFeedForDeletion(db *daos.Dao, feedId string) error { + return database.MarkForDelete(db, feedId) +} diff --git a/services/data-manager/service/grpc/mapper.go b/services/data-manager/service/grpc/mapper.go index ee3ea58..4981044 100644 --- a/services/data-manager/service/grpc/mapper.go +++ b/services/data-manager/service/grpc/mapper.go @@ -39,5 +39,6 @@ func feedToProto(feed *model.Feed) *pb.Feed { Updated: feed.Updated.String(), Retrieved: feed.Retrieved.String(), Modules: feed.Modules, + Deleted: feed.Deleted, } } diff --git a/services/ical/model/appModel.go b/services/ical/model/appModel.go index 2238b09..40f481d 100644 --- a/services/ical/model/appModel.go +++ b/services/ical/model/appModel.go @@ -1,3 +1,19 @@ +//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 . + package model import ( diff --git a/services/ical/model/icalModel.go b/services/ical/model/icalModel.go index 7ff9263..14121fd 100644 --- a/services/ical/model/icalModel.go +++ b/services/ical/model/icalModel.go @@ -61,6 +61,7 @@ type FeedModule struct { type FeedRecord struct { Modules []FeedModule `db:"modules" json:"modules"` Retrieved JSONTime `db:"retrieved" json:"retrieved"` + Deleted bool `db:"deleted" json:"deleted"` BaseModel } diff --git a/services/ical/model/moduleModel.go b/services/ical/model/moduleModel.go index eddb839..558801f 100644 --- a/services/ical/model/moduleModel.go +++ b/services/ical/model/moduleModel.go @@ -1,3 +1,19 @@ +//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 . + package model type Module struct { diff --git a/services/ical/service/connector/feedConnector.go b/services/ical/service/connector/feedConnector.go index e545791..9ff12f4 100644 --- a/services/ical/service/connector/feedConnector.go +++ b/services/ical/service/connector/feedConnector.go @@ -1,8 +1,25 @@ +//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 . + package connector import ( "encoding/json" "errors" + "fmt" "htwkalender/ical/model" "log/slog" ) @@ -13,9 +30,9 @@ func GetFeedByToken(host string, token string) (model.FeedRecord, error) { // /api/collections/feeds/records/{id} response, err := RequestApi(host, "/api/collections/feeds/records/"+token) - if err != nil { + if err != nil || response.StatusCode() != 200 { slog.Error("Failed to get feed record", "error", err) - return model.FeedRecord{}, err + return model.FeedRecord{}, fmt.Errorf("failed to get feed record") } // parse the response body json to FeedRecord struct diff --git a/services/ical/service/connector/grpc/client.go b/services/ical/service/connector/grpc/client.go index f9c7275..066d62b 100644 --- a/services/ical/service/connector/grpc/client.go +++ b/services/ical/service/connector/grpc/client.go @@ -1,3 +1,19 @@ +//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 . + package grpc import ( diff --git a/services/ical/service/connector/grpc/events.go b/services/ical/service/connector/grpc/events.go index fc82755..bcb6b1b 100644 --- a/services/ical/service/connector/grpc/events.go +++ b/services/ical/service/connector/grpc/events.go @@ -1,3 +1,19 @@ +//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 . + package grpc import ( diff --git a/services/ical/service/connector/grpc/feeds.go b/services/ical/service/connector/grpc/feeds.go index 0f1f082..a8a16f6 100644 --- a/services/ical/service/connector/grpc/feeds.go +++ b/services/ical/service/connector/grpc/feeds.go @@ -1,3 +1,19 @@ +//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 . + package grpc import ( @@ -48,5 +64,6 @@ func protoToFeed(feed *pb.Feed) (model.FeedRecord, error) { }, Retrieved: model.ToJSONTime(feed.Retrieved), Modules: modules, + Deleted: feed.Deleted, }, nil } diff --git a/services/ical/service/connector/grpc/modules.go b/services/ical/service/connector/grpc/modules.go index aa47d79..d1bb195 100644 --- a/services/ical/service/connector/grpc/modules.go +++ b/services/ical/service/connector/grpc/modules.go @@ -1,3 +1,19 @@ +//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 . + package grpc import ( diff --git a/services/ical/service/connector/restHandler.go b/services/ical/service/connector/restHandler.go index 4af7ba0..9cc58a8 100644 --- a/services/ical/service/connector/restHandler.go +++ b/services/ical/service/connector/restHandler.go @@ -1,3 +1,19 @@ +//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 . + package connector import ( diff --git a/services/ical/service/functions/semester_test.go b/services/ical/service/functions/semester_test.go index 9595ede..6200a75 100644 --- a/services/ical/service/functions/semester_test.go +++ b/services/ical/service/functions/semester_test.go @@ -1,3 +1,19 @@ +//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 . + package functions import ( diff --git a/services/ical/service/ical/ical.go b/services/ical/service/ical/ical.go index baa6160..7582fa6 100644 --- a/services/ical/service/ical/ical.go +++ b/services/ical/service/ical/ical.go @@ -1,7 +1,24 @@ +//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 . + package ical import ( "bytes" + "fmt" "github.com/jordic/goics" "htwkalender/ical/model" "htwkalender/ical/service/connector" @@ -12,6 +29,8 @@ import ( const expirationTime = 5 * time.Minute +var FeedDeletedError = fmt.Errorf("feed deleted") + func Feed(app model.AppType, token string) (string, error) { // get feed by token feed, err := htwkalenderGrpc.GetFeed(token, app.GrpcClient) @@ -19,6 +38,10 @@ func Feed(app model.AppType, token string) (string, error) { return "", err } + if feed.Deleted { + return "", FeedDeletedError + } + var events model.Events // Get all events for modules diff --git a/services/ical/service/routes.go b/services/ical/service/routes.go index 3aa0381..c92a0f7 100644 --- a/services/ical/service/routes.go +++ b/services/ical/service/routes.go @@ -1,7 +1,24 @@ +//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 . + package service import ( "encoding/json" + "errors" "github.com/gofiber/fiber/v3" "htwkalender/ical/model" "htwkalender/ical/service/ical" @@ -19,9 +36,13 @@ func AddFeedRoutes(app model.AppType) { token := c.Query("token") results, err := ical.Feed(app, token) + if errors.Is(err, ical.FeedDeletedError) { + return c.SendStatus(fiber.StatusGone) + } + if err != nil { slog.Error("Failed to get feed", "error", err, "token", token) - return c.SendStatus(fiber.StatusBadRequest) + return c.SendStatus(fiber.StatusNotFound) } c.Response().Header.Set("Content-type", "text/calendar") c.Response().Header.Set("charset", "utf-8") @@ -53,13 +74,17 @@ func AddFeedRoutes(app model.AppType) { }) // Define a route for the GET method on the root path '/' - app.Fiber.Get("/api/collections/feeds/records/:token", func(c fiber.Ctx) error { + app.Fiber.Get("/api/feeds/records/:token", func(c fiber.Ctx) error { token := c.Params("token") results, err := ical.FeedRecord(app, token) + if results.Deleted { + return c.SendStatus(fiber.StatusNotFound) + } + if err != nil { - slog.Error("Failed to get feed", "error", err, "token", token) + slog.Error("Failed to get feed record", "error", err, "token", token) return c.SendStatus(fiber.StatusBadRequest) } c.Response().Header.Set("Content-type", "application/json; charset=UTF-8") diff --git a/services/protobuf/feeds.proto b/services/protobuf/feeds.proto index 7580cbd..243b83a 100644 --- a/services/protobuf/feeds.proto +++ b/services/protobuf/feeds.proto @@ -12,6 +12,7 @@ message Feed { string retrieved = 3; string created = 4; string updated = 5; + bool deleted = 6; } message GetFeedRequest {