api

package
v0.0.0-...-5607ea2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2026 License: AGPL-3.0 Imports: 35 Imported by: 0

Documentation

Index

Constants

View Source
const (
	OrchestrationBotUserID   = "bot-orchestration"
	OrchestrationBotUsername = "orchestration-bot"
)

OrchestrationBotUserID / OrchestrationBotUsername identify the forgejo "orchestration-bot" account in the local DB. The bot creates every course org and repo on the owner's behalf. The same constants are declared in package db (the canonical home, since it owns the stub row); keep them in sync if either ever changes.

View Source
const CLIMinVersionSettingKey = "cli_min_version"

CLIMinVersionSettingKey is the settings key for the minimum CLI version.

Variables

This section is empty.

Functions

func AdminAuditLogFacets

func AdminAuditLogFacets(gdb *gorm.DB) (actions, resources []string, err error)

AdminAuditLogFacets returns distinct action and resource values present in the audit log, used to populate filter dropdowns.

func AdminForgejoRequestFacets

func AdminForgejoRequestFacets(gdb *gorm.DB) (sources, methods []string, err error)

AdminForgejoRequestFacets returns distinct source and method values present in the Forgejo request log.

func AdminGetAuditLog

func AdminGetAuditLog(gdb *gorm.DB, id string) (db.AuditLog, error)

AdminGetAuditLog fetches a single audit log entry by ID.

func AdminGetCourse

func AdminGetCourse(gdb *gorm.DB, id string) (db.Course, error)

AdminGetCourse fetches a single course by ID (including archived courses via Unscoped, since the admin UI needs to show archived courses too).

func AdminGetForgejoRequest

func AdminGetForgejoRequest(gdb *gorm.DB, id string) (db.ForgejoRequest, error)

AdminGetForgejoRequest fetches a single Forgejo request by ID.

func AdminListStudents

func AdminListStudents(gdb *gorm.DB, courseID string) ([]db.Student, error)

AdminListStudents fetches students for a course with users preloaded.

func AdminListTutors

func AdminListTutors(gdb *gorm.DB, courseID string) ([]db.Tutor, error)

AdminListTutors fetches tutors for a course with users preloaded.

func CanManageCourse

func CanManageCourse(gdb *gorm.DB, c *echo.Context, courseID any) bool

CanManageCourse reports whether the caller may manage the course identified by courseID. Admins always may; staff may only for courses they own. Used by handlers (e.g. HTML fragments) that operate on a course by id and need a boolean guard rather than a query scope.

func CourseAccessScope

func CourseAccessScope(c *echo.Context) func(*gorm.DB) *gorm.DB

CourseAccessScope returns a GORM scope that filters courses based on the caller's role for *read* access. Admins see everything. Staff see joinable courses plus the courses they own. Everyone else (students and unauthenticated callers) sees only courses with allow_join = true.

func CourseManageScope

func CourseManageScope(c *echo.Context) func(*gorm.DB) *gorm.DB

CourseManageScope returns a GORM scope that restricts course queries to the courses the caller may *manage* (edit, delete, assign tutors/students, grade). Admins may manage every course; staff may manage only the courses they own; everyone else matches nothing. Applied to mutating course endpoints so a staff member acting on someone else's course gets a 404 rather than silently touching it.

func CreateAPIKey

func CreateAPIKey(gdb *gorm.DB, userID, name string) (db.ApiKey, string, error)

CreateAPIKey generates a new API key for the given user, stores the hash, and returns the model + the plaintext key (only visible at creation time). Keys expire after 1 year.

func DeleteAPIKey

func DeleteAPIKey(gdb *gorm.DB, userID string, keyID uint) error

DeleteAPIKey deletes an API key, scoped to the given user.

func EchoContext

func EchoContext(ctx context.Context) *echo.Context

EchoContext returns the echo context for the current huma request. Register injects it before the handler runs so handlers can build echo-context dependent GORM scopes and record audit entries. Returns nil when the handler is invoked outside of a registered operation (tests).

func FetchCLIVersions

func FetchCLIVersions() []string

FetchCLIVersions fetches the available hopper-cli release versions from PyPI and returns them sorted descending (newest first) with a "v" prefix. Returns nil if PyPI is unreachable or the response cannot be parsed.

func IsTutorAssigned

func IsTutorAssigned(gdb *gorm.DB, courseID, userID string) (bool, error)

IsTutorAssigned returns true if userID is a tutor of courseID.

func ListAPIKeys

func ListAPIKeys(gdb *gorm.DB, userID string) ([]db.ApiKey, error)

ListAPIKeys returns all API keys for the given user, newest first.

func ListExercisesForCourse

func ListExercisesForCourse(gdb *gorm.DB, courseID uint) ([]db.Exercise, error)

ListExercisesForCourse returns every exercise defined in a course, ordered by start date. Exported so tests and in-process callers can reuse the query without going through HTTP.

func LoadCLIMinVersion

func LoadCLIMinVersion(gdb *gorm.DB) string

LoadCLIMinVersion reads the cli_min_version setting from the database, returning the stored value (with a "v" prefix if missing) or an empty string when the setting does not exist.

func Register

func Register[I, O any](h *Huma, op huma.Operation, handler func(context.Context, *I) (*O, error), mw ...echo.MiddlewareFunc)

Register registers a huma operation with echo middleware applied as operation-level huma middleware, keeping route registration a one-liner:

api.Register(h, op, handler, a.Auth.RequireStaff())

The echo context of the current request is injected into the handler context and can be retrieved with EchoContext.

func ResolveUserID

func ResolveUserID(gdb *gorm.DB, identifier string) (string, error)

ResolveUserID resolves an arbitrary identifier to the canonical user PK (OIDC sub). Tries id, then preferred_username (case-insensitive), then student_id. Returns gorm.ErrRecordNotFound if nothing matches.

func RunTask

func RunTask(c *echo.Context, gdb *gorm.DB, name string) error

RunTask triggers the named task. Returns an error if the task isn't found or fails. Records an audit entry on success. Exported for use by the HTML fragment handlers.

func SearchUsersByNameOrEmail

func SearchUsersByNameOrEmail(gdb *gorm.DB, query string, limit int) ([]db.User, error)

SearchUsersByNameOrEmail returns up to limit users matching name or email. Used by the assign-tutor modal's user-search field.

func SettingAccessScope

func SettingAccessScope(c *echo.Context) func(*gorm.DB) *gorm.DB

SettingAccessScope returns a GORM scope that filters settings by the caller's access level. Unauthenticated callers see only "public" entries. Regular authenticated users see "public" + "authed". Admins see everything.

func UserReadScope

func UserReadScope(c *echo.Context) func(*gorm.DB) *gorm.DB

UserReadScope returns a GORM scope that restricts a users query to the rows the caller is allowed to *read*. Admins see everyone. Any other authenticated caller sees:

  • themselves;
  • (as course owner / staff) every student and tutor enrolled in a course they own;
  • (as a course tutor) every student assigned to a tutor slot they hold.

The ownership branches are harmless for callers who own no courses and hold no tutor slots - their subqueries simply match nothing - so no role branch is needed beyond the admin short-circuit. Applied to the read endpoints so a staff member or tutor gets a scoped view instead of the whole user table, and a 404 (not a 403) for anyone outside their reach.

The whole predicate is a single grouped OR so it AND-combines cleanly with any search/filter scopes the list handler layers on top.

Types

type AssignTutorBody

type AssignTutorBody struct {
	UserID      string `json:"user_id"`
	Description string `json:"description" required:"false"`
}

AssignTutorBody is the body for POST /courses/{id}/tutors.

type AuditLogFacets

type AuditLogFacets struct {
	Actions   []string `json:"actions"`
	Resources []string `json:"resources"`
}

AuditLogFacets is the response body of GET /audit-log/facets. Each field carries the distinct values currently present in the audit_logs table for the matching column, sorted ascending. Clients use it to populate filter dropdowns so we don't have to hardcode the list of actions in the UI every time a new audit call-site is added on the backend.

type AuditLogFacetsInput

type AuditLogFacetsInput struct{}

AuditLogFacetsInput has no request parameters.

type AuditLogFacetsOutput

type AuditLogFacetsOutput struct {
	Body AuditLogFacets
}

