Help us shape the future of Go! Take the 2026 Go Developer Survey

db

package
v3.48.0 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: BSD-3-Clause Imports: 23 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 (
	EventLogin         = "login"          // a user was active (throttled: at most once per window)
	EventLoginRejected = "login-rejected" // a request without an identity was refused
	EventJobScheduled  = "job-scheduled"  // an operation was queued to run later
	EventJobDone       = "job-done"
	EventJobFailed     = "job-failed"
	EventJobExpired    = "job-expired"
	EventJobCancelled  = "job-cancelled"
	EventOpDone        = "op-done" // an interactive (run-now) operation finished
	EventOpFailed      = "op-failed"
	EventCourseCreated = "course-created"
	EventCourseDeleted = "course-deleted"
	EventTokenSaved    = "token-saved"
	EventTokenDeleted  = "token-deleted"

	EventAccessRequested = "access-requested" // someone asked to be let in
	EventAccessGranted   = "access-granted"   // an admin approved a request
	EventAccessRejected  = "access-rejected"  // an admin turned a request down
	EventAccessRevoked   = "access-revoked"   // an admin withdrew an approval
	EventAccessReset     = "access-reset"     // an admin cleared a decision; the person may ask again
)

Event types. Unlike the owner-scoped activity log (which records only the six mutating GitLab ops of a single user), the event log is the platform-wide monitoring trail an operator reads: who signed in, which jobs were scheduled and how they ended, and anything else worth watching. It is deliberately NOT owner-scoped — an admin reads across all users.

View Source
const (
	SeverityInfo    = "info"
	SeverityWarning = "warning"
	SeverityError   = "error"
)

Event severities, used to highlight what matters in the digest and the admin page. They live here (not in config) to stay independent of the linter's Severity type.

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.

View Source
const (
	AccessPending  = "pending"
	AccessApproved = "approved"
	AccessRejected = "rejected"
	AccessRevoked  = "revoked"
)

Access states of a row in the users table. "Never asked" has no row and no constant here; the app calls it AccessNone.

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.

View Source
var ImportTables = []string{
	"system_state",
	"user_secrets",
	"courses",
	"scheduled_jobs",
	"activity",
	"events",
}

ImportTables lists the tables the importer fills, in the order it fills them: the irreplaceable first, so that a run which dies half way has already carried across the part nobody can recreate.

Functions

func LooksLikeMongoURI added in v3.46.0

func LooksLikeMongoURI(uri string) bool

LooksLikeMongoURI reports whether a connection string is a MongoDB one.

This exists for exactly one moment: the evening glabs-web is switched over, when the most likely mistake is a new image starting against an unchanged .glabs-web.yaml. Without it the server would come up and behave as though every user had no courses. bootstrap refuses to start instead.

func MigratePG added in v3.46.0

func MigratePG(ctx context.Context, pool *pgxpool.Pool) error

MigratePG brings the database up to the schema this binary was built with.

Fatal on failure, unlike the EnsureXIndexes calls it replaces: a half-migrated schema means the queries compiled into this binary do not match the database, and every later error would be a confusing symptom of that one cause.

Repeating it is a no-op -- goose applies only what is missing.

func MigrationsChecksum added in v3.46.0

func MigrationsChecksum() (string, error)

MigrationsChecksum identifies the schema this binary carries. Two binaries with the same checksum expect the same tables.

internal/pgtest names its template database after it, so that editing a migration produces a fresh template instead of silently reusing a stale one -- the failure mode that otherwise costs an afternoon of confusing test results.

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) AllActivityFor added in v3.39.0

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

AllActivityFor returns the owner's complete log across all their courses, newest first — the audit-log dump. Unlike the GUI's per-assignment/per-course reads it is UNCAPPED (limit 0): a dump must be complete, and one user's audit trail is bounded in practice.

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) 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) EnsureEventIndexes added in v3.40.0

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

EnsureEventIndexes indexes the log for the newest-first reads (the digest window and the admin page) and a filter-by-type, plus a 180-day TTL.

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) 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) EventsBetween added in v3.40.0

