Documentation
¶
Index ¶
- Constants
- Variables
- func AdminOfCourse(c *gin.Context)
- func AdminToken(daoWrapper dao.DaoWrapper) gin.HandlerFunc
- func CourseListContains(courses []model.Course, courseId uint) bool
- func CreateAccessToken(user uint, samlSubjectID *string) (string, error)
- func DownloadFileName(courseSlug string, s *model.Stream, version string) string
- func ErrorHandler(c *gin.Context)
- func GetCacheItem(key string) (interface{}, bool)
- func InitBranding()
- func InitContext(daoWrapper dao.DaoWrapper) gin.HandlerFunc
- func InitCourse(wrapper dao.DaoWrapper) gin.HandlerFunc
- func InitCronService()
- func InitStream(wrapper dao.DaoWrapper) gin.HandlerFunc
- func LoadConfig()
- func LoggedIn(c *gin.Context)
- func MaskEmail(email string) (masked string, err error)
- func MaskLogin(login string) (masked string)
- func OwnerOfCourse(c *gin.Context)
- func RenderErrorPage(c *gin.Context, status int, message string)
- func RequirePermission(p model.Permission) gin.HandlerFunc
- func SearchCourses(q string, filter string) *meilisearch.SearchResponse
- func SendAccountInvite(ctx context.Context, daoWrapper dao.DaoWrapper, email string) error
- func SetCacheItem(key string, value interface{}, ttl time.Duration)
- func SetErrorPageRenderer(f func(c *gin.Context, status int, message string))
- func SetJWTKeyForTest(k *rsa.PrivateKey) (restore func())
- func SetSignedPlaylists(s *model.Stream, user *model.User, allowDownloading bool) error
- func SetSignedPlaylistsForCourse(s *model.Stream, user *model.User, courseSlug string, allowDownloading bool) error
- func StartSession(c *gin.Context, data *SessionData)
- func Truncate(input string, length int) string
- func TruncateHtml(buf []byte, maxlen int, ellipsis string) ([]byte, error)
- type Branding
- type Config
- type CronService
- type ExportDataEntry
- type ExportStatsContainer
- type JWTClaims
- type JWTPlaylistClaims
- type MailConfig
- type Mailer
- type MeiliCourse
- type MeiliExporter
- type MeiliSearchInterface
- type MeiliStream
- type MeiliSubtitles
- type RequestError
- type SessionData
- type TUMLiveContext
- type TestEnvPolicy
Constants ¶
const ( PageNotFoundErrMsg = "This page does not exist." CourseNotFoundErrMsg = "We couldn't find the course you were looking for." StreamNotFoundErrMsg = "We couldn't find the stream you were looking for." ForbiddenGenericErrMsg = "You don't have permissions to access this resource. " + "Please reach out if this seems wrong :)" ForbiddenStreamAccess = "You don't have permissions to access this stream. " + "Please make sure to use the correct login." ForbiddenCourseAccess = "You don't have permissions to access this course. " + "Please make sure to use the correct login." )
const ( CSVSep = "," CSVLb = "\n\r" )
const AccessTokenTTL = time.Minute * 15
AccessTokenTTL is how long a bearer access token stays valid. Clients hold them in memory and re-mint from the session cookie, so they are far shorter-lived.
const GenericInternalErrorMessage = "internal server error"
GenericInternalErrorMessage is what a client is told when a handler pushes a plain error instead of a RequestError. Raw Go error strings routinely carry SQL fragments, file paths and internal hostnames, so the real cause is logged rather than returned.
const SessionTTL = time.Hour * 24 * 7
SessionTTL is how long a browser session cookie stays valid.
Variables ¶
var ( ErrUnbalancedTags = errors.New("unbalanced tags") TagExpr = regexp.MustCompile("<(/?)([A-Za-z0-9]+).*?>") EntityExpr = regexp.MustCompile("&#?[A-Za-z0-9]+;") )
var CookieSecure = false
CookieSecure sets whether to use secure cookies or not, defaults to false in dev mode, true in production
var ErrMeiliNotConfigured = errors.New("meilisearch is not configured")
var VersionTag = "development"
VersionTag is the build's version, set from main via ldflags, "development" when unstamped. Here rather than in web so the v2 API need not import the frontend it is replacing.
Functions ¶
func AdminOfCourse ¶
AdminOfCourse checks if the user is an admin of the course or admin. If not, aborts with status Forbidden.
func AdminToken ¶
func AdminToken(daoWrapper dao.DaoWrapper) gin.HandlerFunc
func CourseListContains ¶
CourseListContains checks whether courses contain a course with a given courseId
func CreateAccessToken ¶ added in v1.7.10
CreateAccessToken mints a short-lived `Authorization: Bearer` credential for the v2 API, signed with the same key and claims as a session cookie.
func DownloadFileName ¶ added in v1.11.1
DownloadFileName returns a human-friendly file name for downloads of s, like "eidi_Graphs-and-Trees_2025-10-14_COMB.mp4". Streams usually have no name, which gives "eidi_2025-10-14_COMB.mp4". version (COMB, CAM or PRES) is part of the name so that the camera and presentation downloads of one lecture don't overwrite each other. Anything that could break a Content-Disposition header or a path on the user's disk is replaced, and the parts are bounded in length. It returns "" when there is nothing to name the file after, and the edge falls back to the stream id.
func ErrorHandler ¶
func GetCacheItem ¶
GetCacheItem returns the value of the key if it exists in the cache. (nil, err) otherwise
func InitBranding ¶
func InitBranding()
InitBranding initializes the global branding configuration variable `BrandingCfg`. If the config file doesn't exist it will be set to the result of `getDefaultBranding()`.
func InitContext ¶
func InitContext(daoWrapper dao.DaoWrapper) gin.HandlerFunc
func InitCourse ¶
func InitCourse(wrapper dao.DaoWrapper) gin.HandlerFunc
func InitStream ¶
func InitStream(wrapper dao.DaoWrapper) gin.HandlerFunc
func LoadConfig ¶
func LoadConfig()
func LoggedIn ¶
LoggedIn is a middleware that checks if the user is logged in and redirects to the login page if not
func OwnerOfCourse ¶
func RenderErrorPage ¶
RenderErrorPage answers with the given status and message, and aborts the context. A page request gets the SPA shell, which renders the error; under /api/ the caller is a script, which gets the message as plain text.
func RequirePermission ¶ added in v1.7.10
func RequirePermission(p model.Permission) gin.HandlerFunc
RequirePermission aborts with Forbidden unless the caller holds the permission. A route group states the capability it needs rather than the role that has it, so moving a capability between roles is a change to model.rolePermissions alone.
func SearchCourses ¶
func SearchCourses(q string, filter string) *meilisearch.SearchResponse
func SendAccountInvite ¶ added in v1.8.0
SendAccountInvite queues the mail inviting someone to set a password on a new account. The register link is single-use, so calling this twice invalidates the first mail's.
func SetCacheItem ¶
SetCacheItem adds the key and value to the cache with the given expiration time.
func SetErrorPageRenderer ¶ added in v1.10.0
SetErrorPageRenderer sets how RenderErrorPage answers a request for a page.
func SetJWTKeyForTest ¶ added in v1.10.0
func SetJWTKeyForTest(k *rsa.PrivateKey) (restore func())
SetJWTKeyForTest installs the key playlists and tokens are signed with, for tests of code that signs without booting the configuration. It returns what undoes it.
func SetSignedPlaylists ¶
SetSignedPlaylists adds a signed jwt to all available playlist urls that indicates that the user is allowed to consume the playlist. The method assumes that the user has been pre-authorized and doesn't check for permissions. Downloads signed through it are not named after a course, use SetSignedPlaylistsForCourse where downloads may be allowed.
func SetSignedPlaylistsForCourse ¶ added in v1.11.1
func SetSignedPlaylistsForCourse(s *model.Stream, user *model.User, courseSlug string, allowDownloading bool) error
SetSignedPlaylistsForCourse is SetSignedPlaylists that also names downloads after the course (courseSlug), as the stream doesn't know it. The slug is only used when allowDownloading is set.
func StartSession ¶
func StartSession(c *gin.Context, data *SessionData)
func TruncateHtml ¶
TruncateHtml will truncate a given byte slice to a maximum of maxlen visible characters and optionally append ellipsis. HTML tags are automatically closed generating valid truncated HTML. Based on: https://github.com/mborgerson/GoTruncateHtml
Types ¶
type Config ¶
type Config struct {
Mail MailConfig `yaml:"mail"`
Db struct {
User string `yaml:"user"`
Password string `yaml:"password"`
Database string `yaml:"database"`
Host string `yaml:"host"`
Port uint `yaml:"port"`
} `yaml:"db"`
Campus struct {
Base string `yaml:"base"`
Tokens []string `yaml:"tokens"`
CampusProxy *struct {
Host string `yaml:"host"`
Scheme string `yaml:"scheme"`
} `yaml:"campusProxy"`
RelevantOrgs *[]string `yaml:"relevantOrgs"`
} `yaml:"campus"`
Ldap struct {
URL string `yaml:"url"`
User string `yaml:"user"`
Password string `yaml:"password"`
BaseDn string `yaml:"baseDn"`
UserDn string `yaml:"userDn"`
UseForLogin bool `yaml:"useForLogin"`
} `yaml:"ldap"`
Saml *struct {
IdpMetadataURL string `yaml:"idpMetadataURL"`
Cert string `yaml:"cert"`
Privkey string `yaml:"privkey"`
EntityID string `yaml:"entityID"`
RootURLs []string `yaml:"rootURL"`
IdpName string `yaml:"idpName"`
IdpColor string `yaml:"idpColor"`
} `yaml:"saml"`
Paths struct {
Static string `yaml:"static"`
Mass string `yaml:"mass"`
Branding string `yaml:"branding"`
} `yaml:"paths"`
Auths struct {
SmpUser string `yaml:"smpUser"` // todo, do we need this? Should this be in the lecture_halls table?
SmpPassword string `yaml:"smpPassword"`
PwrCrtlAuth string `yaml:"pwrCrtlAuth"`
CamAuth string `yaml:"camAuth"`
CamAuthSony string `yaml:"camAuthSony"`
} `yaml:"auths"`
Alerts *struct {
Matrix *struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
Homeserver string `yaml:"homeserver"`
LogRoomID string `yaml:"logRoomID"`
AlertRoomID string `yaml:"alertRoomId"`
} `yaml:"matrix"`
} `yaml:"alerts"`
VoiceService *struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
AuthToken string `yaml:"authToken"`
}
IngestBase string `yaml:"ingestBase"`
WebUrl string `yaml:"webUrl"`
WorkerToken string `yaml:"workerToken"` // used for workers to join the worker pool
JWTKey *string `yaml:"jwtKey"`
Meili *struct {
Host string `yaml:"host"`
ApiKey string `yaml:"apiKey"`
} `yaml:"meili"`
// MetricsPort is the port the Prometheus endpoint listens on, separate from the
// web port so the metrics are not exposed to the internet. Empty disables it.
MetricsPort string `yaml:"metricsPort"`
VodURLTemplate string `yaml:"vodURLTemplate"`
CanonicalURL string `yaml:"canonicalURL"`
WikiURL string `yaml:"wikiURL"`
RtmpProxyURL string `yaml:"rtmpProxyURL"`
RtmpProxyService string `yaml:"rtmpProxyService"`
AllowedReactions []string `yaml:"allowedReactions"`
}
func (Config) GetJWTKey ¶
func (Config) GetJWTKey() *rsa.PrivateKey
func (Config) GetMeiliClient ¶
func (c Config) GetMeiliClient() (meilisearch.ServiceManager, error)
type CronService ¶
type CronService struct {
// contains filtered or unexported fields
}
var Cron *CronService
Cron is the global CronService
func (*CronService) AddFunc ¶
func (c *CronService) AddFunc(name string, fn func(), spec string) error
AddFunc creates a cronJob fn running at the interval specified by spec. The job can be referenced by name.
func (*CronService) ListCronJobs ¶
func (c *CronService) ListCronJobs() []string
ListCronJobs returns a []string with the names of all cronjobs
func (*CronService) RunJob ¶
func (c *CronService) RunJob(name string)
RunJob executes the cronJob identified by name even when it's not due. Invalid names are ignored silently.
type ExportDataEntry ¶
type ExportStatsContainer ¶
type ExportStatsContainer struct {
// contains filtered or unexported fields
}
func (ExportStatsContainer) AddDataEntry ¶
func (e ExportStatsContainer) AddDataEntry(entry *ExportDataEntry) ExportStatsContainer
func (ExportStatsContainer) ExportCsv ¶
func (e ExportStatsContainer) ExportCsv() string
func (ExportStatsContainer) ExportJson ¶
func (e ExportStatsContainer) ExportJson() []gin.H
type JWTClaims ¶
type JWTClaims struct {
*jwt.RegisteredClaims
UserID uint
SamlSubjectID *string // identifier of the SAML session (if any)
}
JWTClaims are the claims contained in a session
type JWTPlaylistClaims ¶
type JWTPlaylistClaims struct {
jwt.RegisteredClaims
UserID uint
Playlist string
Download bool
StreamID string
CourseID string
// FileName is what a download is saved as. The edge has no database, so the
// human-friendly name has to travel in the token. Only set when downloads are
// allowed, since nothing else reads it; also empty for tokens issued before it
// existed, where the edge falls back to the stream id.
FileName string `json:",omitempty"`
}
type MailConfig ¶
type Mailer ¶
type Mailer struct {
Dao dao.DaoWrapper
MaxMailsPerMinute int
}
type MeiliCourse ¶
type MeiliExporter ¶
type MeiliExporter struct {
// contains filtered or unexported fields
}
func NewMeiliExporter ¶
func NewMeiliExporter(d dao.DaoWrapper) *MeiliExporter
func (*MeiliExporter) Export ¶
func (m *MeiliExporter) Export()
Export exports all relevant search data to MeiliSearch Instance
func (*MeiliExporter) SetIndexSettings ¶
func (m *MeiliExporter) SetIndexSettings()
type MeiliSearchInterface ¶
type MeiliSearchInterface interface {
SearchSubtitles(q string, streamID uint) *meilisearch.SearchResponse
Search(q string, limit int64, searchType int, courseFilter string, streamFilter string, subtitleFilter string) *meilisearch.MultiSearchResponse
}
func NewMeiliSearchFunctions ¶
func NewMeiliSearchFunctions() MeiliSearchInterface
type MeiliStream ¶
type MeiliStream struct {
ID uint `json:"ID"`
Name string `json:"name"`
Description string `json:"description"`
CourseName string `json:"courseName"`
Year int `json:"year"`
TeachingTerm string `json:"semester"`
CourseID uint `json:"courseID"`
Private uint `json:"private"`
Visibility string `json:"visibility"` // corresponds to the visibility of the course
}
type MeiliSubtitles ¶
type MeiliSubtitles struct {
ID string `json:"ID"` // meili id: streamID + timestamp
StreamID uint `json:"streamID"`
Timestamp int64 `json:"timestamp"`
TextPrev string `json:"textPrev"` // the previous subtitle line
Text string `json:"text"`
TextNext string `json:"textNext"` // the next subtitle line
}
type RequestError ¶
func (RequestError) Error ¶
func (r RequestError) Error() string
func (RequestError) ToResponse ¶
func (r RequestError) ToResponse() gin.H
type SessionData ¶
type TUMLiveContext ¶
type TUMLiveContext struct {
User *model.User
Course *model.Course
Stream *model.Stream
SamlSubjectID *string
}
func (*TUMLiveContext) UserIsAdmin ¶
func (c *TUMLiveContext) UserIsAdmin() bool
type TestEnvPolicy ¶ added in v1.11.1
type TestEnvPolicy struct {
Enabled bool
// contains filtered or unexported fields
}
TestEnvPolicy restricts what a server booted against a copy of the production database may do to the outside world. The zero value is production: everything allowed.
var TestEnv TestEnvPolicy
TestEnv is the process-wide policy, set once at boot from the command line.
func NewTestEnvPolicy ¶ added in v1.11.1
func NewTestEnvPolicy(enabled bool, streamCourses string) (TestEnvPolicy, error)
NewTestEnvPolicy builds the policy from the --test-env and --stream-courses flags. Course IDs without --test-env are rejected rather than ignored: a stray flag in a production unit file would otherwise look like it restricts streaming and silently not.
func (TestEnvPolicy) AllowsStream ¶ added in v1.11.1
func (p TestEnvPolicy) AllowsStream(courseID uint) bool
AllowsStream reports whether a stream of the given course may be started.
func (TestEnvPolicy) StreamCourseIDs ¶ added in v1.11.1
func (p TestEnvPolicy) StreamCourseIDs() []uint
StreamCourseIDs returns the allowlisted course IDs, for the boot log.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package safepath builds filesystem paths from untrusted input, such as user-supplied course names, without letting that input escape the directory it is supposed to live in.
|
Package safepath builds filesystem paths from untrusted input, such as user-supplied course names, without letting that input escape the directory it is supposed to live in. |
|
Package timing provides time calculation functions used in TUM Live
|
Package timing provides time calculation functions used in TUM Live |