Documentation
¶
Overview ¶
Package db is glabs-web's MongoDB layer. It owns the connection and the collection access; everything above it works with the decoded documents.
Index ¶
- Constants
- Variables
- type ActivityEntry
- type DB
- func (db *DB) ActivityFor(ctx context.Context, owner, course, assignment string) ([]*ActivityEntry, error)
- func (db *DB) CancelJob(ctx context.Context, owner, id string) (*ScheduledJob, error)
- func (db *DB) ClaimDueJob(ctx context.Context, workerID string, now time.Time) (*ScheduledJob, error)
- func (db *DB) CountUsers(ctx context.Context) (int64, error)
- func (db *DB) CourseActivityFor(ctx context.Context, owner, course string) ([]*ActivityEntry, error)
- func (db *DB) CourseOf(ctx context.Context, owner, name string) (*StoredCourse, error)
- func (db *DB) CoursesOf(ctx context.Context, owner string) ([]*StoredCourse, error)
- func (db *DB) DeleteCourse(ctx context.Context, owner, name string) error
- func (db *DB) DeleteUserGitLabToken(ctx context.Context, owner string) error
- func (db *DB) Disconnect(ctx context.Context) error
- func (db *DB) EnsureActivityIndexes(ctx context.Context) error
- func (db *DB) EnsureCourseIndexes(ctx context.Context) error
- func (db *DB) EnsureJobIndexes(ctx context.Context) error
- func (db *DB) EnsureUserIndexes(ctx context.Context) error
- func (db *DB) EnsureUserSecretIndexes(ctx context.Context) error
- func (db *DB) FinishJob(ctx context.Context, id, status, logText, errText string) error
- func (db *DB) GetUserByEmail(ctx context.Context, email string) (*model.User, error)
- func (db *DB) GetUserSecret(ctx context.Context, owner string) (*UserSecret, error)
- func (db *DB) JobOf(ctx context.Context, owner, id string) (*ScheduledJob, error)
- func (db *DB) JobsOf(ctx context.Context, owner string, statuses []string) ([]*ScheduledJob, error)
- func (db *DB) MarkNotified(ctx context.Context, id string) error
- func (db *DB) RecordActivity(ctx context.Context, e *ActivityEntry) error
- func (db *DB) SaveCourse(ctx context.Context, course *StoredCourse) error
- func (db *DB) SaveJob(ctx context.Context, job *ScheduledJob) error
- func (db *DB) SaveUser(ctx context.Context, user *model.User) error
- func (db *DB) SaveUserGitLabToken(ctx context.Context, owner string, sealed secrets.SealedValue, ...) error
- func (db *DB) UnnotifiedTerminalJobs(ctx context.Context) ([]*ScheduledJob, error)
- type ScheduledJob
- type StoredCourse
- type UserSecret
Constants ¶
const ( JobPending = "pending" JobRunning = "running" JobDone = "done" JobFailed = "failed" JobExpired = "expired" JobCancelled = "cancelled" )
Job status values. A job starts pending, is claimed to running, and ends in exactly one terminal state.
Variables ¶
var ErrCourseNotFound = errors.New("course not found")
ErrCourseNotFound is returned when a course does not exist for the given owner. It deliberately does not distinguish "does not exist" from "belongs to someone else": to one user, another user's course simply is not there.
var ErrJobNotFound = errors.New("scheduled job not found")
ErrJobNotFound is returned when a job does not exist for the given owner (or is no longer in a state that permits the requested change).
var ErrNoDueJob = errors.New("no due job")
ErrNoDueJob is returned by ClaimDueJob when there is nothing to run.
Functions ¶
This section is empty.
Types ¶
type ActivityEntry ¶ added in v3.25.0
type ActivityEntry struct {
Owner string `bson:"owner"`
Course string `bson:"course"`
Assignment string `bson:"assignment"`
Op string `bson:"op"`
Params map[string]string `bson:"params,omitempty"`
// Status is the terminal outcome — "done" or "failed".
Status string `bson:"status"`
// Detail is a short human summary: the repository count on success, the error
// message on failure.
Detail string `bson:"detail,omitempty"`
At time.Time `bson:"at"`
}
ActivityEntry records one mutating operation performed through the web against an assignment: what ran, when, and how it ended. It is the web's stand-in for the shell history the CLI leaves behind — the course page reads it to show, per assignment, what has already been done (setaccess, protect, archive, delete; later generate). Ownership is strict, exactly like courses: an entry belongs to the user who caused it and no other user can see it.
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
func Connect ¶
Connect opens the connection and verifies it with a ping, so a bad URI fails at startup rather than on the first query.
UseLocalTimeZone decodes the UTC that Mongo stores back into time.Local, which main sets to Europe/Berlin — so timestamps read out in the zone they were written in.
func (*DB) ActivityFor ¶ added in v3.25.0
func (db *DB) ActivityFor(ctx context.Context, owner, course, assignment string) ([]*ActivityEntry, error)
ActivityFor returns the log entries of one assignment, newest first.
func (*DB) CancelJob ¶ added in v3.26.0
CancelJob cancels one of the owner's pending jobs. A job that is already running or finished cannot be cancelled, and another user's job is invisible: both cases return ErrJobNotFound.
func (*DB) ClaimDueJob ¶ added in v3.26.0
func (db *DB) ClaimDueJob(ctx context.Context, workerID string, now time.Time) (*ScheduledJob, error)
ClaimDueJob atomically claims the oldest pending job whose time has come, flipping it to running so no other runner can take it. It returns ErrNoDueJob when nothing is due. This single atomic step is what replaces a distributed lock: exactly one runner ever owns a given job.
func (*DB) CountUsers ¶
CountUsers reports how many users exist, so seeding can run only on an empty allowlist.
func (*DB) CourseActivityFor ¶ added in v3.25.0
func (db *DB) CourseActivityFor(ctx context.Context, owner, course string) ([]*ActivityEntry, error)
CourseActivityFor returns the log entries across a whole course, newest first — the course page groups them by assignment to show each one's latest status.
func (*DB) CourseOf ¶ added in v3.2.0
CourseOf returns one course owned by the given user, or ErrCourseNotFound.
func (*DB) CoursesOf ¶ added in v3.2.0
CoursesOf returns the courses owned by the given user, sorted by name.
func (*DB) DeleteCourse ¶ added in v3.2.0
DeleteCourse removes a course owned by the given user. Deleting a course that does not exist for that owner is ErrCourseNotFound, not a silent success — so a delete of another user's course reports "not found" rather than pretending it worked.
func (*DB) DeleteUserGitLabToken ¶ added in v3.3.0
DeleteUserGitLabToken removes only the GitLab PAT from a user's secrets.
func (*DB) EnsureActivityIndexes ¶ added in v3.25.0
EnsureActivityIndexes indexes the log for the two reads the GUI makes: the newest entries of one assignment, and the newest across a whole course.
func (*DB) EnsureCourseIndexes ¶ added in v3.2.0
EnsureCourseIndexes makes (owner, name) unique — a user has at most one course of a given name, and the pair is how every query is keyed.
func (*DB) EnsureJobIndexes ¶ added in v3.26.0
EnsureJobIndexes indexes the collection for the claim ({status, runAt}), the owner's GUI list ({owner, runAt desc}), and a 30-day TTL on finished jobs.
func (*DB) EnsureUserIndexes ¶
EnsureUserIndexes makes the email unique. Called once at startup.
func (*DB) EnsureUserSecretIndexes ¶ added in v3.3.0
EnsureUserSecretIndexes makes owner unique — one secrets document per user.
func (*DB) FinishJob ¶ added in v3.26.0
FinishJob records a terminal state (done/failed/expired) with its log and error.
func (*DB) GetUserByEmail ¶
GetUserByEmail returns the user with the given email, or nil if there is none. The auth middleware treats nil as "not on the allowlist" — a 403 — so a missing user must be nil, nil rather than an error.
func (*DB) GetUserSecret ¶ added in v3.3.0
GetUserSecret returns the stored secrets for a user, or nil when none exist.
func (*DB) JobsOf ¶ added in v3.26.0
JobsOf returns the owner's jobs, newest scheduled first, optionally filtered to the given statuses. Like courses, there is no read without an owner filter.
func (*DB) MarkNotified ¶ added in v3.26.0
MarkNotified flags that the terminal-state email for a job has been sent, so a restart mid-notification does not send it twice.
func (*DB) RecordActivity ¶ added in v3.25.0
func (db *DB) RecordActivity(ctx context.Context, e *ActivityEntry) error
RecordActivity appends one entry to the log. The owner, course and assignment on the entry are set by the caller from the authenticated principal.
func (*DB) SaveCourse ¶ added in v3.2.0
func (db *DB) SaveCourse(ctx context.Context, course *StoredCourse) error
SaveCourse inserts or replaces a course for its owner. The owner and name on the document are the key; a document can never be written under a different owner than the one on it.
func (*DB) SaveJob ¶ added in v3.26.0
func (db *DB) SaveJob(ctx context.Context, job *ScheduledJob) error
SaveJob inserts a new job, assigning it an id if it has none.
func (*DB) SaveUserGitLabToken ¶ added in v3.3.0
func (db *DB) SaveUserGitLabToken(ctx context.Context, owner string, sealed secrets.SealedValue, updatedAt time.Time) error
SaveUserGitLabToken upserts the sealed GitLab PAT for a user, touching only the gitlab fields so it never clobbers other secrets on the document.
func (*DB) UnnotifiedTerminalJobs ¶ added in v3.29.0
func (db *DB) UnnotifiedTerminalJobs(ctx context.Context) ([]*ScheduledJob, error)
UnnotifiedTerminalJobs returns finished jobs (done/failed/expired) whose notification email has not been sent yet, across all owners — the runner's notify sweep. Cancelled jobs are excluded (there is no cancellation mail). This is what makes "email on every terminal state" survive a crash between finishing a job and mailing it: on restart the job is terminal and still unnotified, so the mail is sent (once — MarkNotified then guards against a resend).
type ScheduledJob ¶ added in v3.26.0
type ScheduledJob struct {
ID string `bson:"_id"`
Owner string `bson:"owner"`
Op string `bson:"op"`
Course string `bson:"course"`
Assignment string `bson:"assignment"`
OnlyFor []string `bson:"onlyFor,omitempty"`
Params map[string]string `bson:"params,omitempty"`
RunAt time.Time `bson:"runAt"`
// ConfigHash is copied from the confirm token; the runner re-resolves and
// compares it at fire time, refusing a job whose config drifted since planning.
ConfigHash string `bson:"configHash"`
Status string `bson:"status"`
GraceMin int `bson:"graceMinutes"`
CreatedAt time.Time `bson:"createdAt"`
StartedAt *time.Time `bson:"startedAt,omitempty"`
FinishedAt *time.Time `bson:"finishedAt,omitempty"`
Log string `bson:"log,omitempty"`
Err string `bson:"err,omitempty"`
Notified bool `bson:"notified"`
WorkerID string `bson:"workerID,omitempty"`
}
ScheduledJob is one mutating operation queued to run at a wall-clock time. It is the persistent unit the poll-runner claims and executes; because it lives in Mongo, jobs survive restarts, missed runs are caught up after downtime, and an atomic claim keeps two runners from firing the same job. Ownership is strict, exactly like courses.
type StoredCourse ¶ added in v3.2.0
type StoredCourse struct {
Owner string `bson:"owner"`
Name string `bson:"name"`
Source *config.CourseSource `bson:"source"`
RawYAML []byte `bson:"rawYAML,omitempty"`
ImportedAt time.Time `bson:"importedAt"`
UpdatedAt time.Time `bson:"updatedAt"`
}
StoredCourse is a course as saved by one user. Ownership is strict: a course belongs to the user who imported it, and no other user can see or touch it.
RawYAML is kept verbatim alongside the parsed Source so a download can return exactly what was uploaded — comments and key order and all — as long as the course has not been edited through the web. Re-encoding Source would lose them.
type UserSecret ¶ added in v3.3.0
type UserSecret struct {
Owner string `bson:"owner"`
GitLab *secrets.SealedValue `bson:"gitlab,omitempty"`
GitLabUpdatedAt *time.Time `bson:"gitlabUpdatedAt,omitempty"`
}
UserSecret holds a user's encrypted per-user secrets, keyed by the owner's email — here, the GitLab personal access token. The value is AES-256-GCM sealed; the plaintext never touches the database. This document is never exposed over GraphQL, only a "set / when" status is.