AuditLogFacetsOutput holds the response body for AuditLogFacets.

type AuditLogGetInput

type AuditLogGetInput struct {
	ID string `path:"id"`
}

AuditLogGetInput holds the request parameters for AuditLogGet.

type AuditLogGetOutput

type AuditLogGetOutput struct {
	Body db.AuditLog
}

AuditLogGetOutput holds the response body for AuditLogGet.

type AuditLogListInput

type AuditLogListInput struct {
	AuditLogListQuery
}

AuditLogListInput holds the request parameters for AuditLogList.

type AuditLogListOutput

type AuditLogListOutput struct {
	Body query.PaginatedResponse[db.AuditLog]
}

AuditLogListOutput holds the response body for AuditLogList.

type AuditLogListParams

type AuditLogListParams struct {
	Search     string
	Action     string
	Resource   string
	ActorID    string
	ResourceID string
	Since      string
	Until      string
	SortBy     string
	SortDir    string
	Page       int
	PageSize   int
}

AuditLogListParams holds the filter/sort/pagination inputs for the audit log list. Shared by the JSON API handler, the HTML fragment handler, and the page handler so all three paths agree on how params are read.

func ParseAuditLogListParams

func ParseAuditLogListParams(c *echo.Context) AuditLogListParams

ParseAuditLogListParams extracts AuditLogListParams from query string.

type AuditLogListQuery

type AuditLogListQuery struct {
	Search     string `query:"search" required:"false"`
	Action     string `query:"action" required:"false"`
	Resource   string `query:"resource" required:"false"`
	ActorID    string `query:"actor_id" required:"false"`
	ResourceID string `query:"resource_id" required:"false"`
	Since      string `query:"since" required:"false"`
	Until      string `query:"until" required:"false"`
	SortBy     string `query:"sort_by" required:"false"`
	SortDir    string `query:"sort_dir" required:"false"`
	Page       int    `query:"page" default:"1"`
	PageSize   int    `query:"page_size" default:"25"`
}

AuditLogListQuery holds the filter/sort/pagination inputs for GET /audit-log. The query/default tags drive huma's request binding; the values are mapped into AuditLogListParams for the shared AdminListAuditLog query path.

type AuditLogListResult

type AuditLogListResult struct {
	Items      []db.AuditLog
	Total      int64
	Page       int
	PageSize   int
	TotalPages int
	HasNext    bool
	HasPrev    bool
}

AuditLogListResult holds the paginated result of an audit log list query.

func AdminListAuditLog

func AdminListAuditLog(gdb *gorm.DB, p AuditLogListParams, loc *time.Location) (AuditLogListResult, error)

AdminListAuditLog runs the audit log list query with filters, sort, and pagination. Used by both the JSON API handler and the HTML fragment handler, one query path, two response formats. loc is the app timezone, used to interpret zone-less since/until filter values.

type CLIVersionInput

type CLIVersionInput struct{}

CLIVersionInput holds the request parameters for CLIVersion (none).

type CLIVersionOutput

type CLIVersionOutput struct {
	Body cliVersionResponse
}

CLIVersionOutput holds the response body for CLIVersion.

type ChartsCSVOutput

type ChartsCSVOutput struct {
	ContentType        string `header:"Content-Type"`
	ContentDisposition string `header:"Content-Disposition"`
	Body               []byte
}

ChartsCSVOutput holds the response for ChartsCSV. The headers are bound via huma's output header fields so the raw CSV body keeps its content type.

type ChartsInput

type ChartsInput struct {
	CourseID uint64 `query:"course_id" required:"false"`
}

ChartsInput holds the request parameters for Charts and ChartsCSV.

type ChartsOutput

type ChartsOutput struct {
	Body ChartsResponse
}

ChartsOutput holds the response body for Charts.

type ChartsResponse

type ChartsResponse struct {
	UsersByFaculty        []LabelCount       `json:"users_by_faculty"`
	RegistrationsOverTime []TimeSeriesPoint  `json:"registrations_over_time"`
	LoginsByDay           []TimeSeriesPoint  `json:"logins_by_day"`
	LoginsByHour          []LabelCount       `json:"logins_by_hour"`
	LoginsByWeekday       []LabelCount       `json:"logins_by_weekday"`
	AuditActivityOverTime []TimeSeries       `json:"audit_activity_over_time"`
	ForgejoOverTime       []ForgejoTimePoint `json:"forgejo_over_time"`
	CoursesPerSemester    []LabelCount       `json:"courses_per_semester"`
	StudentsPerTutor      []LabelCount       `json:"students_per_tutor"`
	GradeDistPerExercise  []GradeBucket      `json:"grade_distribution_per_exercise"`
	AvgScorePerExercise   []LabelCount       `json:"average_score_per_exercise"`
	GradedVsUngraded      []GradedUngraded   `json:"graded_vs_ungraded_per_exercise"`
	GradesByWeek          []TimeSeriesPoint  `json:"grades_by_week"`
	AttendancePerWeek     []TimeSeriesPoint  `json:"attendance_per_week"`
	PresentedVsAttended   []LabelCount       `json:"presented_vs_attended"`
	EnrollmentsByFaculty  []LabelCount       `json:"enrollments_by_faculty"`
}

ChartsResponse contains all chart datasets for the stats page.

type CourseBody

type CourseBody struct {
	Name         string `json:"name"`
	Semester     string `json:"semester"`
	DisplayName  string `json:"display_name" required:"false"`
	Website      string `json:"website" required:"false"`
	BuilderImage string `json:"builder_image" required:"false"`
	GraderImage  string `json:"grader_image" required:"false"`
	AllowPush    bool   `json:"allow_push" required:"false"`
	AllowJoin    bool   `json:"allow_join" required:"false"`
	RepoAccess   string `json:"repo_access" required:"false"`
	OwnerID      string `json:"owner_id" required:"false"`
}

CourseBody is the request body for creating and updating courses. The id and timestamp fields are server-managed and not part of the payload. owner_id is accepted on create (admins may set it) and ignored on update.

type CourseBrief

type CourseBrief struct {
	ID          uint   `json:"id"`
	Name        string `json:"name"`
	Semester    string `json:"semester"`
	DisplayName string `json:"display_name"`
}

CourseBrief is a minimal representation of a course for the admin UI.

func VisibleCourses

func VisibleCourses(gdb *gorm.DB, ownerID string, isAdmin bool) []CourseBrief

VisibleCourses returns the courses the caller can see, for filter dropdowns: all courses for admins, owned courses for staff.

type CourseListQuery

type CourseListQuery struct {
	Search    string `query:"search" required:"false"`
	AllowJoin string `query:"allow_join" required:"false"`
	AllowPush string `query:"allow_push" required:"false"`
	Archived  string `query:"archived" required:"false"`
	Name      string `query:"name" required:"false"`
	Display   string `query:"display_name" required:"false"`
	Semester  string `query:"semester" required:"false"`
	SortBy    string `query:"sort_by" required:"false"`
	SortDir   string `query:"sort_dir" required:"false"`
	Page      int    `query:"page" default:"1"`
	PageSize  int    `query:"page_size" default:"25"`
}

CourseListQuery holds the filter/sort/pagination inputs for GET /courses. The query/default tags drive huma's request binding; the values are mapped into CoursesListParams for the shared AdminListCourses query path. The boolean filters are strings because huma does not support pointer query parameters; boolParam converts them below.

type CourseStudentGradesInput

type CourseStudentGradesInput struct {
	CourseSlug string `path:"courseSlug"`
	Username   string `path:"username"`
}

CourseStudentGradesInput holds the request parameters for CourseStudentGrades. The course is addressed by its UID (Semester + "-" + Name, the Forgejo org name) and the student by their Forgejo username (users.preferred_username, the repo name), matching the <org>/<repo> layout of the course repositories.

type CourseStudentGradesOutput

type CourseStudentGradesOutput struct {
	Body []GradeItem
}

CourseStudentGradesOutput holds the response body for CourseStudentGrades.

type CourseStudentListQuery

type CourseStudentListQuery struct {
	SortBy   string `query:"sort_by" required:"false" sort:"id,matrikelnummer" default_sort:"id asc"`
	SortDir  string `query:"sort_dir" required:"false"`
	Page     int    `query:"page" default:"1"`
	PageSize int    `query:"page_size" default:"25"`
}

