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
- func LooksLikeMongoURI(uri string) bool
- func MigratePG(ctx context.Context, pool *pgxpool.Pool) error
- func MigrationsChecksum() (string, error)
- type ActivityEntry
- type DB
- func (db *DB) ActivityFor(ctx context.Context, owner, course, assignment string) ([]*ActivityEntry, error)
- func (db *DB) AllActivityFor(ctx context.Context, owner 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) 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) EnsureEventIndexes(ctx context.Context) error
- func (db *DB) EnsureJobIndexes(ctx context.Context) error
- func (db *DB) EnsureUserSecretIndexes(ctx context.Context) error
- func (db *DB) EventsBetween(ctx context.Context, since, until time.Time) ([]*Event, error)
- func (db *DB) FinishJob(ctx context.Context, id, status, logText, errText string) 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) RecentEvents(ctx context.Context, since time.Time, limit int64) ([]*Event, error)
- func (db *DB) RecordActivity(ctx context.Context, e *ActivityEntry) error
- func (db *DB) RecordEvent(ctx context.Context, e *Event) error
- func (db *DB) SaveCourse(ctx context.Context, course *StoredCourse) error
- func (db *DB) SaveJob(ctx context.Context, job *ScheduledJob) error
- func (db *DB) SaveUserGitLabToken(ctx context.Context, owner string, sealed secrets.SealedValue, ...) error
- func (db *DB) SetSummarySentAt(ctx context.Context, at time.Time) error
- func (db *DB) SystemState(ctx context.Context) (*SystemState, error)
- func (db *DB) UnnotifiedTerminalJobs(ctx context.Context) ([]*ScheduledJob, error)
- type Event
- type PG
- func (db *PG) ActivityFor(ctx context.Context, owner, course, assignment string) ([]*ActivityEntry, error)
- func (db *PG) AllActivityFor(ctx context.Context, owner string) ([]*ActivityEntry, error)
- func (db *PG) CancelJob(ctx context.Context, owner, id string) (*ScheduledJob, error)
- func (db *PG) ClaimDueJob(ctx context.Context, workerID string, now time.Time) (*ScheduledJob, error)
- func (db *PG) CountsForImport(ctx context.Context) (map[string]int64, error)
- func (db *PG) CourseActivityFor(ctx context.Context, owner, course string) ([]*ActivityEntry, error)
- func (db *PG) CourseOf(ctx context.Context, owner, name string) (*StoredCourse, error)
- func (db *PG) CoursesOf(ctx context.Context, owner string) ([]*StoredCourse, error)
- func (db *PG) DBHost() string
- func (db *PG) DeleteCourse(ctx context.Context, owner, name string) error
- func (db *PG) DeleteUserAccess(ctx context.Context, email string) (bool, error)
- func (db *PG) DeleteUserGitLabToken(ctx context.Context, owner string) error
- func (db *PG) Disconnect(_ context.Context) error
- func (db *PG) EnsureSecretsRowForImport(ctx context.Context, owner string) error
- func (db *PG) EventsBetween(ctx context.Context, since, until time.Time) ([]*Event, error)
- func (db *PG) FinishJob(ctx context.Context, id, status, logText, errText string) error
- func (db *PG) GetUserAccess(ctx context.Context, email string) (*UserAccess, error)
- func (db *PG) GetUserSecret(ctx context.Context, owner string) (*UserSecret, error)
- func (db *PG) InsertAccessRequest(ctx context.Context, u *UserAccess) (created bool, err error)
- func (db *PG) JobOf(ctx context.Context, owner, id string) (*ScheduledJob, error)
- func (db *PG) JobsOf(ctx context.Context, owner string, statuses []string) ([]*ScheduledJob, error)
- func (db *PG) ListUserAccess(ctx context.Context) ([]*UserAccess, error)
- func (db *PG) MarkNotified(ctx context.Context, id string) error
- func (db *PG) MigrateSchema(ctx context.Context) error
- func (db *PG) ReapExpired(ctx context.Context, now time.Time) (jobs, events int64, err error)
- func (db *PG) RecentEvents(ctx context.Context, since time.Time, limit int64) ([]*Event, error)
- func (db *PG) RecordActivity(ctx context.Context, e *ActivityEntry) error
- func (db *PG) RecordEvent(ctx context.Context, e *Event) error
- func (db *PG) SaveCourse(ctx context.Context, course *StoredCourse) error
- func (db *PG) SaveJob(ctx context.Context, job *ScheduledJob) error
- func (db *PG) SaveUserGitLabToken(ctx context.Context, owner string, sealed secrets.SealedValue, ...) error
- func (db *PG) SetSummarySentAt(ctx context.Context, at time.Time) error
- func (db *PG) SetUserAccessStatus(ctx context.Context, email, status, decidedBy string, decidedAt time.Time) (*UserAccess, error)
- func (db *PG) SystemState(ctx context.Context) (*SystemState, error)
- func (db *PG) TruncateAllForImport(ctx context.Context) error
- func (db *PG) UnnotifiedTerminalJobs(ctx context.Context) ([]*ScheduledJob, error)
- type ScheduledJob
- type StoredCourse
- type SystemState
- type UserAccess
- type UserSecret
Constants ¶
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.
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.
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.
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 ¶
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.
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
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
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
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 ¶
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
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
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
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) EnsureEventIndexes ¶ added in v3.40.0
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
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
EnsureUserSecretIndexes makes owner unique — one secrets document per user.
func (*DB) EventsBetween ¶ added in v3.40.0
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
FinishJob records a terminal state (done/failed/expired) with its log and 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) RecentEvents ¶ added in v3.40.0
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
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
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
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
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
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
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
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
CoursesOf returns the courses owned by the given user, sorted by name.
func (*PG) DBHost ¶ added in v3.46.0
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
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
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
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
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
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
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
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
GetUserAccess returns the access row for an email, or nil when the person never asked.
func (*PG) GetUserSecret ¶ added in v3.46.0
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
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) JobsOf ¶ added in v3.46.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 (*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
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
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
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
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
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
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
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.
Source Files
¶
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. |