detached de0845e

feat(permission): add SQLite repository implementation

Add SQLite migration for permissions table.
Add SQLC queries for permission CRUD operations.
Implement PermissionRepository with Get, Set, Delete, GetAll methods.
Update models and querier for permission support.
commit
de0845e529a9858c77efdf919a8dc7e79d377411
author
brice · · 2026-01-30 05:01 UTC
committer
brice · · 2026-02-07 11:53 UTC
parent
13fbcf4
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
6 files · +267 -0
A internal/infrastructure/persistence/sqlite/migrations/007_permissions.sql +17 -0
@@ -0,0 +1,17 @@
1+-- +goose Up
2+-- Permission storage for media access (mic, camera, etc.)
3+-- Per W3C spec: display capture permissions cannot be persisted
4+
5+CREATE TABLE IF NOT EXISTS permissions (
6+ origin TEXT NOT NULL,
7+ permission_type TEXT NOT NULL,
8+ decision TEXT NOT NULL CHECK(decision IN ('granted', 'denied')),
9+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10+ PRIMARY KEY (origin, permission_type)
11+);
12+
13+CREATE INDEX IF NOT EXISTS idx_permissions_origin ON permissions(origin);
14+CREATE INDEX IF NOT EXISTS idx_permissions_updated_at ON permissions(updated_at);
15+
16+-- +goose Down
17+DROP TABLE IF EXISTS permissions;
@@ -0,0 +1,17 @@
1+-- +goose Up
2+-- Permission storage for media access (mic, camera, etc.)
3+-- Per W3C spec: display capture permissions cannot be persisted
4+
5+CREATE TABLE IF NOT EXISTS permissions (
6+ origin TEXT NOT NULL,
7+ permission_type TEXT NOT NULL,
8+ decision TEXT NOT NULL CHECK(decision IN ('granted', 'denied')),
9+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10+ PRIMARY KEY (origin, permission_type)
11+);
12+
13+CREATE INDEX IF NOT EXISTS idx_permissions_origin ON permissions(origin);
14+CREATE INDEX IF NOT EXISTS idx_permissions_updated_at ON permissions(updated_at);
15+
16+-- +goose Down
17+DROP TABLE IF EXISTS permissions;
A internal/infrastructure/persistence/sqlite/permission_repo.go +92 -0
@@ -0,0 +1,92 @@
1+package sqlite
2+
3+import (
4+ "context"
5+ "database/sql"
6+ "errors"
7+
8+ "github.com/bnema/dumber/internal/domain/entity"
9+ "github.com/bnema/dumber/internal/domain/repository"
10+ "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite/sqlc"
11+ "github.com/bnema/dumber/internal/logging"
12+)
13+
14+type permissionRepo struct {
15+ queries *sqlc.Queries
16+}
17+
18+// NewPermissionRepository creates a new SQLite-backed permission repository.
19+func NewPermissionRepository(db *sql.DB) repository.PermissionRepository {
20+ return &permissionRepo{queries: sqlc.New(db)}
21+}
22+
23+func (r *permissionRepo) Get(ctx context.Context, origin string, permType entity.PermissionType) (*entity.PermissionRecord, error) {
24+ log := logging.FromContext(ctx)
25+ log.Debug().Str("origin", origin).Str("type", string(permType)).Msg("getting permission")
26+
27+ row, err := r.queries.GetPermission(ctx, sqlc.GetPermissionParams{
28+ Origin: origin,
29+ PermissionType: string(permType),
30+ })
31+ if err != nil {
32+ if errors.Is(err, sql.ErrNoRows) {
33+ return nil, nil
34+ }
35+ return nil, err
36+ }
37+
38+ return permissionFromRow(row), nil
39+}
40+
41+func (r *permissionRepo) Set(ctx context.Context, record *entity.PermissionRecord) error {
42+ log := logging.FromContext(ctx)
43+ log.Debug().
44+ Str("origin", record.Origin).
45+ Str("type", string(record.Type)).
46+ Str("decision", string(record.Decision)).
47+ Msg("setting permission")
48+
49+ return r.queries.SetPermission(ctx, sqlc.SetPermissionParams{
50+ Origin: record.Origin,
51+ PermissionType: string(record.Type),
52+ Decision: string(record.Decision),
53+ })
54+}
55+
56+func (r *permissionRepo) Delete(ctx context.Context, origin string, permType entity.PermissionType) error {
57+ log := logging.FromContext(ctx)
58+ log.Debug().Str("origin", origin).Str("type", string(permType)).Msg("deleting permission")
59+
60+ return r.queries.DeletePermission(ctx, sqlc.DeletePermissionParams{
61+ Origin: origin,
62+ PermissionType: string(permType),
63+ })
64+}
65+
66+func (r *permissionRepo) GetAll(ctx context.Context, origin string) ([]*entity.PermissionRecord, error) {
67+ log := logging.FromContext(ctx)
68+ log.Debug().Str("origin", origin).Msg("getting all permissions for origin")
69+
70+ rows, err := r.queries.ListPermissionsByOrigin(ctx, origin)
71+ if err != nil {
72+ return nil, err
73+ }
74+
75+ records := make([]*entity.PermissionRecord, len(rows))
76+ for i, row := range rows {
77+ records[i] = permissionFromRow(row)
78+ }
79+ return records, nil
80+}
81+
82+func permissionFromRow(row sqlc.Permission) *entity.PermissionRecord {
83+ record := &entity.PermissionRecord{
84+ Origin: row.Origin,
85+ Type: entity.PermissionType(row.PermissionType),
86+ Decision: entity.PermissionDecision(row.Decision),
87+ }
88+ if row.UpdatedAt.Valid {
89+ record.UpdatedAt = row.UpdatedAt.Time
90+ }
91+ return record
92+}
@@ -0,0 +1,92 @@
1+package sqlite
2+
3+import (
4+ "context"
5+ "database/sql"
6+ "errors"
7+
8+ "github.com/bnema/dumber/internal/domain/entity"
9+ "github.com/bnema/dumber/internal/domain/repository"
10+ "github.com/bnema/dumber/internal/infrastructure/persistence/sqlite/sqlc"
11+ "github.com/bnema/dumber/internal/logging"
12+)
13+
14+type permissionRepo struct {
15+ queries *sqlc.Queries
16+}
17+
18+// NewPermissionRepository creates a new SQLite-backed permission repository.
19+func NewPermissionRepository(db *sql.DB) repository.PermissionRepository {
20+ return &permissionRepo{queries: sqlc.New(db)}
21+}
22+
23+func (r *permissionRepo) Get(ctx context.Context, origin string, permType entity.PermissionType) (*entity.PermissionRecord, error) {
24+ log := logging.FromContext(ctx)
25+ log.Debug().Str("origin", origin).Str("type", string(permType)).Msg("getting permission")
26+
27+ row, err := r.queries.GetPermission(ctx, sqlc.GetPermissionParams{
28+ Origin: origin,
29+ PermissionType: string(permType),
30+ })
31+ if err != nil {
32+ if errors.Is(err, sql.ErrNoRows) {
33+ return nil, nil
34+ }
35+ return nil, err
36+ }
37+
38+ return permissionFromRow(row), nil
39+}
40+
41+func (r *permissionRepo) Set(ctx context.Context, record *entity.PermissionRecord) error {
42+ log := logging.FromContext(ctx)
43+ log.Debug().
44+ Str("origin", record.Origin).
45+ Str("type", string(record.Type)).
46+ Str("decision", string(record.Decision)).
47+ Msg("setting permission")
48+
49+ return r.queries.SetPermission(ctx, sqlc.SetPermissionParams{
50+ Origin: record.Origin,
51+ PermissionType: string(record.Type),
52+ Decision: string(record.Decision),
53+ })
54+}
55+
56+func (r *permissionRepo) Delete(ctx context.Context, origin string, permType entity.PermissionType) error {
57+ log := logging.FromContext(ctx)
58+ log.Debug().Str("origin", origin).Str("type", string(permType)).Msg("deleting permission")
59+
60+ return r.queries.DeletePermission(ctx, sqlc.DeletePermissionParams{
61+ Origin: origin,
62+ PermissionType: string(permType),
63+ })
64+}
65+
66+func (r *permissionRepo) GetAll(ctx context.Context, origin string) ([]*entity.PermissionRecord, error) {
67+ log := logging.FromContext(ctx)
68+ log.Debug().Str("origin", origin).Msg("getting all permissions for origin")
69+
70+ rows, err := r.queries.ListPermissionsByOrigin(ctx, origin)
71+ if err != nil {
72+ return nil, err
73+ }
74+
75+ records := make([]*entity.PermissionRecord, len(rows))
76+ for i, row := range rows {
77+ records[i] = permissionFromRow(row)
78+ }
79+ return records, nil
80+}
81+
82+func permissionFromRow(row sqlc.Permission) *entity.PermissionRecord {
83+ record := &entity.PermissionRecord{
84+ Origin: row.Origin,
85+ Type: entity.PermissionType(row.PermissionType),
86+ Decision: entity.PermissionDecision(row.Decision),
87+ }
88+ if row.UpdatedAt.Valid {
89+ record.UpdatedAt = row.UpdatedAt.Time
90+ }
91+ return record
92+}
A internal/infrastructure/persistence/sqlite/queries/permissions.sql +18 -0
@@ -0,0 +1,18 @@
1+-- name: GetPermission :one
2+SELECT * FROM permissions WHERE origin = ? AND permission_type = ? LIMIT 1;
3+
4+-- name: SetPermission :exec
5+INSERT INTO permissions (origin, permission_type, decision, updated_at)
6+VALUES (?, ?, ?, CURRENT_TIMESTAMP)
7+ON CONFLICT(origin, permission_type) DO UPDATE SET
8+ decision = excluded.decision,
9+ updated_at = CURRENT_TIMESTAMP;
10+
11+-- name: DeletePermission :exec
12+DELETE FROM permissions WHERE origin = ? AND permission_type = ?;
13+
14+-- name: ListPermissionsByOrigin :many
15+SELECT * FROM permissions WHERE origin = ? ORDER BY updated_at DESC;
16+
17+-- name: ListAllPermissions :many
18+SELECT * FROM permissions ORDER BY origin, permission_type;
@@ -0,0 +1,18 @@
1+-- name: GetPermission :one
2+SELECT * FROM permissions WHERE origin = ? AND permission_type = ? LIMIT 1;
3+
4+-- name: SetPermission :exec
5+INSERT INTO permissions (origin, permission_type, decision, updated_at)
6+VALUES (?, ?, ?, CURRENT_TIMESTAMP)
7+ON CONFLICT(origin, permission_type) DO UPDATE SET
8+ decision = excluded.decision,
9+ updated_at = CURRENT_TIMESTAMP;
10+
11+-- name: DeletePermission :exec
12+DELETE FROM permissions WHERE origin = ? AND permission_type = ?;
13+
14+-- name: ListPermissionsByOrigin :many
15+SELECT * FROM permissions WHERE origin = ? ORDER BY updated_at DESC;
16+
17+-- name: ListAllPermissions :many
18+SELECT * FROM permissions ORDER BY origin, permission_type;
M internal/infrastructure/persistence/sqlite/sqlc/models.go +7 -0
@@ -63,6 +63,13 @@ type HistoryFt struct {
6363 Title string `json:"title"`
6464 }
6565
66+type Permission struct {
67+ Origin string `json:"origin"`
68+ PermissionType string `json:"permission_type"`
69+ Decision string `json:"decision"`
70+ UpdatedAt sql.NullTime `json:"updated_at"`
71+}
72+
6673 type Session struct {
6774 ID string `json:"id"`
6875 Type string `json:"type"`
@@ -63,6 +63,13 @@ type HistoryFt struct {
63 Title string `json:"title"`63 Title string `json:"title"`
64 }64 }
65 65
66+type Permission struct {
67+ Origin string `json:"origin"`
68+ PermissionType string `json:"permission_type"`
69+ Decision string `json:"decision"`
70+ UpdatedAt sql.NullTime `json:"updated_at"`
71+}
72+
66 type Session struct {73 type Session struct {
67 ID string `json:"id"`74 ID string `json:"id"`
68 Type string `json:"type"`75 Type string `json:"type"`
A internal/infrastructure/persistence/sqlite/sqlc/permissions.sql.go +128 -0
@@ -0,0 +1,128 @@
1+// Code generated by sqlc. DO NOT EDIT.
2+// versions:
3+// sqlc v1.30.0
4+// source: permissions.sql
5+
6+package sqlc
7+
8+import (
9+ "context"
10+)
11+
12+const DeletePermission = `-- name: DeletePermission :exec
13+DELETE FROM permissions WHERE origin = ? AND permission_type = ?
14+`
15+
16+type DeletePermissionParams struct {
17+ Origin string `json:"origin"`
18+ PermissionType string `json:"permission_type"`
19+}
20+
21+func (q *Queries) DeletePermission(ctx context.Context, arg DeletePermissionParams) error {
22+ _, err := q.db.ExecContext(ctx, DeletePermission, arg.Origin, arg.PermissionType)
23+ return err
24+}
25+
26+const GetPermission = `-- name: GetPermission :one
27+SELECT origin, permission_type, decision, updated_at FROM permissions WHERE origin = ? AND permission_type = ? LIMIT 1
28+`
29+
30+type GetPermissionParams struct {
31+ Origin string `json:"origin"`
32+ PermissionType string `json:"permission_type"`
33+}
34+
35+func (q *Queries) GetPermission(ctx context.Context, arg GetPermissionParams) (Permission, error) {
36+ row := q.db.QueryRowContext(ctx, GetPermission, arg.Origin, arg.PermissionType)
37+ var i Permission
38+ err := row.Scan(
39+ &i.Origin,
40+ &i.PermissionType,
41+ &i.Decision,
42+ &i.UpdatedAt,
43+ )
44+ return i, err
45+}
46+
47+const ListAllPermissions = `-- name: ListAllPermissions :many
48+SELECT origin, permission_type, decision, updated_at FROM permissions ORDER BY origin, permission_type
49+`
50+
51+func (q *Queries) ListAllPermissions(ctx context.Context) ([]Permission, error) {
52+ rows, err := q.db.QueryContext(ctx, ListAllPermissions)
53+ if err != nil {
54+ return nil, err
55+ }
56+ defer rows.Close()
57+ items := []Permission{}
58+ for rows.Next() {
59+ var i Permission
60+ if err := rows.Scan(
61+ &i.Origin,
62+ &i.PermissionType,
63+ &i.Decision,
64+ &i.UpdatedAt,
65+ ); err != nil {
66+ return nil, err
67+ }
68+ items = append(items, i)
69+ }
70+ if err := rows.Close(); err != nil {
71+ return nil, err
72+ }
73+ if err := rows.Err(); err != nil {
74+ return nil, err
75+ }
76+ return items, nil
77+}
78+
79+const ListPermissionsByOrigin = `-- name: ListPermissionsByOrigin :many
80+SELECT origin, permission_type, decision, updated_at FROM permissions WHERE origin = ? ORDER BY updated_at DESC
81+`
82+
83+func (q *Queries) ListPermissionsByOrigin(ctx context.Context, origin string) ([]Permission, error) {
84+ rows, err := q.db.QueryContext(ctx, ListPermissionsByOrigin, origin)
85+ if err != nil {
86+ return nil, err
87+ }
88+ defer rows.Close()
89+ items := []Permission{}
90+ for rows.Next() {
91+ var i Permission
92+ if err := rows.Scan(
93+ &i.Origin,
94+ &i.PermissionType,
95+ &i.Decision,
96+ &i.UpdatedAt,
97+ ); err != nil {
98+ return nil, err
99+ }
100+ items = append(items, i)
101+ }
102+ if err := rows.Close(); err != nil {
103+ return nil, err
104+ }
105+ if err := rows.Err(); err != nil {
106+ return nil, err
107+ }
108+ return items, nil
109+}
110+
111+const SetPermission = `-- name: SetPermission :exec
112+INSERT INTO permissions (origin, permission_type, decision, updated_at)
113+VALUES (?, ?, ?, CURRENT_TIMESTAMP)
114+ON CONFLICT(origin, permission_type) DO UPDATE SET
115+ decision = excluded.decision,
116+ updated_at = CURRENT_TIMESTAMP
117+`
118+
119+type SetPermissionParams struct {
120+ Origin string `json:"origin"`
121+ PermissionType string `json:"permission_type"`
122+ Decision string `json:"decision"`
123+}
124+
125+func (q *Queries) SetPermission(ctx context.Context, arg SetPermissionParams) error {
126+ _, err := q.db.ExecContext(ctx, SetPermission, arg.Origin, arg.PermissionType, arg.Decision)
127+ return err
128+}
@@ -0,0 +1,128 @@
1+// Code generated by sqlc. DO NOT EDIT.
2+// versions:
3+// sqlc v1.30.0
4+// source: permissions.sql
5+
6+package sqlc
7+
8+import (
9+ "context"
10+)
11+
12+const DeletePermission = `-- name: DeletePermission :exec
13+DELETE FROM permissions WHERE origin = ? AND permission_type = ?
14+`
15+
16+type DeletePermissionParams struct {
17+ Origin string `json:"origin"`
18+ PermissionType string `json:"permission_type"`
19+}
20+
21+func (q *Queries) DeletePermission(ctx context.Context, arg DeletePermissionParams) error {
22+ _, err := q.db.ExecContext(ctx, DeletePermission, arg.Origin, arg.PermissionType)
23+ return err
24+}
25+
26+const GetPermission = `-- name: GetPermission :one
27+SELECT origin, permission_type, decision, updated_at FROM permissions WHERE origin = ? AND permission_type = ? LIMIT 1
28+`
29+
30+type GetPermissionParams struct {
31+ Origin string `json:"origin"`
32+ PermissionType string `json:"permission_type"`
33+}
34+
35+func (q *Queries) GetPermission(ctx context.Context, arg GetPermissionParams) (Permission, error) {
36+ row := q.db.QueryRowContext(ctx, GetPermission, arg.Origin, arg.PermissionType)
37+ var i Permission
38+ err := row.Scan(
39+ &i.Origin,
40+ &i.PermissionType,
41+ &i.Decision,
42+ &i.UpdatedAt,
43+ )
44+ return i, err
45+}
46+
47+const ListAllPermissions = `-- name: ListAllPermissions :many
48+SELECT origin, permission_type, decision, updated_at FROM permissions ORDER BY origin, permission_type
49+`
50+
51+func (q *Queries) ListAllPermissions(ctx context.Context) ([]Permission, error) {
52+ rows, err := q.db.QueryContext(ctx, ListAllPermissions)
53+ if err != nil {
54+ return nil, err
55+ }
56+ defer rows.Close()
57+ items := []Permission{}
58+ for rows.Next() {
59+ var i Permission
60+ if err := rows.Scan(
61+ &i.Origin,
62+ &i.PermissionType,
63+ &i.Decision,
64+ &i.UpdatedAt,
65+ ); err != nil {
66+ return nil, err
67+ }
68+ items = append(items, i)
69+ }
70+ if err := rows.Close(); err != nil {
71+ return nil, err
72+ }
73+ if err := rows.Err(); err != nil {
74+ return nil, err
75+ }
76+ return items, nil
77+}
78+
79+const ListPermissionsByOrigin = `-- name: ListPermissionsByOrigin :many
80+SELECT origin, permission_type, decision, updated_at FROM permissions WHERE origin = ? ORDER BY updated_at DESC
81+`
82+
83+func (q *Queries) ListPermissionsByOrigin(ctx context.Context, origin string) ([]Permission, error) {
84+ rows, err := q.db.QueryContext(ctx, ListPermissionsByOrigin, origin)
85+ if err != nil {
86+ return nil, err
87+ }
88+ defer rows.Close()
89+ items := []Permission{}
90+ for rows.Next() {
91+ var i Permission
92+ if err := rows.Scan(
93+ &i.Origin,
94+ &i.PermissionType,
95+ &i.Decision,
96+ &i.UpdatedAt,
97+ ); err != nil {
98+ return nil, err
99+ }
100+ items = append(items, i)
101+ }
102+ if err := rows.Close(); err != nil {
103+ return nil, err
104+ }
105+ if err := rows.Err(); err != nil {
106+ return nil, err
107+ }
108+ return items, nil
109+}
110+
111+const SetPermission = `-- name: SetPermission :exec
112+INSERT INTO permissions (origin, permission_type, decision, updated_at)
113+VALUES (?, ?, ?, CURRENT_TIMESTAMP)
114+ON CONFLICT(origin, permission_type) DO UPDATE SET
115+ decision = excluded.decision,
116+ updated_at = CURRENT_TIMESTAMP
117+`
118+
119+type SetPermissionParams struct {
120+ Origin string `json:"origin"`
121+ PermissionType string `json:"permission_type"`
122+ Decision string `json:"decision"`
123+}
124+
125+func (q *Queries) SetPermission(ctx context.Context, arg SetPermissionParams) error {
126+ _, err := q.db.ExecContext(ctx, SetPermission, arg.Origin, arg.PermissionType, arg.Decision)
127+ return err
128+}
M internal/infrastructure/persistence/sqlite/sqlc/querier.go +5 -0
@@ -26,6 +26,7 @@ type Querier interface {
2626 DeleteHistoryOlderThan(ctx context.Context, lastVisited sql.NullTime) error
2727 // Deletes exited browser sessions beyond the keep limit, keeping the most recent ones.
2828 DeleteOldestExitedSessions(ctx context.Context, offset int64) (int64, error)
29+ DeletePermission(ctx context.Context, arg DeletePermissionParams) error
2930 DeleteSession(ctx context.Context, id string) error
3031 DeleteSessionState(ctx context.Context, sessionID string) error
3132 DeleteTag(ctx context.Context, id int64) error
@@ -51,6 +52,7 @@ type Querier interface {
5152 GetHistoryStats(ctx context.Context) (GetHistoryStatsRow, error)
5253 GetHourlyDistribution(ctx context.Context) ([]GetHourlyDistributionRow, error)
5354 GetMostVisited(ctx context.Context, datetime interface{}) ([]History, error)
55+ GetPermission(ctx context.Context, arg GetPermissionParams) (Permission, error)
5456 GetRecentHistory(ctx context.Context, arg GetRecentHistoryParams) ([]History, error)
5557 GetRecentHistorySince(ctx context.Context, datetime interface{}) ([]History, error)
5658 GetRecentSessions(ctx context.Context, limit int64) ([]Session, error)
@@ -67,6 +69,8 @@ type Querier interface {
6769 IncrementVisitCountByDelta(ctx context.Context, arg IncrementVisitCountByDeltaParams) error
6870 InsertSession(ctx context.Context, arg InsertSessionParams) error
6971 IsWhitelisted(ctx context.Context, domain string) (int64, error)
72+ ListAllPermissions(ctx context.Context) ([]Permission, error)
73+ ListPermissionsByOrigin(ctx context.Context, origin string) ([]Permission, error)
7074 ListZoomLevels(ctx context.Context) ([]ZoomLevel, error)
7175 MarkSessionEnded(ctx context.Context, arg MarkSessionEndedParams) error
7276 RemoveFromWhitelist(ctx context.Context, domain string) error
@@ -77,6 +81,7 @@ type Querier interface {
7781 SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error)
7882 SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error
7983 SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error
84+ SetPermission(ctx context.Context, arg SetPermissionParams) error
8085 SetZoomLevel(ctx context.Context, arg SetZoomLevelParams) error
8186 UpdateFavorite(ctx context.Context, arg UpdateFavoriteParams) error
8287 UpdateFavoritePosition(ctx context.Context, arg UpdateFavoritePositionParams) error
@@ -26,6 +26,7 @@ type Querier interface {
26 DeleteHistoryOlderThan(ctx context.Context, lastVisited sql.NullTime) error26 DeleteHistoryOlderThan(ctx context.Context, lastVisited sql.NullTime) error
27 // Deletes exited browser sessions beyond the keep limit, keeping the most recent ones.27 // Deletes exited browser sessions beyond the keep limit, keeping the most recent ones.
28 DeleteOldestExitedSessions(ctx context.Context, offset int64) (int64, error)28 DeleteOldestExitedSessions(ctx context.Context, offset int64) (int64, error)
29+ DeletePermission(ctx context.Context, arg DeletePermissionParams) error
29 DeleteSession(ctx context.Context, id string) error30 DeleteSession(ctx context.Context, id string) error
30 DeleteSessionState(ctx context.Context, sessionID string) error31 DeleteSessionState(ctx context.Context, sessionID string) error
31 DeleteTag(ctx context.Context, id int64) error32 DeleteTag(ctx context.Context, id int64) error
@@ -51,6 +52,7 @@ type Querier interface {
51 GetHistoryStats(ctx context.Context) (GetHistoryStatsRow, error)52 GetHistoryStats(ctx context.Context) (GetHistoryStatsRow, error)
52 GetHourlyDistribution(ctx context.Context) ([]GetHourlyDistributionRow, error)53 GetHourlyDistribution(ctx context.Context) ([]GetHourlyDistributionRow, error)
53 GetMostVisited(ctx context.Context, datetime interface{}) ([]History, error)54 GetMostVisited(ctx context.Context, datetime interface{}) ([]History, error)
55+ GetPermission(ctx context.Context, arg GetPermissionParams) (Permission, error)
54 GetRecentHistory(ctx context.Context, arg GetRecentHistoryParams) ([]History, error)56 GetRecentHistory(ctx context.Context, arg GetRecentHistoryParams) ([]History, error)
55 GetRecentHistorySince(ctx context.Context, datetime interface{}) ([]History, error)57 GetRecentHistorySince(ctx context.Context, datetime interface{}) ([]History, error)
56 GetRecentSessions(ctx context.Context, limit int64) ([]Session, error)58 GetRecentSessions(ctx context.Context, limit int64) ([]Session, error)
@@ -67,6 +69,8 @@ type Querier interface {
67 IncrementVisitCountByDelta(ctx context.Context, arg IncrementVisitCountByDeltaParams) error69 IncrementVisitCountByDelta(ctx context.Context, arg IncrementVisitCountByDeltaParams) error
68 InsertSession(ctx context.Context, arg InsertSessionParams) error70 InsertSession(ctx context.Context, arg InsertSessionParams) error
69 IsWhitelisted(ctx context.Context, domain string) (int64, error)71 IsWhitelisted(ctx context.Context, domain string) (int64, error)
72+ ListAllPermissions(ctx context.Context) ([]Permission, error)
73+ ListPermissionsByOrigin(ctx context.Context, origin string) ([]Permission, error)
70 ListZoomLevels(ctx context.Context) ([]ZoomLevel, error)74 ListZoomLevels(ctx context.Context) ([]ZoomLevel, error)
71 MarkSessionEnded(ctx context.Context, arg MarkSessionEndedParams) error75 MarkSessionEnded(ctx context.Context, arg MarkSessionEndedParams) error
72 RemoveFromWhitelist(ctx context.Context, domain string) error76 RemoveFromWhitelist(ctx context.Context, domain string) error
@@ -77,6 +81,7 @@ type Querier interface {
77 SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error)81 SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error)
78 SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error82 SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error
79 SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error83 SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error
84+ SetPermission(ctx context.Context, arg SetPermissionParams) error
80 SetZoomLevel(ctx context.Context, arg SetZoomLevelParams) error85 SetZoomLevel(ctx context.Context, arg SetZoomLevelParams) error
81 UpdateFavorite(ctx context.Context, arg UpdateFavoriteParams) error86 UpdateFavorite(ctx context.Context, arg UpdateFavoriteParams) error
82 UpdateFavoritePosition(ctx context.Context, arg UpdateFavoritePositionParams) error87 UpdateFavoritePosition(ctx context.Context, arg UpdateFavoritePositionParams) error

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close