CourseStudentListQuery holds the pagination inputs for the course students list endpoint. The query/default tags drive huma's request binding; the sort/default_sort tags are ignored by huma and consumed by query.Scopes.

type CourseTutorListQuery

type CourseTutorListQuery struct {
	SortBy   string `query:"sort_by" required:"false" sort:"id,created_at" default_sort:"id asc"`
	SortDir  string `query:"sort_dir" required:"false"`
	Page     int    `query:"page" default:"1"`
	PageSize int    `query:"page_size" default:"25"`
}

CourseTutorListQuery holds the pagination inputs for the course tutors list endpoint. The query/default tags drive huma's request binding; the sort/default_sort tags are ignored by huma and consumed by query.Scopes.

type CoursesCreateInput

type CoursesCreateInput struct {
	Body CourseBody
}

type CoursesCreateOutput

type CoursesCreateOutput struct {
	Body db.Course
}

CoursesCreateOutput holds the response body for CoursesCreate.

type CoursesDeleteInput

type CoursesDeleteInput struct {
	ID string `path:"id"`
}

type CoursesDeleteOutput

type CoursesDeleteOutput struct {
	Status int
}

CoursesDeleteOutput holds the response status for CoursesDelete.

type CoursesGetInput

type CoursesGetInput struct {
	ID string `path:"id"`
}

CoursesGetInput holds the request parameters for CoursesGet.

type CoursesGetOutput

type CoursesGetOutput struct {
	Body db.Course
}

CoursesGetOutput holds the response body for CoursesGet.

type CoursesHardDeleteInput

type CoursesHardDeleteInput struct {
	ID string `path:"id"`
}

type CoursesHardDeleteOutput

type CoursesHardDeleteOutput struct {
	Status int
}

CoursesHardDeleteOutput holds the response status for CoursesHardDelete.

type CoursesJoinInput

type CoursesJoinInput struct {
	ID string `path:"id"`
}

CoursesJoinInput holds the request parameters for CoursesJoin.

type CoursesJoinOutput

type CoursesJoinOutput struct {
	Body db.Student
}

CoursesJoinOutput holds the response body for CoursesJoin.

type CoursesListInput

type CoursesListInput struct {
	CourseListQuery
}

CoursesListInput holds the request parameters for CoursesList.

type CoursesListOutput

type CoursesListOutput struct {
	Body query.PaginatedResponse[db.Course]
}

CoursesListOutput holds the response body for CoursesList.

type CoursesListParams

type CoursesListParams struct {
	Search    string
	AllowJoin *bool
	AllowPush *bool
	Archived  *bool
	Name      string
	Display   string
	Semester  string
	SortBy    string
	SortDir   string
	Page      int
	PageSize  int
}

CoursesListParams holds the filter/sort/pagination inputs for the admin courses list. Shared by the JSON API handler, the HTML fragment handler, and the page handler so all three paths agree on how params are read.

func ParseCoursesListParams

func ParseCoursesListParams(c *echo.Context) CoursesListParams

ParseCoursesListParams extracts CoursesListParams from query string. Shared by the JSON API handler, the HTML fragment handler, and the page handler so all three paths agree on how params are read.

type CoursesListResult

type CoursesListResult struct {
	Items      []db.Course
	Total      int64
	Page       int
	PageSize   int
	TotalPages int
	HasNext    bool
	HasPrev    bool
}

CoursesListResult holds the paginated result of an admin courses list query.

func AdminListCourses

func AdminListCourses(gdb *gorm.DB, p CoursesListParams) (CoursesListResult, error)

AdminListCourses runs the courses list query with filters, sort, and pagination. Used by both the JSON API handler and the HTML fragment handler, one query path, two response formats. Always returns both active and archived courses (use Archived to filter).

type CoursesRestoreInput

type CoursesRestoreInput struct {
	ID string `path:"id"`
}

type CoursesRestoreOutput

type CoursesRestoreOutput struct {
	Body db.Course
}

CoursesRestoreOutput holds the response body for CoursesRestore.

type CoursesStudentsListInput

type CoursesStudentsListInput struct {
	ID string `path:"id"`
	CourseStudentListQuery
}

CoursesStudentsListInput holds the request parameters for CoursesStudentsList.

type CoursesStudentsListOutput

type CoursesStudentsListOutput struct {
	Body query.PaginatedResponse[StudentOut]
}

CoursesStudentsListOutput holds the response body for CoursesStudentsList.

type CoursesStudentsMineInput

type CoursesStudentsMineInput struct {
	ID string `path:"id"`
}

CoursesStudentsMineInput holds the request parameters for CoursesStudentsMine.

type CoursesStudentsMineOutput

type CoursesStudentsMineOutput struct {
	Body []StudentOut
}

CoursesStudentsMineOutput holds the response body for CoursesStudentsMine.

type CoursesStudentsUnassignInput

type CoursesStudentsUnassignInput struct {
	ID     string `path:"id"`
	UserID string `path:"userID"`
}

CoursesStudentsUnassignInput holds the request parameters for CoursesStudentsUnassign.

type CoursesStudentsUnassignOutput

type CoursesStudentsUnassignOutput struct {
	Status int
}

CoursesStudentsUnassignOutput holds the response status for CoursesStudentsUnassign.

type CoursesTutorsAssignInput

type CoursesTutorsAssignInput struct {
	ID   string `path:"id"`
	Body AssignTutorBody
}

CoursesTutorsAssignInput holds the request parameters for CoursesTutorsAssign.

type CoursesTutorsAssignOutput

type CoursesTutorsAssignOutput struct {
	Body TutorOut
}

CoursesTutorsAssignOutput holds the response body for CoursesTutorsAssign.

type CoursesTutorsListInput

type CoursesTutorsListInput struct {
	ID string `path:"id"`
	CourseTutorListQuery
}

CoursesTutorsListInput holds the request parameters for CoursesTutorsList.

type CoursesTutorsListOutput

type CoursesTutorsListOutput struct {
	Body query.PaginatedResponse[TutorOut]
}

CoursesTutorsListOutput holds the response body for CoursesTutorsList.

type CoursesTutorsUnassignInput

type CoursesTutorsUnassignInput struct {
	ID     string `path:"id"`
	UserID string `path:"userID"`
}

CoursesTutorsUnassignInput holds the request parameters for CoursesTutorsUnassign.

type CoursesTutorsUnassignOutput

type CoursesTutorsUnassignOutput struct {
	Status int
}

CoursesTutorsUnassignOutput holds the response status for CoursesTutorsUnassign.

type CoursesUpdateInput

type CoursesUpdateInput struct {
	ID   string `path:"id"`
	Body CourseBody
}

type CoursesUpdateOutput

type CoursesUpdateOutput struct {
	Body db.Course
}

CoursesUpdateOutput holds the response body for CoursesUpdate.

type CreateUserBody

type CreateUserBody struct {
	Email             string  `json:"email" required:"false"`
	Name              string  `json:"name"`
	Subject           string  `json:"subject" required:"false"`
	PreferredUsername string  `json:"preferred_username" required:"false"`
	StudentID         string  `json:"student_id" required:"false"`
	AccountType       string  `json:"account_type" required:"false"`
	Faculty           string  `json:"faculty" required:"false"`
	EnrolledSubject   string  `json:"enrolled_subject" required:"false"`
	ExtraClaims       string  `json:"extra_claims" required:"false"`
	Role              db.Role `json:"role" required:"false"`
}

CreateUserBody is the request body for creating a user. The id and the timestamp fields are server-managed and not part of the payload.

type CronJobResponse

type CronJobResponse struct {
	ID                 string      `json:"id"`
	Name               string      `json:"name"`
	Tags               []string    `json:"tags"`
	IsRunning          bool        `json:"is_running"`
	LastRunStartedAt   *time.Time  `json:"last_run_started_at,omitempty"`
	LastRunCompletedAt *time.Time  `json:"last_run_completed_at,omitempty"`
	NextRun            *time.Time  `json:"next_run,omitempty"`
	NextRuns           []time.Time `json:"next_runs,omitempty"`
	Schedule           any         `json:"schedule,omitempty"` // optional, see below
}

func ListCronJobs