func (db *DB) EventsBetween(ctx context.Context, since, until time.Time) ([]*Event, error)

EventsBetween returns every event in [since, until), oldest first — the window the nightly digest aggregates. It is cross-user by design.

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) 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) RecentEvents added in v3.40.0

func (db *DB) RecentEvents(ctx context.Context, since time.Time, limit int64) ([]*Event, error)

RecentEvents returns events at or after since, newest first, capped at limit — the admin page's live feed. Cross-user by design.

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) RecordEvent added in v3.40.0

func (db *DB) RecordEvent(ctx context.Context, e *Event) error

RecordEvent appends one event to the log. Callers set At and Severity; a missing severity defaults to info so a forgotten field never hides an event.

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) SetSummarySentAt added in v3.40.0

func (db *DB) SetSummarySentAt(ctx context.Context, at time.Time) error

SetSummarySentAt records when the nightly summary was last sent.

func (*DB) SystemState added in v3.40.0

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

SystemState returns the single state document, or a zero-valued one (never nil) if it does not exist yet.

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 Event added in v3.40.0

type Event struct {
	At   time.Time `bson:"at"`
	Type string    `bson:"type"`
	// Actor is the acting user's email (lowercased); empty for an anonymous
	// rejected login (no identity header at all).
	Actor      string `bson:"actor,omitempty"`
	ActorName  string `bson:"actorName,omitempty"`
	Department string `bson:"department,omitempty"`
	Course     string `bson:"course,omitempty"`
	Assignment string `bson:"assignment,omitempty"`
	Op         string `bson:"op,omitempty"`
	Severity   string `bson:"severity"`
	Detail     string `bson:"detail,omitempty"`
	JobID      string `bson:"jobID,omitempty"`
}

Event is one thing that happened on the platform, worth an operator's attention. Most fields are optional and depend on the type: a login carries an actor (and maybe a department) but no course; a job event carries course, assignment and op. Ownership is NOT enforced — this collection exists precisely to be read across all users by an admin.

type PG added in v3.46.0

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

PG is glabs-web's PostgreSQL store. It implements the same set of methods as *DB, the MongoDB one, and web/db/storetest runs the same suite against both -- that equivalence is what makes the switch a configuration change rather than a leap of faith.

The DTOs (StoredCourse, ScheduledJob, Event, ...) and the sentinel errors are shared: they live next to the Mongo store and stay exactly as they are. Only the way they are read and written changes.

func NewPG added in v3.46.0

func NewPG(ctx context.Context, uri string) (*PG, error)

NewPG connects and verifies the connection, so a wrong URI fails at startup rather than on the first query -- the same promise db.Connect makes.

The session time zone is pinned to Europe/Berlin, but not because the application depends on it: pgx decodes timestamptz into time.Local regardless of the session setting, and time.Local is Europe/Berlin because main.go says so. Removing this line leaves the store contract suite green -- checked, not assumed.

It is here so that a psql on the host, an ad-hoc query during an incident and the GUI all print the same wall-clock time. That is worth one line; believing it is what makes the timestamps correct would not be.

func (*PG) ActivityFor added in v3.46.0

func (db *PG) ActivityFor(ctx context.Context, owner, course, assignment string) ([]*ActivityEntry, error)

ActivityFor returns the log entries of one assignment, newest first, capped.

func (*PG) AllActivityFor added in v3.46.0

func (db *PG) AllActivityFor(ctx context.Context, owner string) ([]*ActivityEntry, error)

AllActivityFor returns the owner's complete log, newest first and UNCAPPED: a dump has to be complete, and one user's trail is bounded in practice.

func (*PG) CancelJob added in v3.46.0

func (db *PG) 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 (*PG) ClaimDueJob added in v3.46.0

func (db *PG) 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.

func (*PG) CountsForImport added in v3.46.0

func (db *PG) CountsForImport(ctx context.Context) (map[string]int64, error)

CountsForImport returns the number of rows in each table.

It answers two questions for the importer: is the target empty (so a run cannot silently duplicate a previous one), and afterwards, does every table hold as many rows as MongoDB did.

func (*PG) CourseActivityFor added in v3.46.0

