db

package
v3.32.0 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

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

View Source
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

View Source
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.

View Source
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).

View Source
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

func Connect(ctx context.Context, uri, database string) (*DB, error)

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

func (db *DB) CancelJob(ctx context.Context, owner, id string) (*ScheduledJob, error)

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

func (db *DB) CountUsers(ctx context.Context) (int64, error)

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

func (db *DB) CourseOf(ctx context.Context, owner, name string) (*StoredCourse, error)

CourseOf returns one course owned by the given user, or ErrCourseNotFound.

func (*DB) CoursesOf added in v3.2.0

func (db *DB) CoursesOf(ctx context.Context, owner string) ([]*StoredCourse, error)

CoursesOf returns the courses owned by the given user, sorted by name.

func (*DB) DeleteCourse added in v3.2.0

func (db *DB) DeleteCourse(ctx context.Context, owner, name string) error

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

func (db *DB) DeleteUserGitLabToken(ctx context.Context, owner string) error

DeleteUserGitLabToken removes only the GitLab PAT from a user's secrets.

func (*DB) Disconnect

func (db *DB) Disconnect(ctx context.Context) error

func (*DB) EnsureActivityIndexes added in v3.25.0

func (db *DB) EnsureActivityIndexes(ctx context.Context) error

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

func (db *DB) EnsureCourseIndexes(ctx context.Context) error

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

func (db *DB) EnsureJobIndexes(ctx context.Context) error

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

func (db *DB) EnsureUserIndexes(ctx context.Context) error

EnsureUserIndexes makes the email unique. Called once at startup.

func (*DB) EnsureUserSecretIndexes added in v3.3.0

func (db *DB) EnsureUserSecretIndexes(ctx context.Context) error

EnsureUserSecretIndexes makes owner unique — one secrets document per user.

func (*DB) FinishJob added in v3.26.0

func (db *DB) FinishJob(ctx context.Context, id, status, logText, errText string) error

FinishJob records a terminal state (done/failed/expired) with its log and error.

func (*DB) GetUserByEmail

func (db *DB) GetUserByEmail(ctx context.Context, email string) (*model.User, error)

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

func (db *DB) GetUserSecret(ctx context.Context, owner string) (*UserSecret, error)

GetUserSecret returns the stored secrets for a user, or nil when none exist.

func (*DB) JobOf added in v3.26.0

func (db *DB) JobOf(ctx context.Context, owner, id string) (*ScheduledJob, error)

JobOf returns one of the owner's jobs, or ErrJobNotFound.

func (*DB) JobsOf added in v3.26.0

func (db *DB) JobsOf(ctx context.Context, owner string, statuses []string) ([]*ScheduledJob, error)

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

func (db *DB) MarkNotified(ctx context.Context, id string) error

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) SaveUser

func (db *DB) SaveUser(ctx context.Context, user *model.User) error

SaveUser inserts or updates a user, keyed by email.

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.

Jump to

Keyboard shortcuts

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