func ListCronJobs(scheduler gocron.Scheduler) []CronJobResponse

ListCronJobs is the exported version of listCronJobs, for use by the HTML fragment handlers (which have access to *app.App but not *api.Handler).

func RunCronJob

func RunCronJob(c *echo.Context, scheduler gocron.Scheduler, gdb *gorm.DB, id string) (CronJobResponse, error)

RunCronJob triggers the given job by ID string. Returns the refreshed job response and an error if the job couldn't be found or run. Records an audit entry on success.

type CronListInput

type CronListInput struct{}

CronListInput holds the request parameters for CronList (none).

type CronListOutput

type CronListOutput struct {
	Body []CronJobResponse
}

CronListOutput holds the response body for CronList.

type CronRunInput

type CronRunInput struct {
	ID string `path:"id"`
}

CronRunInput holds the request parameters for CronRun.

type CronRunOutput

type CronRunOutput struct {
	Body CronJobResponse
}

CronRunOutput holds the response body for CronRun.

type ExerciseAllRow

type ExerciseAllRow struct {
	ID            uint      `json:"id"`
	Name          string    `json:"name"`
	CourseID      uint      `json:"course_id"`
	CourseName    string    `json:"course_name"`
	Semester      string    `json:"semester"`
	Start         time.Time `json:"start"`
	End           time.Time `json:"end"`
	Points        *float64  `json:"points"`
	UngradedCount int64     `json:"ungraded_count"`
}

ExerciseAllRow is one exercise with its course and the number of submissions that still need grading (points IS NULL).

type ExerciseOut

type ExerciseOut struct {
	ID       uint      `json:"id"`
	Name     string    `json:"name"`
	Start    time.Time `json:"start"`
	End      time.Time `json:"end"`
	Points   *float64  `json:"points"`
	CourseID uint      `json:"course_id"`
}

ExerciseOut is the public shape of an exercise returned by the build/ grading lookup endpoint. It mirrors the subset of db.Exercise that an external consumer (the build/grading service) needs to decide whether a build is eligible and to pick a pre-deadline commit. It is a separate type from db.Exercise so we never accidentally leak internal fields.

type ExercisesAllListParams

type ExercisesAllListParams struct {
	Search   string
	CourseID string
	SortBy   string
	SortDir  string
	Page     int
	PageSize int
}

ExercisesAllListParams holds the filter/sort/pagination inputs for the cross-course exercises list. Shared by the page handler and the HTML fragment handler.

func ParseExercisesAllListParams

func ParseExercisesAllListParams(c *echo.Context) ExercisesAllListParams

ParseExercisesAllListParams extracts ExercisesAllListParams from the query string.

type ExercisesAllListResult

type ExercisesAllListResult struct {
	Items      []ExerciseAllRow
	Total      int64
	Page       int
	PageSize   int
	TotalPages int
	HasNext    bool
	HasPrev    bool
}

ExercisesAllListResult holds the paginated result of the cross-course exercises list.

func AdminListExercisesAll

func AdminListExercisesAll(gdb *gorm.DB, p ExercisesAllListParams) (ExercisesAllListResult, error)

AdminListExercisesAll lists exercises across all courses.

func ScopedListExercisesAll

func ScopedListExercisesAll(gdb *gorm.DB, p ExercisesAllListParams, ownerID string) (ExercisesAllListResult, error)

ScopedListExercisesAll lists exercises restricted to courses owned by the given staff member.

type ExercisesListInput

type ExercisesListInput struct {
	ID string `path:"id"`
}

ExercisesListInput holds the request parameters for ExercisesList.

type ExercisesListOutput

type ExercisesListOutput struct {
	Body []ExerciseOut
}

ExercisesListOutput holds the response body for ExercisesList.

type ForgejoRequestFacets

type ForgejoRequestFacets struct {
	Sources []string `json:"sources"`
	Methods []string `json:"methods"`
}

ForgejoRequestFacets is the response body of GET /forgejo-requests/facets. Each field carries the distinct values currently present in the forgejo_requests table, sorted ascending; the UI uses them to populate filter dropdowns.

type ForgejoRequestListParams

type ForgejoRequestListParams struct {
	Search    string
	Source    string
	Method    string
	MinStatus string
	MaxStatus string
	Since     string
	Until     string
	SortBy    string
	SortDir   string
	Page      int
	PageSize  int
}

ForgejoRequestListParams holds the filter/sort/pagination inputs for the Forgejo request log list. Shared by the JSON API handler, the HTML fragment handler, and the page handler.

func ParseForgejoRequestListParams

func ParseForgejoRequestListParams(c *echo.Context) ForgejoRequestListParams

ParseForgejoRequestListParams extracts ForgejoRequestListParams from query string.

type ForgejoRequestListQuery

type ForgejoRequestListQuery struct {
	Search    string `query:"search" required:"false"`
	Source    string `query:"source" required:"false"`
	Method    string `query:"method" required:"false"`
	MinStatus string `query:"min_status" required:"false"`
	MaxStatus string `query:"max_status" required:"false"`
	Since     string `query:"since" required:"false"`
	Until     string `query:"until" required:"false"`
	SortBy    string `query:"sort_by" required:"false"`
	SortDir   string `query:"sort_dir" required:"false"`
	Page      int    `query:"page" default:"1"`
	PageSize  int    `query:"page_size" default:"25"`
}

ForgejoRequestListQuery holds the filter/sort/pagination inputs for GET /forgejo-requests. The query/default tags drive huma's request binding; the values are mapped into ForgejoRequestListParams for the shared AdminListForgejoRequests query path.

type ForgejoRequestListResult

type ForgejoRequestListResult struct {
	Items      []db.ForgejoRequest
	Total      int64
	Page       int
	PageSize   int
	TotalPages int
	HasNext    bool
	HasPrev    bool
}

ForgejoRequestListResult holds the paginated result of a Forgejo request list query.

func AdminListForgejoRequests

func AdminListForgejoRequests(gdb *gorm.DB, p ForgejoRequestListParams, loc *time.Location) (ForgejoRequestListResult, error)

AdminListForgejoRequests runs the Forgejo request list query with filters, sort, and pagination. Used by both the JSON API handler and the HTML fragment handler.

type ForgejoRequestsFacetsInput

type ForgejoRequestsFacetsInput struct{}

ForgejoRequestsFacetsInput has no request parameters.

type ForgejoRequestsFacetsOutput

type ForgejoRequestsFacetsOutput struct {
	Body ForgejoRequestFacets
}

ForgejoRequestsFacetsOutput holds the response body for ForgejoRequestsFacets.

type ForgejoRequestsListInput

type ForgejoRequestsListInput struct {
	ForgejoRequestListQuery
}

ForgejoRequestsListInput holds the request parameters for ForgejoRequestsList.

type ForgejoRequestsListOutput

type ForgejoRequestsListOutput struct {
	Body query.PaginatedResponse[db.ForgejoRequest]
}

ForgejoRequestsListOutput holds the response body for ForgejoRequestsList.

type ForgejoTimePoint

type ForgejoTimePoint struct {
	Date       string `json:"date"`
	Success    int64  `json:"success"`
	Error      int64  `json:"error"`
	AvgLatency int64  `json:"avg_latency_ms"`
}

ForgejoTimePoint combines request counts and latency for a single day.

type GradeBucket

type GradeBucket struct {
	Exercise string `json:"exercise"`
	Bucket   string `json:"bucket"`
	Count    int64  `json:"count"`
}

GradeBucket is one bucket of the grade-distribution histogram for an exercise.

type GradeItem

type GradeItem struct {
	ExerciseName string     `json:"exercise_name"`
	CourseName   string     `json:"course_name"`
	CourseSlug   string     `json:"course_slug"`
	CourseID     uint       `json:"course_id"`
	Points       *float64   `json:"points"`
	MaxPoints    *float64   `json:"max_points"`
	GradedAt     *time.Time `json:"graded_at"`
	Start        time.Time  `json:"start"`
	End          time.Time  `json:"end"`
}

GradeItem is one exercise entry for the /users/me/grades overview. Unlike RecentGradeItem it also covers exercises that have not been graded yet (no student_exercises row, or one with NULL points): for those points and graded_at are null. Start/End are the exercise's time window so the frontend can render countdowns to the deadline.

