switched to fully self contained docker

This commit is contained in:
2026-07-26 14:01:20 -05:00
parent 0ed0fb5817
commit 5d2f90ce24
34 changed files with 688 additions and 672 deletions
+2 -2
View File
@@ -216,7 +216,7 @@ func (s *Server) currentTechnician(r *http.Request) (*technician, error) {
r.Context(),
`SELECT gitea_login, display_name, csrf_token
FROM technician_sessions
WHERE token_hash = ? AND expires_at > UTC_TIMESTAMP(6)`,
WHERE token_hash = ? AND expires_at > CURRENT_TIMESTAMP`,
tokenHash[:],
).Scan(&tech.Login, &tech.DisplayName, &tech.CSRFToken)
if err != nil {
@@ -224,7 +224,7 @@ func (s *Server) currentTechnician(r *http.Request) (*technician, error) {
}
_, _ = s.db.ExecContext(
r.Context(),
`UPDATE technician_sessions SET last_seen_at = UTC_TIMESTAMP(6)
`UPDATE technician_sessions SET last_seen_at = CURRENT_TIMESTAMP
WHERE token_hash = ?`,
tokenHash[:],
)
+29 -26
View File
@@ -215,10 +215,10 @@ func auditQuery(filters auditFilters, limit int) (string, []any) {
var arguments []any
timeClauses := map[string]string{
"24h": " AND ae.created_at >= UTC_TIMESTAMP(6) - INTERVAL 1 DAY",
"7d": " AND ae.created_at >= UTC_TIMESTAMP(6) - INTERVAL 7 DAY",
"30d": " AND ae.created_at >= UTC_TIMESTAMP(6) - INTERVAL 30 DAY",
"90d": " AND ae.created_at >= UTC_TIMESTAMP(6) - INTERVAL 90 DAY",
"24h": " AND ae.created_at >= datetime('now', '-1 day')",
"7d": " AND ae.created_at >= datetime('now', '-7 days')",
"30d": " AND ae.created_at >= datetime('now', '-30 days')",
"90d": " AND ae.created_at >= datetime('now', '-90 days')",
}
query += timeClauses[filters.TimeRange]
@@ -240,11 +240,11 @@ func auditQuery(filters auditFilters, limit int) (string, []any) {
if filter.value == "" {
continue
}
query += " AND LOCATE(?, " + filter.column + ") > 0"
query += " AND instr(" + filter.column + ", ?) > 0"
arguments = append(arguments, filter.value)
}
if filters.SourceIP != "" {
query += " AND LOCATE(?, ae.source_ip) > 0"
query += " AND instr(ae.source_ip, ?) > 0"
arguments = append(arguments, filters.SourceIP)
}
query += " ORDER BY ae.created_at DESC LIMIT ?"
@@ -325,21 +325,23 @@ func (s *Server) listAuthorizations(r *http.Request) ([]authorizationRecord, err
WHERE h.authorization_id = a.id),
a.created_at, a.expires_at, a.revoked_at,
COALESCE((
SELECT GROUP_CONCAT(p.display_name
ORDER BY p.display_name SEPARATOR ', ')
FROM authorization_packages ap
JOIN packages p ON p.id = ap.package_id
WHERE ap.authorization_id = a.id
SELECT GROUP_CONCAT(display_name, ', ')
FROM (SELECT p.display_name
FROM authorization_packages ap
JOIN packages p ON p.id = ap.package_id
WHERE ap.authorization_id = a.id
ORDER BY p.display_name)
), ''),
COALESCE((
SELECT GROUP_CONCAT(ia.display_name
ORDER BY ia.sort_order, ia.display_name SEPARATOR ', ')
FROM authorization_actions aa
JOIN installer_actions ia ON ia.slug = aa.action_slug
WHERE aa.authorization_id = a.id
SELECT GROUP_CONCAT(display_name, ', ')
FROM (SELECT ia.display_name
FROM authorization_actions aa
JOIN installer_actions ia ON ia.slug = aa.action_slug
WHERE aa.authorization_id = a.id
ORDER BY ia.sort_order, ia.display_name)
), '')
FROM authorizations a
WHERE a.created_at > UTC_TIMESTAMP(6) - INTERVAL 30 DAY
WHERE a.created_at > datetime('now', '-30 days')
ORDER BY a.created_at DESC
LIMIT 100`,
)
@@ -489,7 +491,7 @@ func (s *Server) handleRevokeAuthorization(w http.ResponseWriter, r *http.Reques
result, err := s.db.ExecContext(
r.Context(),
`UPDATE authorizations
SET revoked_at = COALESCE(revoked_at, UTC_TIMESTAMP(6))
SET revoked_at = COALESCE(revoked_at, CURRENT_TIMESTAMP)
WHERE id = ?`,
id,
)
@@ -504,7 +506,7 @@ func (s *Server) handleRevokeAuthorization(w http.ResponseWriter, r *http.Reques
}
_, _ = s.db.ExecContext(
r.Context(),
`UPDATE download_sessions SET revoked_at = UTC_TIMESTAMP(6)
`UPDATE download_sessions SET revoked_at = CURRENT_TIMESTAMP
WHERE authorization_id = ? AND revoked_at IS NULL`,
id,
)
@@ -568,13 +570,14 @@ func (s *Server) savePackage(
`INSERT INTO packages
(slug, display_name, package_name, package_version, file_name, sha256, enabled)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
display_name = VALUES(display_name),
package_name = VALUES(package_name),
package_version = VALUES(package_version),
file_name = VALUES(file_name),
sha256 = VALUES(sha256),
enabled = VALUES(enabled)`,
ON CONFLICT(slug) DO UPDATE SET
display_name = excluded.display_name,
package_name = excluded.package_name,
package_version = excluded.package_version,
file_name = excluded.file_name,
sha256 = excluded.sha256,
enabled = excluded.enabled,
updated_at = CURRENT_TIMESTAMP`,
slug,
displayName,
packageName,
+4 -2
View File
@@ -37,7 +37,10 @@ func LoadConfig() (Config, error) {
var err error
cfg.ListenAddr = envDefault("TAPM_LISTEN_ADDR", ":8080")
cfg.DatabaseDSN = os.Getenv("TAPM_DATABASE_DSN")
cfg.DatabaseDSN = envDefault(
"TAPM_DATABASE_DSN",
"file:/data/tapm.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)",
)
cfg.GiteaClientID = os.Getenv("TAPM_GITEA_CLIENT_ID")
cfg.GiteaClientSecret = os.Getenv("TAPM_GITEA_CLIENT_SECRET")
cfg.GiteaPackageOwner = envDefault("TAPM_GITEA_PACKAGE_OWNER", "TAI")
@@ -103,7 +106,6 @@ func LoadConfig() (Config, error) {
}
required := map[string]string{
"TAPM_DATABASE_DSN": cfg.DatabaseDSN,
"TAPM_GITEA_CLIENT_ID": cfg.GiteaClientID,
"TAPM_GITEA_CLIENT_SECRET": cfg.GiteaClientSecret,
"TAPM_GITEA_PACKAGE_USERNAME": cfg.GiteaPackageUser,
+5 -6
View File
@@ -101,7 +101,7 @@ func (s *Server) exchangeRateLimited(r *http.Request, sourceIP string) (bool, er
FROM audit_events
WHERE event_type = 'code_exchange_failed'
AND source_ip = ?
AND created_at > UTC_TIMESTAMP(6) - INTERVAL 10 MINUTE`,
AND created_at > datetime('now', '-10 minutes')`,
sourceIP,
).Scan(&attempts)
return attempts >= 10, err
@@ -130,8 +130,7 @@ func (s *Server) exchangeDeploymentCode(
FROM authorizations
WHERE code_hash = ?
AND revoked_at IS NULL
AND expires_at > UTC_TIMESTAMP(6)
FOR UPDATE`,
AND expires_at > CURRENT_TIMESTAMP`,
codeHash[:],
).Scan(&authorizationID, &hostLimit, &expiresAt)
if errors.Is(err, sql.ErrNoRows) {
@@ -216,7 +215,7 @@ func (s *Server) exchangeDeploymentCode(
r.Context(),
`UPDATE authorization_hosts
SET hostname = ?,
last_seen_at = UTC_TIMESTAMP(6)
last_seen_at = CURRENT_TIMESTAMP
WHERE id = ?`,
request.Hostname, hostID,
)
@@ -342,9 +341,9 @@ func (s *Server) handlePackageDownload(w http.ResponseWriter, r *http.Request) {
JOIN packages p ON p.id = ap.package_id
WHERE ds.token_hash = ?
AND ds.revoked_at IS NULL
AND ds.expires_at > UTC_TIMESTAMP(6)
AND ds.expires_at > CURRENT_TIMESTAMP
AND a.revoked_at IS NULL
AND a.expires_at > UTC_TIMESTAMP(6)
AND a.expires_at > CURRENT_TIMESTAMP
AND p.slug = ?
AND p.enabled = TRUE`,
sessionHash[:], slug,
+6 -4
View File
@@ -17,7 +17,7 @@ import (
"strings"
"time"
_ "github.com/go-sql-driver/mysql"
_ "modernc.org/sqlite"
)
//go:embed templates/*.html static/*
@@ -110,13 +110,15 @@ type pageData struct {
}
func New(cfg Config) (*Server, error) {
db, err := sql.Open("mysql", cfg.DatabaseDSN)
db, err := sql.Open("sqlite", cfg.DatabaseDSN)
if err != nil {
return nil, err
}
db.SetConnMaxLifetime(5 * time.Minute)
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(10)
// A single writer connection avoids SQLITE_BUSY errors while WAL mode still
// permits concurrent readers. This service is intentionally single-node.
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()