3d99940658
Sandbox Deployment Platform — Go control plane + agents, NextJS dashboard, nginx reverse proxy. Cross-compile via Docker; deploy via sshpass to 172.18.136.92 (micro) and 172.18.139.186 (gateway). - control-plane: HTTP API, WS hub, SQLite (modernc.org/sqlite) for progress, .log files for log persistence - agent-micro / agent-gateway: alpine:3.20 + bind-mounted repo, binary exec'd in container, no Dockerfile build step - dashboard: NextJS static export + shadcn/ui components, single WebSocket hook - docker-compose.yml: three services on alpine:latest with docker socket bind for agents - scripts/: build.sh (golang:1.23-alpine cross-compile), deploy.sh, patch-nginx.sh (idempotent nginx splice), ssh wrappers Runtime model: pass-through Bitbucket creds per deploy, never logged or persisted on the agent. Control plane never touches git or docker directly — agents do all the work locally.
46 lines
859 B
Go
46 lines
859 B
Go
package api
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Sessions is an in-memory token store. Replace with a signed JWT or
|
|
// a Redis-backed store when we need multi-replica CP. For MVP one process
|
|
// is enough.
|
|
type Sessions struct {
|
|
mu sync.RWMutex
|
|
store map[string]session
|
|
}
|
|
|
|
type session struct {
|
|
user string
|
|
expires time.Time
|
|
}
|
|
|
|
func NewSessions() *Sessions {
|
|
return &Sessions{store: make(map[string]session)}
|
|
}
|
|
|
|
func (s *Sessions) Issue(user string) string {
|
|
b := make([]byte, 16)
|
|
_, _ = rand.Read(b)
|
|
tok := hex.EncodeToString(b)
|
|
s.mu.Lock()
|
|
s.store[tok] = session{user: user, expires: time.Now().Add(12 * time.Hour)}
|
|
s.mu.Unlock()
|
|
return tok
|
|
}
|
|
|
|
func (s *Sessions) Valid(tok string) bool {
|
|
s.mu.RLock()
|
|
sess, ok := s.store[tok]
|
|
s.mu.RUnlock()
|
|
if !ok {
|
|
return false
|
|
}
|
|
return time.Now().Before(sess.expires)
|
|
}
|