type GradedUngraded

type GradedUngraded struct {
	Exercise string `json:"exercise"`
	Graded   int64  `json:"graded"`
	Ungraded int64  `json:"ungraded"`
}

GradedUngraded pairs graded and ungraded counts for a single exercise.

type HTTPError

type HTTPError struct {
	Message string `json:"message"`
}

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

func NewHandler

func NewHandler(a *app.App, hu *Huma) *Handler

func (*Handler) AttendanceHandler

func (h *Handler) AttendanceHandler(c *echo.Context) error

AttendanceHandler handles POST /api/attendance. A tutor scans a student's QR code (which encodes the student's user ID); this logs a TutorialParticipation record for today. The caller must be a tutor of the given course.

Students assigned to this tutor can check in once per day. Students enrolled in the course but assigned to a different tutor (or no tutor) are "guests" and can attend once per course per week. Students not enrolled in the course at all are rejected.

func (*Handler) AuditLogFacets

func (h *Handler) AuditLogFacets(ctx context.Context, input *AuditLogFacetsInput) (*AuditLogFacetsOutput, error)

AuditLogFacets returns the distinct action and resource values currently present in the audit log, used to populate filter dropdowns (admin only).

func (*Handler) AuditLogGet

func (h *Handler) AuditLogGet(ctx context.Context, input *AuditLogGetInput) (*AuditLogGetOutput, error)

AuditLogGet returns a single audit log entry by ID (admin only).

func (*Handler) AuditLogList

func (h *Handler) AuditLogList(ctx context.Context, input *AuditLogListInput) (*AuditLogListOutput, error)

AuditLogList returns a paginated list of audit log entries, newest first, filterable by action, resource, actor and time range (admin only).

func (*Handler) CLIVersion

func (h *Handler) CLIVersion(ctx context.Context, input *CLIVersionInput) (*CLIVersionOutput, error)

CLIVersion returns the minimum required hopper-cli version from the "cli_min_version" setting. Public endpoint; no authentication required.

func (*Handler) Charts

func (h *Handler) Charts(ctx context.Context, input *ChartsInput) (*ChartsOutput, error)

Charts returns all chart datasets (overview, per-course, cross-dimensional) as JSON. When course_id is present and non-zero, per-course charts filter to that course; overview and cross-dimensional charts are always unfiltered.

func (*Handler) ChartsCSV

func (h *Handler) ChartsCSV(ctx context.Context, input *ChartsInput) (*ChartsCSVOutput, error)

ChartsCSV returns a CSV file containing all chart datasets, one section per chart separated by a blank line. When course_id is present and non-zero, per-course charts filter to that course.

func (*Handler) CourseStudentGrades

func (h *Handler) CourseStudentGrades(ctx context.Context, input *CourseStudentGradesInput) (*CourseStudentGradesOutput, error)

CourseStudentGrades returns all exercises of one course for one student, including ungraded ones (points and graded_at are null for those), with the same item shape as UsersMeGrades. The course is resolved by its UID and the student by their Forgejo username. Access is limited to the student themselves and course staff (course owner, admins, tutors of that course); anyone else signed in gets 403, unknown courses or students 404.

func (*Handler) CoursesCreate

func (h *Handler) CoursesCreate(ctx context.Context, input *CoursesCreateInput) (*CoursesCreateOutput, error)

CoursesCreate creates a new course (staff only).

func (*Handler) CoursesDelete

func (h *Handler) CoursesDelete(ctx context.Context, input *CoursesDeleteInput) (*CoursesDeleteOutput, error)

CoursesDelete deletes a course by ID (soft delete, staff only).

func (*Handler) CoursesGet

func (h *Handler) CoursesGet(ctx context.Context, input *CoursesGetInput) (*CoursesGetOutput, error)

CoursesGet returns a single course by ID. Non-admin users see only joinable courses.

func (*Handler) CoursesHardDelete

func (h *Handler) CoursesHardDelete(ctx context.Context, input *CoursesHardDeleteInput) (*CoursesHardDeleteOutput, error)

CoursesHardDelete permanently removes an archived course and all of its data from the database. Only courses that are already soft-deleted (archived) can be hard-deleted (admin only).

func (*Handler) CoursesJoin

func (h *Handler) CoursesJoin(ctx context.Context, input *CoursesJoinInput) (*CoursesJoinOutput, error)

CoursesJoin enrolls the authenticated user as a student in the given course. The course must have allow_join enabled. When Forgejo is configured, the user is required to have an account on the Forgejo instance (the local part of their email is used as the username); otherwise the request is rejected with 409.

func (*Handler) CoursesList

func (h *Handler) CoursesList(ctx context.Context, input *CoursesListInput) (*CoursesListOutput, error)

CoursesList returns a paginated list of courses. Non-admin users see only joinable courses.

func (*Handler) CoursesRestore

func (h *Handler) CoursesRestore(ctx context.Context, input *CoursesRestoreInput) (*CoursesRestoreOutput, error)

CoursesRestore restores a soft-deleted (archived) course by clearing its deleted_at (staff only).

func (*Handler) CoursesStudentsList

func (h *Handler) CoursesStudentsList(ctx context.Context, input *CoursesStudentsListInput) (*CoursesStudentsListOutput, error)

CoursesStudentsList returns a paginated list of students enrolled in a specific course. Only callers who may manage the course get access.

func (*Handler) CoursesStudentsMine

func (h *Handler) CoursesStudentsMine(ctx context.Context, input *CoursesStudentsMineInput) (*CoursesStudentsMineOutput, error)

CoursesStudentsMine returns the students assigned to the authenticated tutor in the given course. Admins and the course owner see all students; tutors see only those assigned to their tutor slot. Each student includes a trimmed user profile (id, name, preferred_username; the Forgejo repo name).

func (*Handler) CoursesStudentsUnassign

func (h *Handler) CoursesStudentsUnassign(ctx context.Context, input *CoursesStudentsUnassignInput) (*CoursesStudentsUnassignOutput, error)

CoursesStudentsUnassign removes a user's enrollment from the given course (staff only).

func (*Handler) CoursesTutorsAssign

func (h *Handler) CoursesTutorsAssign(ctx context.Context, input *CoursesTutorsAssignInput) (*CoursesTutorsAssignOutput, error)

CoursesTutorsAssign assigns an existing user as a tutor of the given course (staff only).

func (*Handler) CoursesTutorsList

func (h *Handler) CoursesTutorsList(ctx context.Context, input *CoursesTutorsListInput) (*CoursesTutorsListOutput, error)

CoursesTutorsList returns a paginated list of tutors assigned to a specific course. Only callers who may manage the course get access.

func (*Handler) CoursesTutorsUnassign

func (h *Handler) CoursesTutorsUnassign(ctx context.Context, input *CoursesTutorsUnassignInput) (*CoursesTutorsUnassignOutput, error)

CoursesTutorsUnassign removes a user as a tutor of the given course (staff only).

func (*Handler) CoursesUpdate

func (h *Handler) CoursesUpdate(ctx context.Context, input *CoursesUpdateInput) (*CoursesUpdateOutput, error)

CoursesUpdate updates an existing course by ID. Staff may only update the courses they own.

func (*Handler) CronList

func (h *Handler) CronList(ctx context.Context, input *CronListInput) (*CronListOutput, error)

CronList returns a list of all jobs regularly run by the orchestration app.

func (*Handler) CronRun

func (h *Handler) CronRun(ctx context.Context, input *CronRunInput) (*CronRunOutput, error)

CronRun triggers the given job to run immediately without waiting for its next scheduled execution.

func (*Handler) ExercisesList

func (h *Handler) ExercisesList(ctx context.Context, input *ExercisesListInput) (*ExercisesListOutput, error)

ExercisesList returns the exercises defined in a course, ordered by start date. Used by the build/grading service to resolve exercise deadlines without duplicating the exercise table.

func (*Handler) ForgejoRequestsFacets

func (h *Handler) ForgejoRequestsFacets(ctx context.Context, input *ForgejoRequestsFacetsInput) (*ForgejoRequestsFacetsOutput, error)

ForgejoRequestsFacets returns the distinct source and method values currently present in the Forgejo request log, used to populate filter dropdowns (admin only).

func (*Handler) ForgejoRequestsList

