diff --git a/Dockerfile b/Dockerfile index a5d6f36..2158892 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,9 @@ COPY .git . RUN npm ci COPY web/ . -RUN npm run build + +RUN GIT_DESCRIBE="$(git describe --always --dirty --tags 2>/dev/null || echo unknown)" \ + npm run build # go builder @@ -25,6 +27,9 @@ RUN go mod download COPY . . +RUN go install github.com/swaggo/swag/cmd/swag@latest +RUN swag init --parseDependency + RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /makedotcsh diff --git a/database/db.go b/database/db.go index 6b566af..88918c5 100644 --- a/database/db.go +++ b/database/db.go @@ -66,6 +66,19 @@ func (database *DatabaseHelper) GetArea(areaID int) (models.Area, error) { return area, nil } +func (database *DatabaseHelper) DeleteArea(areaID int) error { + _, err := database.DB.Exec( + "DELETE FROM areas WHERE id = $1", + areaID, + ) + + if err != nil { + return err + } + + return nil +} + func (database *DatabaseHelper) GetAllAreas() ([]models.Area, error) { rows, err := database.DB.Query("SELECT id, name, description, ldapgroup, photourl FROM areas") if err != nil { @@ -135,7 +148,7 @@ func (database *DatabaseHelper) GetTraining(trainingID int, includeAnswers bool) var currentQuestion *models.Question lastQuestionId := 0 - query := "SELECT id, title, required_correct, description FROM trainings WHERE id = $1" + query := "SELECT id, title, required_correct, description, show_answers FROM trainings WHERE id = $1" row := database.DB.QueryRow(query, trainingID) @@ -144,6 +157,7 @@ func (database *DatabaseHelper) GetTraining(trainingID int, includeAnswers bool) &training.Title, &training.RequiredCorrect, &training.Description, + &training.ShowAnswers, ) if err != nil { return models.Training{}, err @@ -233,10 +247,11 @@ func (database *DatabaseHelper) CreateTraining(training models.CreateTrainingReq var trainingId int err = tx.QueryRow( - "INSERT INTO trainings (title, required_correct, description) VALUES ($1, $2, $3) RETURNING id", + "INSERT INTO trainings (title, required_correct, description, showAnswers) VALUES ($1, $2, $3, $4) RETURNING id", training.Title, training.RequiredCorrect, training.Description, + training.ShowAnswers, ).Scan(&trainingId) if err != nil { @@ -286,10 +301,11 @@ func (database *DatabaseHelper) UpdateTraining(training models.CreateTrainingReq defer tx.Rollback() _, err = tx.Exec( - "UPDATE trainings SET title = $1, required_correct = $2, description = $3 WHERE id = $4", + "UPDATE trainings SET title = $1, required_correct = $2, description = $3, show_answers = $4 WHERE id = $5", training.Title, training.RequiredCorrect, training.Description, + training.ShowAnswers, trainingId, ) @@ -562,3 +578,152 @@ func (database *DatabaseHelper) GetTrainingAnswers(trainingID int) (map[int]stri return answers, nil } + +func (database *DatabaseHelper) GetResource(rID int) (models.Resource, error) { + row := database.DB.QueryRow("SELECT id, name, description, url FROM resources WHERE id = $1", rID) + + var resource models.Resource + + err := row.Scan( + &resource.ID, + &resource.Name, + &resource.Description, + &resource.URL, + ) + + if err != nil { + return models.Resource{}, err + } + + return resource, nil +} + +func (database *DatabaseHelper) GetAllResources() ([]models.Resource, error) { + rows, err := database.DB.Query("SELECT id, name, description, url FROM resources") + if err != nil { + return []models.Resource{}, nil + } + + var resources []models.Resource = []models.Resource{} + + for rows.Next() { + var r models.Resource + + err := rows.Scan( + &r.ID, + &r.Name, + &r.Description, + &r.URL, + ) + + if err != nil { + return []models.Resource{}, nil + } + + resources = append(resources, r) + } + + return resources, nil +} + +func (database *DatabaseHelper) GetAreaResources(area int) ([]models.Resource, error) { + rows, err := database.DB.Query("SELECT resource_id FROM area_resources WHERE area_id = $1", area) + if err != nil { + return []models.Resource{}, nil + } + + var resources []models.Resource = []models.Resource{} + + for rows.Next() { + var resourceID int + + err := rows.Scan( + &resourceID, + ) + + if err != nil { + return []models.Resource{}, err + } + + // fetch this training, add to array + resource, err := database.GetResource(resourceID) + if err != nil { + return []models.Resource{}, err + } + + resources = append(resources, resource) + } + + return resources, nil +} + +func (database *DatabaseHelper) CreateResource(r models.Resource) error { + _, err := database.DB.Exec( + "INSERT INTO resources (name, description, url) VALUES ($1, $2, $3)", + r.Name, + r.Description, + r.URL, + ) + + if err != nil { + return err + } + + return nil +} + +func (database *DatabaseHelper) UpdateResource(r models.Resource, id int) error { + _, err := database.DB.Exec( + "UPDATE resources SET name = $1, description = $2, url = $3 WHERE id = $4", + r.Name, + r.Description, + r.URL, + id, + ) + + if err != nil { + return err + } + + return nil +} + +func (database *DatabaseHelper) DeleteResource(rID int) error { + _, err := database.DB.Exec( + "DELETE FROM resources WHERE id = $1", + rID, + ) + + if err != nil { + return err + } + + return nil +} + +func (database *DatabaseHelper) AddResourceToArea(areaID int, resourceIDs []int) error { + for _, resourceID := range resourceIDs { + _, err := database.DB.Exec( + "INSERT INTO area_resources (area_id, resource_id) VALUES ($1, $2)", + areaID, resourceID, + ) + + if err != nil { + return err + } + } + + return nil +} + +func (database *DatabaseHelper) RemoveResourceFromArea(areaID int, resourceID int) error { + _, err := database.DB.Exec( + "DELETE FROM area_resources WHERE area_id = $1 AND resource_id = $2", + areaID, resourceID, + ) + + if err != nil { + return err + } + return nil +} diff --git a/database/migrations/0003_resources.up.sql b/database/migrations/0003_resources.up.sql new file mode 100644 index 0000000..c6c78ba --- /dev/null +++ b/database/migrations/0003_resources.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE resources ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + description TEXT NOT NULL, + url TEXT NOT NULL +); + +CREATE TABLE area_resources ( + area_id INTEGER NOT NULL, + resource_id INTEGER NOT NULL, + PRIMARY KEY (area_id, resource_id), + FOREIGN KEY (area_id) REFERENCES areas(id) ON DELETE CASCADE, + FOREIGN KEY (resource_id) REFERENCES resources(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/database/migrations/0004_training.up.sql b/database/migrations/0004_training.up.sql new file mode 100644 index 0000000..2064918 --- /dev/null +++ b/database/migrations/0004_training.up.sql @@ -0,0 +1 @@ +ALTER TABLE trainings ADD show_answers BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go index 4240bef..5dddae9 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -172,6 +172,48 @@ const docTemplate = `{ } } } + }, + "delete": { + "description": "Deletes an area", + "consumes": [ + "application/json" + ], + "tags": [ + "areas" + ], + "summary": "Deletes an area", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } } }, "/me": { @@ -229,23 +271,23 @@ const docTemplate = `{ } } }, - "/trainings": { + "/resources": { "get": { - "description": "Returns all trainings", + "description": "Returns all resources", "produces": [ "application/json" ], "tags": [ - "trainings" + "resources" ], - "summary": "Get all trainings", + "summary": "Get all resources", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/models.Training" + "$ref": "#/definitions/models.Resource" } } }, @@ -256,18 +298,56 @@ const docTemplate = `{ } } } + }, + "post": { + "description": "Creates a new resource", + "consumes": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "Create resource", + "parameters": [ + { + "description": "Resource", + "name": "resource", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.Resource" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } } }, - "/trainings/area/{id}": { + "/resources/area/{id}": { "get": { - "description": "Returns all trainings required for an area", + "description": "Returns all resources associated with an area", "produces": [ "application/json" ], "tags": [ - "trainings" + "resources" ], - "summary": "Get area trainings", + "summary": "Get area resources", "parameters": [ { "type": "integer", @@ -283,7 +363,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/models.Training" + "$ref": "#/definitions/models.Resource" } } }, @@ -302,14 +382,17 @@ const docTemplate = `{ } }, "post": { - "description": "Associates one or more trainings with an area", + "description": "Adds resources to an area", "consumes": [ "application/json" ], + "produces": [ + "application/json" + ], "tags": [ - "trainings" + "resources" ], - "summary": "Add trainings to area", + "summary": "Add resources to area", "parameters": [ { "type": "integer", @@ -319,8 +402,8 @@ const docTemplate = `{ "required": true }, { - "description": "Training IDs", - "name": "trainingIds", + "description": "Resource IDs", + "name": "request", "in": "body", "required": true, "schema": { @@ -332,8 +415,8 @@ const docTemplate = `{ } ], "responses": { - "201": { - "description": "Created" + "200": { + "description": "OK" }, "400": { "description": "Bad Request", @@ -350,14 +433,14 @@ const docTemplate = `{ } }, "delete": { - "description": "Removes a training requirement from an area", + "description": "Removes a resource from an area", "consumes": [ "application/json" ], "tags": [ - "trainings" + "resources" ], - "summary": "Remove training from area", + "summary": "Remove resource from area", "parameters": [ { "type": "integer", @@ -367,12 +450,12 @@ const docTemplate = `{ "required": true }, { - "description": "Training removal request", + "description": "Resource removal request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/trainings.RemoveTrainingRequest" + "$ref": "#/definitions/resources.RemoveResourceRequest" } } ], @@ -395,7 +478,153 @@ const docTemplate = `{ } } }, - "/trainings/create": { + "/resources/{id}": { + "get": { + "description": "Returns a resource by ID", + "produces": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "Get resource", + "parameters": [ + { + "type": "integer", + "description": "Resource ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Resource" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "put": { + "description": "Updates an existing resource", + "consumes": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "Update resource", + "parameters": [ + { + "type": "integer", + "description": "Resource ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Resource", + "name": "resource", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.Resource" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Deletes a resource by ID", + "tags": [ + "resources" + ], + "summary": "Delete resource", + "parameters": [ + { + "type": "integer", + "description": "Resource ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/trainings": { + "get": { + "description": "Returns all trainings", + "produces": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Get all trainings", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Training" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, "post": { "description": "Create a new training", "consumes": [ @@ -435,6 +664,143 @@ const docTemplate = `{ } } }, + "/trainings/area/{id}": { + "get": { + "description": "Returns all trainings required for an area", + "produces": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Get area trainings", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Training" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "post": { + "description": "Associates one or more trainings with an area", + "consumes": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Add trainings to area", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Training IDs", + "name": "trainingIds", + "in": "body", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Removes a training requirement from an area", + "consumes": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Remove training from area", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Training removal request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/trainings.RemoveTrainingRequest" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, "/trainings/{id}": { "get": { "description": "Returns a training by ID", @@ -594,6 +960,46 @@ const docTemplate = `{ } } } + }, + "/trainings/{id}/submissions": { + "post": { + "description": "Submits and grades a completed training", + "consumes": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Submits and grades a completed training", + "parameters": [ + { + "description": "Training", + "name": "training", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.Submission" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } } }, "definitions": { @@ -623,6 +1029,9 @@ const docTemplate = `{ "ldap.UserWUUID": { "type": "object", "properties": { + "name": { + "type": "string" + }, "username": { "type": "string" }, @@ -680,6 +1089,12 @@ const docTemplate = `{ "$ref": "#/definitions/models.Question" } }, + "requiredCorrect": { + "type": "integer" + }, + "showAnswers": { + "type": "boolean" + }, "title": { "type": "string" } @@ -717,6 +1132,29 @@ const docTemplate = `{ } } }, + "models.Resource": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "models.Submission": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "models.Training": { "type": "object", "properties": { @@ -732,11 +1170,25 @@ const docTemplate = `{ "$ref": "#/definitions/models.Question" } }, + "requiredCorrect": { + "type": "integer" + }, + "showAnswers": { + "type": "boolean" + }, "title": { "type": "string" } } }, + "resources.RemoveResourceRequest": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, "trainings.RemoveTrainingRequest": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index d770ebc..d8fab55 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -164,6 +164,48 @@ } } } + }, + "delete": { + "description": "Deletes an area", + "consumes": [ + "application/json" + ], + "tags": [ + "areas" + ], + "summary": "Deletes an area", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } } }, "/me": { @@ -221,23 +263,23 @@ } } }, - "/trainings": { + "/resources": { "get": { - "description": "Returns all trainings", + "description": "Returns all resources", "produces": [ "application/json" ], "tags": [ - "trainings" + "resources" ], - "summary": "Get all trainings", + "summary": "Get all resources", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { - "$ref": "#/definitions/models.Training" + "$ref": "#/definitions/models.Resource" } } }, @@ -248,18 +290,56 @@ } } } + }, + "post": { + "description": "Creates a new resource", + "consumes": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "Create resource", + "parameters": [ + { + "description": "Resource", + "name": "resource", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.Resource" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } } }, - "/trainings/area/{id}": { + "/resources/area/{id}": { "get": { - "description": "Returns all trainings required for an area", + "description": "Returns all resources associated with an area", "produces": [ "application/json" ], "tags": [ - "trainings" + "resources" ], - "summary": "Get area trainings", + "summary": "Get area resources", "parameters": [ { "type": "integer", @@ -275,7 +355,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/models.Training" + "$ref": "#/definitions/models.Resource" } } }, @@ -294,14 +374,17 @@ } }, "post": { - "description": "Associates one or more trainings with an area", + "description": "Adds resources to an area", "consumes": [ "application/json" ], + "produces": [ + "application/json" + ], "tags": [ - "trainings" + "resources" ], - "summary": "Add trainings to area", + "summary": "Add resources to area", "parameters": [ { "type": "integer", @@ -311,8 +394,8 @@ "required": true }, { - "description": "Training IDs", - "name": "trainingIds", + "description": "Resource IDs", + "name": "request", "in": "body", "required": true, "schema": { @@ -324,8 +407,8 @@ } ], "responses": { - "201": { - "description": "Created" + "200": { + "description": "OK" }, "400": { "description": "Bad Request", @@ -342,14 +425,14 @@ } }, "delete": { - "description": "Removes a training requirement from an area", + "description": "Removes a resource from an area", "consumes": [ "application/json" ], "tags": [ - "trainings" + "resources" ], - "summary": "Remove training from area", + "summary": "Remove resource from area", "parameters": [ { "type": "integer", @@ -359,12 +442,12 @@ "required": true }, { - "description": "Training removal request", + "description": "Resource removal request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/trainings.RemoveTrainingRequest" + "$ref": "#/definitions/resources.RemoveResourceRequest" } } ], @@ -387,7 +470,153 @@ } } }, - "/trainings/create": { + "/resources/{id}": { + "get": { + "description": "Returns a resource by ID", + "produces": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "Get resource", + "parameters": [ + { + "type": "integer", + "description": "Resource ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.Resource" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "put": { + "description": "Updates an existing resource", + "consumes": [ + "application/json" + ], + "tags": [ + "resources" + ], + "summary": "Update resource", + "parameters": [ + { + "type": "integer", + "description": "Resource ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Resource", + "name": "resource", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.Resource" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Deletes a resource by ID", + "tags": [ + "resources" + ], + "summary": "Delete resource", + "parameters": [ + { + "type": "integer", + "description": "Resource ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, + "/trainings": { + "get": { + "description": "Returns all trainings", + "produces": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Get all trainings", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Training" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, "post": { "description": "Create a new training", "consumes": [ @@ -427,6 +656,143 @@ } } }, + "/trainings/area/{id}": { + "get": { + "description": "Returns all trainings required for an area", + "produces": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Get area trainings", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Training" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "post": { + "description": "Associates one or more trainings with an area", + "consumes": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Add trainings to area", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Training IDs", + "name": "trainingIds", + "in": "body", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + }, + "delete": { + "description": "Removes a training requirement from an area", + "consumes": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Remove training from area", + "parameters": [ + { + "type": "integer", + "description": "Area ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Training removal request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/trainings.RemoveTrainingRequest" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } + }, "/trainings/{id}": { "get": { "description": "Returns a training by ID", @@ -586,6 +952,46 @@ } } } + }, + "/trainings/{id}/submissions": { + "post": { + "description": "Submits and grades a completed training", + "consumes": [ + "application/json" + ], + "tags": [ + "trainings" + ], + "summary": "Submits and grades a completed training", + "parameters": [ + { + "description": "Training", + "name": "training", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.Submission" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.ErrorResponse" + } + } + } + } } }, "definitions": { @@ -615,6 +1021,9 @@ "ldap.UserWUUID": { "type": "object", "properties": { + "name": { + "type": "string" + }, "username": { "type": "string" }, @@ -672,6 +1081,12 @@ "$ref": "#/definitions/models.Question" } }, + "requiredCorrect": { + "type": "integer" + }, + "showAnswers": { + "type": "boolean" + }, "title": { "type": "string" } @@ -709,6 +1124,29 @@ } } }, + "models.Resource": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "models.Submission": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "models.Training": { "type": "object", "properties": { @@ -724,11 +1162,25 @@ "$ref": "#/definitions/models.Question" } }, + "requiredCorrect": { + "type": "integer" + }, + "showAnswers": { + "type": "boolean" + }, "title": { "type": "string" } } }, + "resources.RemoveResourceRequest": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, "trainings.RemoveTrainingRequest": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 229be76..4c0dbbb 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -17,6 +17,8 @@ definitions: type: object ldap.UserWUUID: properties: + name: + type: string username: type: string uuid: @@ -54,6 +56,10 @@ definitions: items: $ref: '#/definitions/models.Question' type: array + requiredCorrect: + type: integer + showAnswers: + type: boolean title: type: string type: object @@ -78,6 +84,21 @@ definitions: type: type: string type: object + models.Resource: + properties: + description: + type: string + id: + type: integer + name: + type: string + url: + type: string + type: object + models.Submission: + additionalProperties: + type: string + type: object models.Training: properties: description: @@ -88,9 +109,18 @@ definitions: items: $ref: '#/definitions/models.Question' type: array + requiredCorrect: + type: integer + showAnswers: + type: boolean title: type: string type: object + resources.RemoveResourceRequest: + properties: + id: + type: integer + type: object trainings.RemoveTrainingRequest: properties: id: @@ -146,6 +176,34 @@ paths: tags: - areas /areas/{id}: + delete: + consumes: + - application/json + description: Deletes an area + parameters: + - description: Area ID + in: path + name: id + required: true + type: integer + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Deletes an area + tags: + - areas get: description: Returns an area by ID parameters: @@ -242,6 +300,222 @@ paths: summary: Get active CSH members tags: - members + /resources: + get: + description: Returns all resources + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Resource' + type: array + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Get all resources + tags: + - resources + post: + consumes: + - application/json + description: Creates a new resource + parameters: + - description: Resource + in: body + name: resource + required: true + schema: + $ref: '#/definitions/models.Resource' + responses: + "201": + description: Created + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Create resource + tags: + - resources + /resources/{id}: + delete: + description: Deletes a resource by ID + parameters: + - description: Resource ID + in: path + name: id + required: true + type: integer + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Delete resource + tags: + - resources + get: + description: Returns a resource by ID + parameters: + - description: Resource ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.Resource' + "404": + description: Not Found + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Get resource + tags: + - resources + put: + consumes: + - application/json + description: Updates an existing resource + parameters: + - description: Resource ID + in: path + name: id + required: true + type: integer + - description: Resource + in: body + name: resource + required: true + schema: + $ref: '#/definitions/models.Resource' + responses: + "201": + description: Created + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Update resource + tags: + - resources + /resources/area/{id}: + delete: + consumes: + - application/json + description: Removes a resource from an area + parameters: + - description: Area ID + in: path + name: id + required: true + type: integer + - description: Resource removal request + in: body + name: request + required: true + schema: + $ref: '#/definitions/resources.RemoveResourceRequest' + responses: + "201": + description: Created + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Remove resource from area + tags: + - resources + get: + description: Returns all resources associated with an area + parameters: + - description: Area ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Resource' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Get area resources + tags: + - resources + post: + consumes: + - application/json + description: Adds resources to an area + parameters: + - description: Area ID + in: path + name: id + required: true + type: integer + - description: Resource IDs + in: body + name: request + required: true + schema: + items: + type: integer + type: array + produces: + - application/json + responses: + "200": + description: OK + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Add resources to area + tags: + - resources /trainings: get: description: Returns all trainings @@ -261,6 +535,31 @@ paths: summary: Get all trainings tags: - trainings + post: + consumes: + - application/json + description: Create a new training + parameters: + - description: Training + in: body + name: training + required: true + schema: + $ref: '#/definitions/models.CreateTrainingRequest' + responses: + "201": + description: Created + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Create training + tags: + - trainings /trainings/{id}: delete: description: Deletes a training @@ -367,6 +666,32 @@ paths: summary: Get training tags: - trainings + /trainings/{id}/submissions: + post: + consumes: + - application/json + description: Submits and grades a completed training + parameters: + - description: Training + in: body + name: training + required: true + schema: + $ref: '#/definitions/models.Submission' + responses: + "201": + description: Created + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.ErrorResponse' + summary: Submits and grades a completed training + tags: + - trainings /trainings/area/{id}: delete: consumes: @@ -458,30 +783,4 @@ paths: summary: Add trainings to area tags: - trainings - /trainings/create: - post: - consumes: - - application/json - description: Create a new training - parameters: - - description: Training - in: body - name: training - required: true - schema: - $ref: '#/definitions/models.CreateTrainingRequest' - responses: - "201": - description: Created - "400": - description: Bad Request - schema: - $ref: '#/definitions/models.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/models.ErrorResponse' - summary: Create training - tags: - - trainings swagger: "2.0" diff --git a/go.mod b/go.mod index 42d9430..8aa75c2 100644 --- a/go.mod +++ b/go.mod @@ -3,71 +3,73 @@ module makedotcsh go 1.26.4 require ( - github.com/computersciencehouse/csh-auth/v2 v2.0.2 + github.com/computersciencehouse/csh-auth/v2 v2.1.0 github.com/gin-contrib/cors v1.7.7 github.com/gin-contrib/gzip v1.2.6 github.com/gin-contrib/logger v1.2.7 github.com/gin-gonic/gin v1.12.0 - github.com/go-ldap/ldap/v3 v3.4.13 + github.com/go-ldap/ldap/v3 v3.4.14 github.com/golang-migrate/migrate/v4 v4.19.1 github.com/joho/godotenv v1.5.1 - github.com/lib/pq v1.10.9 + github.com/lib/pq v1.12.3 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.1 github.com/swaggo/swag v1.16.6 ) require ( - github.com/Azure/go-ntlmssp v0.1.0 // indirect + github.com/Azure/go-ntlmssp v0.1.1 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/bytedance/gopkg v0.1.4 // indirect - github.com/bytedance/sonic v1.15.1 // indirect - github.com/bytedance/sonic/loader v0.5.1 // indirect + github.com/bytedance/sonic v1.15.2 // indirect + github.com/bytedance/sonic/loader v0.5.2 // indirect github.com/cloudwego/base64x v0.1.7 // indirect - github.com/coreos/go-oidc/v3 v3.18.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/coreos/go-oidc/v3 v3.20.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.15 // indirect github.com/gin-contrib/sse v1.1.1 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-openapi/jsonpointer v1.0.0 // indirect + github.com/go-openapi/jsonreference v1.0.0 // indirect + github.com/go-openapi/spec v0.22.9 // indirect + github.com/go-openapi/swag/conv v0.28.0 // indirect + github.com/go-openapi/swag/jsonutils v0.28.0 // indirect + github.com/go-openapi/swag/loading v0.28.0 // indirect + github.com/go-openapi/swag/pools v0.28.0 // indirect + github.com/go-openapi/swag/stringutils v0.28.0 // indirect + github.com/go-openapi/swag/typeutils v0.28.0 // indirect + github.com/go-openapi/swag/yamlutils v0.28.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.30.2 // indirect + github.com/go-playground/validator/v10 v10.30.3 // indirect github.com/goccy/go-json v0.10.6 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.22 // indirect + github.com/klauspost/cpuid/v2 v2.4.0 // indirect + github.com/leodido/go-urn v1.5.0 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-isatty v0.0.24 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.59.0 // indirect - github.com/rs/zerolog v1.34.0 // indirect + github.com/quic-go/quic-go v0.61.0 // indirect + github.com/rs/zerolog v1.35.1 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.1 // indirect - go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect - golang.org/x/arch v0.27.0 // indirect - golang.org/x/crypto v0.51.0 // indirect - golang.org/x/mod v0.35.0 // indirect - golang.org/x/net v0.54.0 // indirect + github.com/ugorji/go/codec v1.3.2 // indirect + go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.30.0 // indirect + golang.org/x/crypto v0.54.0 // indirect + golang.org/x/mod v0.38.0 // indirect + golang.org/x/net v0.57.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.44.0 // indirect - golang.org/x/text v0.37.0 // indirect - golang.org/x/tools v0.44.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + golang.org/x/tools v0.48.0 // indirect google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 9ef640b..b73dc39 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,29 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A= -github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk= +github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw= +github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI= github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM= github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4= -github.com/bytedance/sonic v1.15.1 h1:nJD5PmM0vY7J8CT6MxoqbVAAMhkSmV2HgRAUrrpLoOw= -github.com/bytedance/sonic v1.15.1/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA= -github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI= -github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo= +github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA= +github.com/bytedance/sonic/loader v0.5.2 h1:0QtP1gevc1OZ6/H8Lb9BRZiCXd1Ftjd3OKuj1T1lBIo= +github.com/bytedance/sonic/loader v0.5.2/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= -github.com/computersciencehouse/csh-auth/v2 v2.0.2 h1:xlxv2w2cmiWriVNhwA8Io1FWI54sDmv7c12gkVAcK30= -github.com/computersciencehouse/csh-auth/v2 v2.0.2/go.mod h1:uL/2UDr9GJlAQORsn+rmFc18YkBsZPymYJbwb+uTRig= +github.com/computersciencehouse/csh-auth/v2 v2.1.0 h1:L00q0la/2vNZgo6rceICruPYtvTbghl7B8xBCAxpvm0= +github.com/computersciencehouse/csh-auth/v2 v2.1.0/go.mod h1:uL/2UDr9GJlAQORsn+rmFc18YkBsZPymYJbwb+uTRig= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A= -github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/coreos/go-oidc/v3 v3.20.0 h1:EtE0WIBHk03N+DqGkY4+UONzzZHk7amKt6IyNd7OsZE= +github.com/coreos/go-oidc/v3 v3.20.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -46,8 +40,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= -github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI= +github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ= github.com/gin-contrib/cors v1.7.7 h1:Oh9joP463x7Mw72vhvJ61YQm8ODh9b04YR7vsOErD0Q= github.com/gin-contrib/cors v1.7.7/go.mod h1:K5tW0RkzJtWSiOdikXloy8VEZlgdVNpHNw8FpjUPNrE= github.com/gin-contrib/gzip v1.2.6 h1:OtN8DplD5DNZCSLAnQ5HxRkD2qZ5VU+JhOrcfJrcRvg= @@ -58,39 +52,55 @@ github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s= github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= -github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= -github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ= +github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ= -github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0= +github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs= +github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= +github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= +github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY= +github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0= +github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w= +github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0= github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag/conv v0.28.0 h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8= +github.com/go-openapi/swag/conv v0.28.0/go.mod h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k= +github.com/go-openapi/swag/jsonutils v0.28.0 h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4= +github.com/go-openapi/swag/jsonutils v0.28.0/go.mod h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0 h1:qV+VVUAx5Oro8WjVWpZeql7YReTKhT4smR4zhcOQZr0= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= +github.com/go-openapi/swag/loading v0.28.0 h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU= +github.com/go-openapi/swag/loading v0.28.0/go.mod h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k= +github.com/go-openapi/swag/pools v0.28.0 h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU= +github.com/go-openapi/swag/pools v0.28.0/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE= +github.com/go-openapi/swag/stringutils v0.28.0 h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU= +github.com/go-openapi/swag/stringutils v0.28.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= +github.com/go-openapi/swag/typeutils v0.28.0 h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY= +github.com/go-openapi/swag/typeutils v0.28.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= +github.com/go-openapi/swag/yamlutils v0.28.0 h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY= +github.com/go-openapi/swag/yamlutils v0.28.0/go.mod h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo= +github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= +github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ= -github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc= +github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= +github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= @@ -118,34 +128,22 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= +github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= -github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0= +github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= +github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -157,27 +155,27 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= -github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0= +github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= -github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/quic-go/quic-go v0.61.0 h1:ui88A53s8MSVYLC56en0KQ17HARk+9986Dn0SBfKNvA= +github.com/quic-go/quic-go v0.61.0/go.mod h1:9So2anK4Tp22URSQq00k+Vo2PNkle96ycDPDHL4s9vs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= -github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= +github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -185,7 +183,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -200,11 +197,11 @@ github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= -github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/ugorji/go/codec v1.3.2 h1:zkEASHHyEClGeURfgNT9PJZVfAbs9oEX9QXggwWNJbc= +github.com/ugorji/go/codec v1.3.2/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8= -go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8= +go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= @@ -217,67 +214,57 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -golang.org/x/arch v0.27.0 h1:0WNVcR8u9yFz8j5FvdHpgwNp3FS5U4guYdzHwEiGjoU= -golang.org/x/arch v0.27.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.30.0 h1:sB9h+1gRGa2+LauFSV0tm8bK1J2yo1bx6/Uyi/P6DTU= +golang.org/x/arch v0.30.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 7cd3a69..ff7a285 100644 --- a/main.go +++ b/main.go @@ -104,6 +104,7 @@ func main() { // auth router.GET("/auth/login", auth.HandleLogin) // This endpoint should match the path for loginURL router.GET("/auth/callback", auth.HandleCallback) // This endpoint should match the path for callbackURL + router.POST("/auth/refresh", auth.HandleRefresh) router.GET("/auth/logout", auth.HandleLogout) // api @@ -111,13 +112,13 @@ func main() { // frontend if os.Getenv("DEV") == "true" { - router.NoRoute(auth.CookieMiddleware(), createViteProxy()) + router.NoRoute(createViteProxy()) } else { gin.SetMode(gin.ReleaseMode) router.StaticFS("/assets", http.FS(assetsFS)) - router.NoRoute(auth.CookieMiddleware(), func(c *gin.Context) { + router.NoRoute(func(c *gin.Context) { if strings.HasPrefix(c.Request.URL.Path, "/api") { c.JSON(404, gin.H{"error": "not found"}) return diff --git a/models/resources.go b/models/resources.go new file mode 100644 index 0000000..686333d --- /dev/null +++ b/models/resources.go @@ -0,0 +1,8 @@ +package models + +type Resource struct { + ID int `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description"` + URL string `json:"url"` +} diff --git a/models/training.go b/models/training.go index d66daa8..2426c7e 100644 --- a/models/training.go +++ b/models/training.go @@ -18,6 +18,7 @@ type Training struct { Title string `json:"title"` Description string `json:"description"` RequiredCorrect int `json:"requiredCorrect"` + ShowAnswers bool `json:"showAnswers"` Questions []Question `json:"questions"` } @@ -25,6 +26,7 @@ type CreateTrainingRequest struct { Title string `json:"title"` Description string `json:"description"` RequiredCorrect int `json:"requiredCorrect"` + ShowAnswers bool `json:"showAnswers"` Questions []Question `json:"questions"` } @@ -38,8 +40,9 @@ type UserTraining struct { type Submission = map[int]string type SubmissionResponse struct { - Passed bool `json:"passed"` - NumCorrect int `json:"numCorrect"` - NumIncorrect int `json:"numIncorrect"` - Grade int `json:"grade"` + Passed bool `json:"passed"` + NumCorrect int `json:"numCorrect"` + NumIncorrect int `json:"numIncorrect"` + Grade int `json:"grade"` + GradedResponse map[int]bool `json:"gradedResponse"` } diff --git a/routes/api/areas/areas.go b/routes/api/areas/areas.go index fd9de90..2a4ad01 100644 --- a/routes/api/areas/areas.go +++ b/routes/api/areas/areas.go @@ -129,6 +129,35 @@ func updateArea(c *gin.Context) { c.Status(204) } + +// DeleteArea godoc +// +// @Summary Deletes an area +// @Description Deletes an area +// @Tags areas +// @Accept json +// @Param id path int true "Area ID" +// @Success 204 +// @Failure 400 {object} models.ErrorResponse +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /areas/{id} [delete] +func deleteArea(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.Error(err) + return + } + + err = database.Helper.DeleteArea(id) + if err != nil { + c.Error(err) + return + } + + c.Status(201) +} + func Routes(route *gin.RouterGroup) { areas := route.Group("/areas") areas.GET("/", getAllAreas) @@ -136,4 +165,6 @@ func Routes(route *gin.RouterGroup) { areas.PUT("/:id", middleware.RequireGroup("eboard"), updateArea) areas.POST("/", middleware.RequireGroup("eboard"), createArea) + + areas.DELETE("/:id", middleware.RequireGroup("eboard"), deleteArea) } diff --git a/routes/api/me/me.go b/routes/api/me/me.go index f135fb0..407b440 100644 --- a/routes/api/me/me.go +++ b/routes/api/me/me.go @@ -6,13 +6,22 @@ import ( "github.com/gin-gonic/gin" ) +// bullshit type that matches csh.auth.UserInfo because swag can't resolve dependencies +type MeResponse struct { + Uuid string `json:"uuid"` + Email string `json:"email"` + Username string `json:"preferred_username"` + FullName string `json:"name"` + Groups []string `json:"groups"` +} + // GetAuthUser godoc // // @Summary Get authenticated user // @Description Returns information about the currently authenticated user // @Tags me // @Produce json -// @Success 200 {object} csh_auth.UserInfo +// @Success 200 {object} MeResponse // @Failure 500 {object} models.ErrorResponse // @Router /me [get] func getAuthUser(c *gin.Context) { diff --git a/routes/api/resources/resources.go b/routes/api/resources/resources.go new file mode 100644 index 0000000..f19c891 --- /dev/null +++ b/routes/api/resources/resources.go @@ -0,0 +1,270 @@ +package resources + +import ( + "database/sql" + "errors" + "makedotcsh/database" + "makedotcsh/middleware" + "makedotcsh/models" + "strconv" + + "github.com/gin-gonic/gin" +) + +type RemoveResourceRequest struct { + ID int `json:"id"` +} + +// GetAllResources godoc +// +// @Summary Get all resources +// @Description Returns all resources +// @Tags resources +// @Produce json +// @Success 200 {array} models.Resource +// @Failure 500 {object} models.ErrorResponse +// @Router /resources [get] +func getAllResources(c *gin.Context) { + rs, err := database.Helper.GetAllResources() + if err != nil { + c.Error(err) + return + } + + c.JSON(200, rs) +} + +// GetResource godoc +// +// @Summary Get resource +// @Description Returns a resource by ID +// @Tags resources +// @Produce json +// @Param id path int true "Resource ID" +// @Success 200 {object} models.Resource +// @Failure 404 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /resources/{id} [get] +func getResource(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + + r, err := database.Helper.GetResource(id) + + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + c.JSON(404, gin.H{"error": "resource not found"}) + return + } + + c.Error(err) + return + } + + c.JSON(200, r) +} + +// CreateResource godoc +// +// @Summary Create resource +// @Description Creates a new resource +// @Tags resources +// @Accept json +// @Param resource body models.Resource true "Resource" +// @Success 201 +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /resources [post] +func createResource(c *gin.Context) { + var req models.Resource + + err := c.ShouldBindBodyWithJSON(&req) + if err != nil { + c.JSON(400, gin.H{ + "error": err.Error(), + }) + return + } + + err = database.Helper.CreateResource(req) + if err != nil { + c.Error(err) + return + } + + c.Status(201) +} + +// UpdateResource godoc +// +// @Summary Update resource +// @Description Updates an existing resource +// @Tags resources +// @Accept json +// @Param id path int true "Resource ID" +// @Param resource body models.Resource true "Resource" +// @Success 201 +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /resources/{id} [put] +func updateResource(c *gin.Context) { + var req models.Resource + + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.JSON(400, gin.H{ + "error": err.Error(), + }) + return + } + + err = c.ShouldBindBodyWithJSON(&req) + if err != nil { + c.JSON(400, gin.H{ + "error": err.Error(), + }) + return + } + + err = database.Helper.UpdateResource(req, id) + if err != nil { + c.Error(err) + return + } + + c.Status(201) +} + +// DeleteResource godoc +// +// @Summary Delete resource +// @Description Deletes a resource by ID +// @Tags resources +// @Param id path int true "Resource ID" +// @Success 204 +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /resources/{id} [delete] +func deleteResource(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.JSON(400, gin.H{ + "error": err.Error(), + }) + return + } + + err = database.Helper.DeleteResource(id) + if err != nil { + c.Error(err) + return + } + + c.Status(204) +} + +// GetAreaResources godoc +// +// @Summary Get area resources +// @Description Returns all resources associated with an area +// @Tags resources +// @Produce json +// @Param id path int true "Area ID" +// @Success 200 {array} models.Resource +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /resources/area/{id} [get] +func getAreaResources(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.JSON(400, gin.H{ + "error": err.Error(), + }) + return + } + + resources, err := database.Helper.GetAreaResources(id) + if err != nil { + c.Error(err) + return + } + + c.JSON(200, resources) +} + +// AddResourceToArea godoc +// +// @Summary Add resources to area +// @Description Adds resources to an area +// @Tags resources +// @Accept json +// @Produce json +// @Param id path int true "Area ID" +// @Param request body []int true "Resource IDs" +// @Success 200 +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /resources/area/{id} [post] +func addResourceToArea(c *gin.Context) { + var req []int + id, err := strconv.Atoi(c.Param("id")) + err = c.ShouldBindBodyWithJSON(&req) + if err != nil { + c.JSON(400, gin.H{ + "error": err.Error(), + }) + return + } + + err = database.Helper.AddResourceToArea(id, req) + if err != nil { + c.Error(err) + return + } +} + +// RemoveResourceFromArea godoc +// +// @Summary Remove resource from area +// @Description Removes a resource from an area +// @Tags resources +// @Accept json +// @Param id path int true "Area ID" +// @Param request body resources.RemoveResourceRequest true "Resource removal request" +// @Success 201 +// @Failure 400 {object} models.ErrorResponse +// @Failure 500 {object} models.ErrorResponse +// @Router /resources/area/{id} [delete] +func removeResourceFromArea(c *gin.Context) { + var req RemoveResourceRequest + id, err := strconv.Atoi(c.Param("id")) + err = c.ShouldBindBodyWithJSON(&req) + if err != nil { + c.JSON(400, gin.H{ + "error": err.Error(), + }) + return + } + + err = database.Helper.RemoveResourceFromArea(id, req.ID) + if err != nil { + c.Error(err) + return + } + + c.Status(201) +} + +func Routes(route *gin.RouterGroup) { + resources := route.Group("/resources") + resources.GET("/", getAllResources) + resources.GET("/:id", getResource) + resources.GET("/area/:id", getAreaResources) + + resources.PUT("/:id", middleware.RequireGroup("eboard"), updateResource) + + resources.POST("/", middleware.RequireGroup("eboard"), createResource) + resources.POST("/area/:id", middleware.RequireGroup("eboard"), addResourceToArea) + + resources.DELETE("/area/:id", middleware.RequireGroup("eboard"), removeResourceFromArea) + resources.DELETE("/:id", middleware.RequireGroup("eboard"), deleteResource) +} diff --git a/routes/routes.go b/routes/routes.go index ba29fb1..0c2dd82 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -4,6 +4,7 @@ import ( "makedotcsh/routes/api/areas" "makedotcsh/routes/api/me" "makedotcsh/routes/api/members" + "makedotcsh/routes/api/resources" "makedotcsh/routes/api/trainings" "makedotcsh/routes/api/user" @@ -34,5 +35,6 @@ func SetRoutes(router *gin.Engine, auth csh_auth.Auth) { trainings.Routes(api) user.Routes(api) members.Routes(api) + resources.Routes(api) } diff --git a/utils/util.go b/utils/util.go index ba0a814..627f0bb 100644 --- a/utils/util.go +++ b/utils/util.go @@ -51,6 +51,11 @@ func GradeTraining(trainingId int, userUUID string, submission models.Submission return models.SubmissionResponse{}, errors.New("invalid submission") } + if len(answers) == 0 { + // no submission allowed + return models.SubmissionResponse{}, errors.New("invalid submission") + } + var correct, incorrect int graded := map[int]bool{} totalQuestions := len(answers) @@ -76,6 +81,12 @@ func GradeTraining(trainingId int, userUUID string, submission models.Submission Grade: int((float64(correct) / float64(totalQuestions)) * 100), } + if training.ShowAnswers { + res.GradedResponse = graded + } else { + res.GradedResponse = map[int]bool{} + } + if passed { SubmitTraining(userUUID, training) diff --git a/web/src/components/AddResourcePopup.vue b/web/src/components/AddResourcePopup.vue new file mode 100644 index 0000000..570d7fc --- /dev/null +++ b/web/src/components/AddResourcePopup.vue @@ -0,0 +1,111 @@ + + + + diff --git a/web/src/components/AddTrainingPopup.vue b/web/src/components/AddTrainingPopup.vue index dfb127a..373c8ee 100644 --- a/web/src/components/AddTrainingPopup.vue +++ b/web/src/components/AddTrainingPopup.vue @@ -93,7 +93,6 @@ async function save() {

{{ error }}

diff --git a/web/src/components/AppNavbar.vue b/web/src/components/AppNavbar.vue index 0aeae25..5e19320 100644 --- a/web/src/components/AppNavbar.vue +++ b/web/src/components/AppNavbar.vue @@ -29,7 +29,6 @@ function logout() {