func (db *PG) CourseActivityFor(ctx context.Context, owner, course string) ([]*ActivityEntry, error)

CourseActivityFor returns the log entries across a whole course, newest first, capped.

func (*PG) CourseOf added in v3.46.0

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

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

It deliberately does not distinguish "does not exist" from "belongs to someone else": to one user, another user's course simply is not there.

func (*PG) CoursesOf added in v3.46.0

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

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

func (*PG) DBHost added in v3.46.0

func (db *PG) DBHost() string

DBHost is host:port with everything else stripped -- no scheme, no path and in particular no credentials, because the result is shown in the admin view.

func (*PG) DeleteCourse added in v3.46.0

func (db *PG) 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.

func (*PG) DeleteUserAccess added in v3.48.0

func (db *PG) DeleteUserAccess(ctx context.Context, email string) (bool, error)

DeleteUserAccess removes the row, so the person is back to "never asked". It reports whether there was a row.

func (*PG) DeleteUserGitLabToken added in v3.46.0

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

DeleteUserGitLabToken removes only the GitLab PAT from a user's secrets; the row stays. A user who never had one is not an error.

func (*PG) Disconnect added in v3.46.0

func (db *PG) Disconnect(_ context.Context) error

Disconnect closes the pool. Named to match the Mongo store so bootstrap does not have to know which one it holds.

func (*PG) EnsureSecretsRowForImport added in v3.46.0

func (db *PG) EnsureSecretsRowForImport(ctx context.Context, owner string) error

EnsureSecretsRowForImport creates a secrets row that holds no token.

MongoDB's token deletion unsets the fields and keeps the document, so a user who once had a token and removed it still has a row. Functionally the two are the same -- every caller checks `s == nil || s.GitLab == nil` -- but a migration that silently drops rows is the wrong precedent, and the schema comment on user_secrets says the row is deliberately kept. Carrying it across also lets the row counts be compared without an exception.

func (*PG) EventsBetween added in v3.46.0

func (db *PG) EventsBetween(ctx context.Context, since, until time.Time) ([]*Event, error)

EventsBetween returns every event in [since, until), oldest first -- the window the nightly digest aggregates. Cross-user by design.

func (*PG) FinishJob added in v3.46.0

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

FinishJob records a terminal state (done/failed/expired) with its log and error. An id that no longer exists matches nothing and is not an error.

func (*PG) GetUserAccess added in v3.48.0

func (db *PG) GetUserAccess(ctx context.Context, email string) (*UserAccess, error)

GetUserAccess returns the access row for an email, or nil when the person never asked.

func (*PG) GetUserSecret added in v3.46.0

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

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

(nil, nil) rather than a not-found error: every caller reads a nil result as "no token configured", which is the normal state on a first visit.

func (*PG) InsertAccessRequest added in v3.48.0

func (db *PG) InsertAccessRequest(ctx context.Context, u *UserAccess) (created bool, err error)

InsertAccessRequest stores a pending request. created is false when a row for the email already existed; the existing row is then left untouched, which is what makes a repeated request harmless.

func (*PG) JobOf added in v3.46.0

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

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

func (*PG) JobsOf added in v3.46.0

func (db *PG) 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 (*PG) ListUserAccess added in v3.48.0

func (db *PG) ListUserAccess(ctx context.Context) ([]*UserAccess, error)

ListUserAccess returns every row, open requests first.

func (*PG) MarkNotified added in v3.46.0

func (db *PG) 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 (*PG) MigrateSchema added in v3.46.0

func (db *PG) MigrateSchema(ctx context.Context) error

MigrateSchema brings the connected database up to the schema this binary carries. It is what the server calls at startup.

func (*PG) ReapExpired added in v3.46.0

func (db *PG) ReapExpired(ctx context.Context, now time.Time) (jobs, events int64, err error)

ReapExpired deletes what MongoDB's TTL indexes used to delete: finished jobs after 30 days, events after 180.

PostgreSQL has no TTL, and pg_cron would mean an extension with shared_preload_libraries and therefore a custom image. A delete on the runner's existing tick is less machinery and has three advantages over the TTL monitor it replaces: it is testable, it shows up in the log, and the retention period sits in Go next to the comment explaining it rather than inside an index option.