func (h *Handler) ForgejoRequestsList(ctx context.Context, input *ForgejoRequestsListInput) (*ForgejoRequestsListOutput, error)

ForgejoRequestsList returns a paginated list of every outgoing Forgejo HTTP request the orchestrator has made (most recent first), with full request and response envelopes (admin only).

func (*Handler) GitPostRecieveHandler

func (h *Handler) GitPostRecieveHandler(c *echo.Context) error

func (*Handler) GitPreRecieveHandler

func (h *Handler) GitPreRecieveHandler(c *echo.Context) error

func (*Handler) SettingsCreate

func (h *Handler) SettingsCreate(ctx context.Context, input *SettingsCreateInput) (*SettingsCreateOutput, error)

SettingsCreate creates a new setting (admin only).

func (*Handler) SettingsDelete

func (h *Handler) SettingsDelete(ctx context.Context, input *SettingsDeleteInput) (*SettingsDeleteOutput, error)

SettingsDelete deletes a setting by key (admin only).

func (*Handler) SettingsGet

func (h *Handler) SettingsGet(ctx context.Context, input *SettingsGetInput) (*SettingsGetOutput, error)

SettingsGet returns a single setting by key if visible to the caller.

func (*Handler) SettingsList

func (h *Handler) SettingsList(ctx context.Context, input *SettingsListInput) (*SettingsListOutput, error)

SettingsList returns a paginated list of settings visible to the caller.

func (*Handler) SettingsUpdate

func (h *Handler) SettingsUpdate(ctx context.Context, input *SettingsUpdateInput) (*SettingsUpdateOutput, error)

SettingsUpdate updates an existing setting by key (admin only).

func (*Handler) Stats

func (h *Handler) Stats(ctx context.Context, input *StatsInput) (*StatsOutput, error)

Stats returns the aggregate dashboard statistics (admin only).

func (*Handler) TaskGet

func (h *Handler) TaskGet(ctx context.Context, input *TaskGetInput) (*TaskGetOutput, error)

TaskGet returns details for a single registered task.

func (*Handler) TaskList

func (h *Handler) TaskList(ctx context.Context, input *TaskListInput) (*TaskListOutput, error)

TaskList returns a list of all registered tasks that can be run manually.

func (*Handler) TaskRun

func (h *Handler) TaskRun(ctx context.Context, input *TaskRunInput) (*TaskRunOutput, error)

TaskRun triggers a registered task to run immediately.

func (*Handler) UsersAdminList

func (h *Handler) UsersAdminList(ctx context.Context, input *UsersAdminListInput) (*UsersAdminListOutput, error)

UsersAdminList returns a paginated list of users with their session count, last login, recent sessions, and course relationships (admin only).

func (*Handler) UsersCreate

func (h *Handler) UsersCreate(ctx context.Context, input *UsersCreateInput) (*UsersCreateOutput, error)

UsersCreate creates a new user (admin only).

func (*Handler) UsersDelete

func (h *Handler) UsersDelete(ctx context.Context, input *UsersDeleteInput) (*UsersDeleteOutput, error)

UsersDelete deletes a user by ID (admin only).

func (*Handler) UsersDeleteSessions

func (h *Handler) UsersDeleteSessions(ctx context.Context, input *UsersDeleteSessionsInput) (*UsersDeleteSessionsOutput, error)

UsersDeleteSessions deletes all active OIDC sessions for the specified user, forcing them to re-authenticate on their next request (admin only).

func (*Handler) UsersGet

func (h *Handler) UsersGet(ctx context.Context, input *UsersGetInput) (*UsersGetOutput, error)

UsersGet returns a single user by ID. Non-admin callers may only read users they are related to (staff: members of their courses; tutors: their assigned students; anyone: themselves); others get 404.

func (*Handler) UsersList

func (h *Handler) UsersList(ctx context.Context, input *UsersListInput) (*UsersListOutput, error)

UsersList returns a paginated list of users matching the requested filters. Non-admin callers only see users they are related to: staff see the students and tutors of the courses they own, tutors see their assigned students, and everyone always sees themselves.

func (*Handler) UsersMe

func (h *Handler) UsersMe(ctx context.Context, input *UsersMeInput) (*UsersMeOutput, error)

UsersMe returns the authenticated user for the currently signed-in user.

func (*Handler) UsersMeCourses

func (h *Handler) UsersMeCourses(ctx context.Context, input *UsersMeCoursesInput) (*UsersMeCoursesOutput, error)

UsersMeCourses returns the courses in which the authenticated user is a student or a tutor. Each entry carries a `role` field ("student" or "tutor") so the caller can pick the right action. Archived (soft-deleted) courses are excluded.

func (*Handler) UsersMeGrades

func (h *Handler) UsersMeGrades(ctx context.Context, input *UsersMeGradesInput) (*UsersMeGradesOutput, error)

UsersMeGrades returns all exercises of the courses the authenticated user is enrolled in as a student, including ungraded ones (points and graded_at are null for those). Items are ordered by course name, then by exercise start, end, and name.

func (*Handler) UsersMeRecentGrades

func (h *Handler) UsersMeRecentGrades(ctx context.Context, input *UsersMeRecentGradesInput) (*UsersMeRecentGradesOutput, error)

UsersMeRecentGrades returns the 5 most recently graded exercises for the authenticated user.

func (*Handler) UsersUpdate

func (h *Handler) UsersUpdate(ctx context.Context, input *UsersUpdateInput) (*UsersUpdateOutput, error)

UsersUpdate updates an existing user by ID (admin only).

type Huma

type Huma struct {
	API huma.API
	// contains filtered or unexported fields
}

Huma holds the huma API mounted on the shared echo instance.

func NewHuma

func NewHuma(e *echo.Echo, meta config.APIConfig) *Huma

NewHuma builds the huma API on top of the given echo instance. The API metadata comes from the resolved config (API_HOST, API_TERMS, API_SUPPORT_* env vars, read once at startup). Huma serves the generated spec itself at /api/openapi.json and /api/openapi.yaml and the docs UI at /api/docs. The schema endpoint is disabled because the spec embeds all schemas.

func (*Huma) UseAuth

func (h *Huma) UseAuth(mw ...echo.MiddlewareFunc) huma.Middlewares

UseAuth converts echo middleware (a.Auth.Require, RequireStaff, ...) into huma middleware for one operation. The middleware runs against the unwrapped echo context; if it short-circuits with an error, the error is rendered by echo's existing error handler unchanged and the huma handler is never invoked.

type LabelCount

type LabelCount struct {
	Label string `json:"label"`
	Count int64  `json:"count"`
}

LabelCount is a generic label/count pair for categorical chart data.

type MyCourseItem

type MyCourseItem struct {
	db.Course
	Role string `json:"role"` // "student" or "tutor"
}

MyCourseItem pairs a full course with the role the current user has in it (student or tutor). Used by /users/me/courses so the dashboard template can render the right action per role.

type RecentGradeItem

type RecentGradeItem struct {
	ExerciseName string    `json:"exercise_name"`
	CourseName   string    `json:"course_name"`
	CourseSlug   string    `json:"course_slug"`
	CourseID     uint      `json:"course_id"`
	Points       *float64  `json:"points"`
	MaxPoints    *float64  `json:"max_points"`
	GradedAt     time.Time `json:"graded_at"`
}

RecentGradeItem is a grade record for the dashboard "Recently Graded" list.

type SemesterCount

type SemesterCount struct {
	Semester string `json:"semester"`
	Count    int64  `json:"count"`
}

SemesterCount pairs a semester identifier with the number of courses in it.

type SessionBrief

type SessionBrief struct {
	ID        string    `json:"id"`
	CreatedAt time.Time `json:"created_at"`
	ExpiresAt time.Time `json:"expires_at"`
}

SessionBrief is a minimal representation of an OIDC session for the admin UI.

type SettingBody

type SettingBody struct {
	Key    string `json:"key"`
	Value  string `json:"value"`
	Type   string `json:"type" required:"false"`
	Access string `json:"access" required:"false"`
}

SettingBody is the request body for creating and updating settings. Timestamps are server-managed and not part of the payload.

type SettingListQuery

