switched to fully self contained docker
This commit is contained in:
@@ -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[:],
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user