func (*PG) RecentEvents added in v3.46.0

func (db *PG) RecentEvents(ctx context.Context, since time.Time, limit int64) ([]*Event, error)

RecentEvents returns events at or after since, newest first, capped at limit -- the admin page's live feed. A limit of zero or less means no cap, matching the Mongo store. Cross-user by design.

func (*PG) RecordActivity added in v3.46.0

func (db *PG) RecordActivity(ctx context.Context, e *ActivityEntry) error

RecordActivity appends one entry to the log.

func (*PG) RecordEvent added in v3.46.0

func (db *PG) RecordEvent(ctx context.Context, e *Event) error

RecordEvent appends one event to the log. A missing severity defaults to info so a forgotten field never hides an event -- and, as before, the default is written back onto the caller's struct.

func (*PG) SaveCourse added in v3.46.0

func (db *PG) SaveCourse(ctx context.Context, course *StoredCourse) error

SaveCourse inserts or replaces a course for its owner.

func (*PG) SaveJob added in v3.46.0

func (db *PG) SaveJob(ctx context.Context, job *ScheduledJob) error

SaveJob inserts a new job, assigning it an id if it has none.

func (*PG) SaveUserGitLabToken added in v3.46.0

func (db *PG) 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 columns so it never clobbers other secrets on the row.

func (*PG) SetSummarySentAt added in v3.46.0

func (db *PG) SetSummarySentAt(ctx context.Context, at time.Time) error

SetSummarySentAt records when the nightly summary was last sent.

func (*PG) SetUserAccessStatus added in v3.48.0

func (db *PG) SetUserAccessStatus(ctx context.Context, email, status, decidedBy string, decidedAt time.Time) (*UserAccess, error)

SetUserAccessStatus records an admin's decision and returns the updated row, or nil when there is no row for the email.

func (*PG) SystemState added in v3.46.0

func (db *PG) SystemState(ctx context.Context) (*SystemState, error)

SystemState returns the single state row, or a zero-valued one (never nil) if it somehow does not exist.

The migration seeds the row, so the not-found branch should be unreachable -- it stays because the caller's contract is "never nil", and the nightly digest asks for this on every tick including the first one after a fresh install.

func (*PG) TruncateAllForImport added in v3.46.0

func (db *PG) TruncateAllForImport(ctx context.Context) error

TruncateAllForImport empties every table so an aborted import can simply be run again.

Rerunning is what makes the cut-over rehearsable, and truncate-then-load is the only honest way to get it here: activity and events have no natural key, so there is nothing for an upsert to conflict on, and a second run would silently double every log entry.

system_state is re-seeded afterwards, because the migration that created it only runs once.

func (*PG) UnnotifiedTerminalJobs added in v3.46.0

func (db *PG) UnnotifiedTerminalJobs(ctx context.Context) ([]*ScheduledJob, error)

UnnotifiedTerminalJobs returns finished jobs (done/failed/expired) whose notification email has not been sent yet, across all owners.

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 SystemState added in v3.40.0

type SystemState struct {
	ID            string     `bson:"_id"`
	SummarySentAt *time.Time `bson:"summarySentAt,omitempty"`
}

SystemState holds server-wide bookkeeping. Today it records only when the last nightly summary was sent, so the digest window survives restarts and a summary is never sent twice for the same period.

type UserAccess added in v3.48.0

type UserAccess struct {
	Email       string
	Name        string
	Department  string
	Status      string
	Reason      string
	RequestedAt time.Time
	DecidedAt   *time.Time
	DecidedBy   string
}

UserAccess is one person who asked to use glabs-web, and what an admin decided. PostgreSQL only: there is no MongoDB implementation, as with ReapExpired.

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.

Directories

Path Synopsis
Package storetest is the contract every glabs-web store implementation has to satisfy, written once and run against each of them.
Package storetest is the contract every glabs-web store implementation has to satisfy, written once and run against each of them.

Jump to

Keyboard shortcuts

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