type SettingListQuery struct {
	Key      string `query:"key" required:"false" filter:"key"`
	Type     string `query:"type" required:"false" filter:"type"`
	Access   string `query:"access" required:"false" filter:"access"`
	Search   string `query:"search" required:"false" search:"key,value"`
	SortBy   string `query:"sort_by" required:"false" sort:"key,access,created_at,updated_at"`
	SortDir  string `query:"sort_dir" required:"false"`
	Page     int    `query:"page" default:"1"`
	PageSize int    `query:"page_size" default:"25"`
}

SettingListQuery holds the filter/sort/pagination inputs for GET /settings. The query/default tags drive huma's request binding; the filter/search/sort tags are ignored by huma and consumed by query.Scopes.

type SettingsCreateInput

type SettingsCreateInput struct {
	Body SettingBody
}

SettingsCreateInput holds the request parameters for SettingsCreate.

type SettingsCreateOutput

type SettingsCreateOutput struct {
	Body db.Setting
}

SettingsCreateOutput holds the response body for SettingsCreate.

type SettingsDeleteInput

type SettingsDeleteInput struct {
	Key string `path:"key"`
}

SettingsDeleteInput holds the request parameters for SettingsDelete.

type SettingsDeleteOutput

type SettingsDeleteOutput struct {
	Status int
}

SettingsDeleteOutput holds the response status for SettingsDelete.

type SettingsGetInput

type SettingsGetInput struct {
	Key string `path:"key"`
}

SettingsGetInput holds the request parameters for SettingsGet.

type SettingsGetOutput

type SettingsGetOutput struct {
	Body db.Setting
}

SettingsGetOutput holds the response body for SettingsGet.

type SettingsListInput

type SettingsListInput struct {
	SettingListQuery
}

SettingsListInput holds the request parameters for SettingsList.

type SettingsListOutput

type SettingsListOutput struct {
	Body query.PaginatedResponse[db.Setting]
}

SettingsListOutput holds the response body for SettingsList.

type SettingsUpdateInput

type SettingsUpdateInput struct {
	Key  string `path:"key"`
	Body SettingBody
}

SettingsUpdateInput holds the request parameters for SettingsUpdate.

type SettingsUpdateOutput

type SettingsUpdateOutput struct {
	Body db.Setting
}

SettingsUpdateOutput holds the response body for SettingsUpdate.

type StaffStatsResponse

type StaffStatsResponse struct {
	MyCourses         int64           `json:"my_courses"`
	JoinableCourses   int64           `json:"joinable_courses"`
	ArchivedCourses   int64           `json:"archived_courses"`
	SemesterBreakdown []SemesterCount `json:"semester_breakdown"`
	MyStudents        int64           `json:"my_students"`
	MyTutors          int64           `json:"my_tutors"`
	MyExercises       int64           `json:"my_exercises"`
}

StaffStatsResponse holds dashboard stats scoped to a single staff member.

func ComputeStaffStats

func ComputeStaffStats(gdb *gorm.DB, userID string) StaffStatsResponse

ComputeStaffStats aggregates dashboard statistics scoped to the courses owned by the given user ID.

type StatsInput

type StatsInput struct{}

StatsInput holds the request parameters for Stats (none).

type StatsOutput

type StatsOutput struct {
	Body StatsResponse
}

StatsOutput holds the response body for Stats.

type StatsResponse

type StatsResponse struct {
	TotalUsers        int64           `json:"total_users"`
	StaffUsers        int64           `json:"total_staff"`
	AdminUsers        int64           `json:"admin_users"`
	TotalCourses      int64           `json:"total_courses"`
	JoinableCourses   int64           `json:"joinable_courses"`
	ArchivedCourses   int64           `json:"archived_courses"`
	TotalStudents     int64           `json:"total_students"`
	TotalTutors       int64           `json:"total_tutors"`
	TotalAuditLogs    int64           `json:"total_audit_logs"`
	TotalExercises    int64           `json:"total_exercises"`
	ActiveSessions    int64           `json:"active_sessions"`
	TotalTasks        int64           `json:"total_tasks"`
	TotalCronJobs     int64           `json:"total_cron_jobs"`
	OrphanOrgs        int64           `json:"orphan_orgs"`
	SemesterBreakdown []SemesterCount `json:"semester_breakdown"`
}

StatsResponse is the response body of GET /stats. It aggregates counts across the major tables and includes a Forgejo orphan-org count when the Forgejo client is configured.

func ComputeStats

func ComputeStats(gdb *gorm.DB, scheduler jobsLister) StatsResponse

ComputeStats aggregates dashboard statistics. Exported so the HTML fragment handler (which has *app.App but not *api.Handler) can call it. The orphan-org count needs the Forgejo client, which is a package-level singleton, so this function has no receiver.

type StudentOut

type StudentOut struct {
	ID             uint       `json:"id"`
	Matrikelnummer *int       `json:"matrikelnummer"`
	CourseID       uint       `json:"course_id"`
	UserID         string     `json:"user_id"`
	TutorID        *uint      `json:"tutor_id"`
	User           *UserBrief `json:"user,omitempty"`
}

StudentOut is the public shape of a course-student row. It mirrors the flat db.Student fields and embeds the preloaded user as a trimmed UserBrief. db.Student.User is tagged json:"-" in the model (repo-wide convention to avoid serialization loops), so the DTO carries the user explicitly and controls exactly which user fields leave the API.

type TaskGetInput

type TaskGetInput struct {
	Name string `path:"name"`
}

TaskGetInput holds the request parameters for TaskGet.

type TaskGetOutput

type TaskGetOutput struct {
	Body tasks.Task
}

TaskGetOutput holds the response body for TaskGet.

type TaskListInput

type TaskListInput struct{}

TaskListInput holds the request parameters for TaskList (none).

type TaskListOutput

type TaskListOutput struct {
	Body []tasks.Task
}

TaskListOutput holds the response body for TaskList.

type TaskRunInput

type TaskRunInput struct {
	Name string `path:"name"`
}

TaskRunInput holds the request parameters for TaskRun.

type TaskRunOutput

type TaskRunOutput struct {
	Body map[string]string
}

TaskRunOutput holds the response body for TaskRun.

type TimeSeries

type TimeSeries struct {
	Label  string            `json:"label"`
	Points []TimeSeriesPoint `json:"points"`
}

TimeSeries is a named series of time-series points.

type TimeSeriesPoint

type TimeSeriesPoint struct {
	Date  string `json:"date"`
	Count int64  `json:"count"`
}

TimeSeriesPoint is a single point in a time series.

type TutorOut

type TutorOut struct {
	ID          uint       `json:"id"`
	Description string     `json:"description"`
	CreatedAt   time.Time  `json:"created_at"`
	UpdatedAt   time.Time  `json:"updated_at"`
	CourseID    uint       `json:"course_id"`
	UserID      string     `json:"user_id"`
	User        *UserBrief `json:"user,omitempty"`
}

TutorOut is the public shape of a course-tutor row. It mirrors the flat db.Tutor fields and embeds the preloaded user as a trimmed UserBrief. db.Tutor.User is tagged json:"-" in the model (repo-wide convention to avoid serialization loops), so the DTO carries the user explicitly and controls exactly which user fields leave the API.

type UpdateUserBody

type UpdateUserBody struct {
	Subject           string  `json:"subject" required:"false"`
	PreferredUsername string  `json:"preferred_username" required:"false"`
	StudentID         string  `json:"student_id" required:"false"`
	AccountType       string  `json:"account_type" required:"false"`
	Faculty           string  `json:"faculty" required:"false"`
	EnrolledSubject   string  `json:"enrolled_subject" required:"false"`
	Role              db.Role `json:"role" required:"false"`
}

UpdateUserBody is the request body for updating a user. name, email, and extra_claims are excluded because the update path never writes them. subject is still accepted so the identity-uniqueness check sees the same values the handler checks, but it is omitted from the update payload.

type UserAdminItem

type UserAdminItem struct {
	db.User `json:"user"`

	SessionCount int            `json:"session_count"`
	LastLogin    *time.Time     `json:"last_login"`
	Sessions     []SessionBrief `json:"sessions"`

	OwnedCourses    []CourseBrief `json:"owned_courses"`
	TutoredCourses  []CourseBrief `json:"tutored_courses"`
	EnrolledCourses []CourseBrief `json:"enrolled_courses"`

	APIKeys []db.ApiKey `json:"api_keys"`

	Grades []RecentGradeItem `json:"grades"`

	IsProtectedAdmin bool `json:"is_protected_admin"`
}

UserAdminItem is the full user representation for the admin users page, including session and course relationship data.

func AdminGetUser

func AdminGetUser(gdb *gorm.DB, id string) (UserAdminItem, error)

AdminGetUser fetches a single user with sessions and course relationships for the admin detail modal. Returns the enriched UserAdminItem.

func ScopedGetUser

func ScopedGetUser(gdb *gorm.DB, ownerID, targetUserID string) (UserAdminItem, error)

ScopedGetUser fetches a single user if they are within the caller's scope (self, or a student/tutor in a course the caller owns). Returns gorm.ErrRecordNotFound if the target is out of scope.

type UserBrief

type UserBrief struct {
	ID                string `json:"id"`
	Name              string `json:"name"`
	PreferredUsername string `json:"preferred_username"`
}

UserBrief is a trimmed user representation that is safe to embed in list endpoints reachable by non-admin staff (e.g. tutors viewing their students). It exposes only identity fields; email, OIDC subject, student ID (matrikelnummer), account type, faculty, enrolled subject, raw extra claims, and the global role are intentionally omitted.

type UserListQuery

type UserListQuery struct {
	ID        string `query:"id" required:"false" filter:"id"`
	Email     string `query:"email" required:"false" filter:"email"`
	Subject   string `query:"subject" required:"false" filter:"subject"`
	StudentID string `query:"student_id" required:"false" filter:"student_id"`
	Role      string `query:"role" required:"false" filter:"role"`
	Search    string `query:"search" required:"false" search:"name,email"`
	SortBy    string `query:"sort_by" required:"false" sort:"id,name,email,created_at,updated_at,student_id,role" default_sort:"created_at desc"`
	SortDir   string `query:"sort_dir" required:"false"`
	Page      int    `query:"page" default:"1"`
	PageSize  int    `query:"page_size" default:"25"`
}

UserListQuery holds the filter and pagination inputs for GET /users. The query/default tags drive huma's request binding; the filter/search/sort tags are ignored by huma and consumed by query.Scopes.

type UsersAdminListInput

type UsersAdminListInput struct {
	UsersAdminListQuery
}

UsersAdminListInput holds the request parameters for UsersAdminList.

type UsersAdminListOutput

type UsersAdminListOutput struct {
	Body query.PaginatedResponse[UserAdminItem]
}

UsersAdminListOutput holds the response body for UsersAdminList.

type UsersAdminListParams

type UsersAdminListParams struct {
	Search         string
	Role           string
	Matrikelnummer string
	Tutor          bool
	SortBy         string
	SortDir        string
	Page           int
	PageSize       int
}

UsersAdminListParams holds the filter/sort/pagination inputs for the admin users list. Shared by the JSON API handler and the HTML fragment handler.

func ParseUsersAdminListParams

func ParseUsersAdminListParams(c *echo.Context) UsersAdminListParams

ParseUsersAdminListParams extracts UsersAdminListParams from query string. Shared by the JSON API handler, the HTML fragment handler, and the page handler so all three paths agree on how params are read.

type UsersAdminListQuery

type UsersAdminListQuery struct {
	Search         string `query:"search" required:"false"`
	Role           string `query:"role" required:"false"`
	Matrikelnummer string `query:"matrikelnummer" required:"false"`
	Tutor          bool   `query:"tutor" required:"false"`
	SortBy         string `query:"sort_by" required:"false"`
	SortDir        string `query:"sort_dir" required:"false"`
	Page           int    `query:"page" default:"1"`
	PageSize       int    `query:"page_size" default:"25"`
}

UsersAdminListQuery holds the filter/sort/pagination inputs for the admin users list. The query/default tags drive huma's request binding; the values are mapped into UsersAdminListParams for the shared AdminListUsers query path.

type UsersAdminListResult

type UsersAdminListResult struct {
	Items      []UserAdminItem
	Total      int64
	Page       int
	PageSize   int
	TotalPages int
	HasNext    bool
	HasPrev    bool
}

UsersAdminListResult holds the paginated result of an admin users list query.

func AdminListUsers

func AdminListUsers(gdb *gorm.DB, p UsersAdminListParams) (UsersAdminListResult, error)

AdminListUsers runs the admin users list query and returns enriched items with pagination metadata. Used by both the JSON API handler and the HTML fragment handler: one query path, two response formats.

func ScopedListUsers

func ScopedListUsers(gdb *gorm.DB, p UsersAdminListParams, ownerID string) (UsersAdminListResult, error)

ScopedListUsers runs the admin users list query but restricts results to users visible to the given staff owner: themselves, plus students and tutors of courses they own. Used by staff-scoped fragment handlers.

type UsersCreateInput

type UsersCreateInput struct {
	Body CreateUserBody
}

UsersCreateInput holds the request parameters for UsersCreate.

type UsersCreateOutput

type UsersCreateOutput struct {
	Body db.User
}

UsersCreateOutput holds the response body for UsersCreate.

type UsersDeleteInput

type UsersDeleteInput struct {
	ID string `path:"id"`
}

UsersDeleteInput holds the request parameters for UsersDelete.

type UsersDeleteOutput

type UsersDeleteOutput struct {
	Status int
}

UsersDeleteOutput holds the response status for UsersDelete.

type UsersDeleteSessionsInput

type UsersDeleteSessionsInput struct {
	ID string `path:"id"`
}

UsersDeleteSessionsInput holds the request parameters for UsersDeleteSessions.

type UsersDeleteSessionsOutput

type UsersDeleteSessionsOutput struct {
	Body struct {
		DeletedCount int64 `json:"deleted_count"`
	}
}

UsersDeleteSessionsOutput holds the response body for UsersDeleteSessions.

type UsersGetInput

type UsersGetInput struct {
	ID string `path:"id"`
}

UsersGetInput holds the request parameters for UsersGet.

type UsersGetOutput

type UsersGetOutput struct {
	Body db.User
}

UsersGetOutput holds the response body for UsersGet.

type UsersListInput

type UsersListInput struct {
	UserListQuery
}

UsersListInput holds the request parameters for UsersList.

type UsersListOutput

type UsersListOutput struct {
	Body query.PaginatedResponse[db.User]
}

UsersListOutput holds the response body for UsersList.

type UsersMeCoursesInput

type UsersMeCoursesInput struct{}

UsersMeCoursesInput holds the request parameters for UsersMeCourses (none).

type UsersMeCoursesOutput

type UsersMeCoursesOutput struct {
	Body []MyCourseItem
}

UsersMeCoursesOutput holds the response body for UsersMeCourses.

type UsersMeGradesInput

type UsersMeGradesInput struct{}

UsersMeGradesInput holds the request parameters for UsersMeGrades (none).

type UsersMeGradesOutput

type UsersMeGradesOutput struct {
	Body []GradeItem
}

UsersMeGradesOutput holds the response body for UsersMeGrades.

type UsersMeInput

type UsersMeInput struct{}

UsersMeInput holds the request parameters for UsersMe (none).

type UsersMeOutput

type UsersMeOutput struct {
	Body db.User
}

UsersMeOutput holds the response body for UsersMe.

type UsersMeRecentGradesInput

type UsersMeRecentGradesInput struct{}

UsersMeRecentGradesInput holds the request parameters for UsersMeRecentGrades (none).

type UsersMeRecentGradesOutput

type UsersMeRecentGradesOutput struct {
	Body []RecentGradeItem
}

UsersMeRecentGradesOutput holds the response body for UsersMeRecentGrades.

type UsersUpdateInput

type UsersUpdateInput struct {
	ID   string `path:"id"`
	Body UpdateUserBody
}

UsersUpdateInput holds the request parameters for UsersUpdate.

type UsersUpdateOutput

type UsersUpdateOutput struct {
	Body db.User
}

UsersUpdateOutput holds the response body for UsersUpdate.

Directories

Path Synopsis
Package query provides tag-driven request binding, GORM scopes, pagination, and data access helpers for the API layer.
Package query provides tag-driven request binding, GORM scopes, pagination, and data access helpers for the API layer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL