Skip to content

Commit b52c15a

Browse files
committed
Merge branch 'main' of https://github.com/gcclinux/scmd
2 parents 55af4ae + de6d548 commit b52c15a

10 files changed

Lines changed: 733 additions & 22 deletions

File tree

auth.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package main
2+
3+
import (
4+
"crypto/rand"
5+
"encoding/base64"
6+
"fmt"
7+
"log"
8+
"net/http"
9+
"os"
10+
"strings"
11+
"sync"
12+
"time"
13+
)
14+
15+
// Session represents a user session
16+
type Session struct {
17+
Email string
18+
CreatedAt time.Time
19+
ExpiresAt time.Time
20+
}
21+
22+
// SessionStore manages active sessions
23+
type SessionStore struct {
24+
sessions map[string]*Session
25+
mu sync.RWMutex
26+
}
27+
28+
var sessionStore = &SessionStore{
29+
sessions: make(map[string]*Session),
30+
}
31+
32+
// generateSessionID creates a random session ID
33+
func generateSessionID() (string, error) {
34+
b := make([]byte, 32)
35+
if _, err := rand.Read(b); err != nil {
36+
return "", err
37+
}
38+
return base64.URLEncoding.EncodeToString(b), nil
39+
}
40+
41+
// CreateSession creates a new session for the user
42+
func (s *SessionStore) CreateSession(email string) (string, error) {
43+
s.mu.Lock()
44+
defer s.mu.Unlock()
45+
46+
sessionID, err := generateSessionID()
47+
if err != nil {
48+
return "", err
49+
}
50+
51+
session := &Session{
52+
Email: email,
53+
CreatedAt: time.Now(),
54+
ExpiresAt: time.Now().Add(24 * time.Hour), // 24 hour session
55+
}
56+
57+
s.sessions[sessionID] = session
58+
return sessionID, nil
59+
}
60+
61+
// GetSession retrieves a session by ID
62+
func (s *SessionStore) GetSession(sessionID string) (*Session, bool) {
63+
s.mu.RLock()
64+
defer s.mu.RUnlock()
65+
66+
session, exists := s.sessions[sessionID]
67+
if !exists {
68+
return nil, false
69+
}
70+
71+
// Check if session has expired
72+
if time.Now().After(session.ExpiresAt) {
73+
return nil, false
74+
}
75+
76+
return session, true
77+
}
78+
79+
// DeleteSession removes a session
80+
func (s *SessionStore) DeleteSession(sessionID string) {
81+
s.mu.Lock()
82+
defer s.mu.Unlock()
83+
delete(s.sessions, sessionID)
84+
}
85+
86+
// CleanupExpiredSessions removes expired sessions
87+
func (s *SessionStore) CleanupExpiredSessions() {
88+
s.mu.Lock()
89+
defer s.mu.Unlock()
90+
91+
now := time.Now()
92+
for id, session := range s.sessions {
93+
if now.After(session.ExpiresAt) {
94+
delete(s.sessions, id)
95+
}
96+
}
97+
}
98+
99+
// AuthenticateUser validates email and API key against the database
100+
func AuthenticateUser(email, apiKey string) (bool, error) {
101+
email = strings.TrimSpace(email)
102+
apiKey = strings.TrimSpace(apiKey)
103+
104+
if email == "" || apiKey == "" {
105+
return false, fmt.Errorf("email and API key are required")
106+
}
107+
108+
if db == nil {
109+
return false, fmt.Errorf("database not connected")
110+
}
111+
112+
accessTbl := os.Getenv("ACCESS_TB")
113+
if accessTbl == "" {
114+
accessTbl = "access"
115+
}
116+
117+
var count int
118+
query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE email = $1 AND api_key = $2", accessTbl)
119+
err := db.QueryRow(query, email, apiKey).Scan(&count)
120+
if err != nil {
121+
return false, fmt.Errorf("authentication query failed: %v", err)
122+
}
123+
124+
return count > 0, nil
125+
}
126+
127+
// RequireAuth is middleware that checks if user is authenticated
128+
func RequireAuth(next http.HandlerFunc) http.HandlerFunc {
129+
return func(w http.ResponseWriter, r *http.Request) {
130+
cookie, err := r.Cookie("session_id")
131+
if err != nil {
132+
http.Redirect(w, r, "/login", http.StatusSeeOther)
133+
return
134+
}
135+
136+
_, exists := sessionStore.GetSession(cookie.Value)
137+
if !exists {
138+
http.Redirect(w, r, "/login", http.StatusSeeOther)
139+
return
140+
}
141+
142+
next(w, r)
143+
}
144+
}
145+
146+
// StartSessionCleanup starts a goroutine to periodically clean up expired sessions
147+
func StartSessionCleanup() {
148+
ticker := time.NewTicker(1 * time.Hour)
149+
go func() {
150+
for range ticker.C {
151+
sessionStore.CleanupExpiredSessions()
152+
log.Println("Cleaned up expired sessions")
153+
}
154+
}()
155+
}

build.sh

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/bin/bash
2+
# Terminal Intelligence Build Script for Linux/macOS
3+
4+
set -e
5+
6+
BINARY_NAME="scmd"
7+
VERSION="2.0.1"
8+
BUILD_DIR="build"
9+
10+
# Colors for output
11+
RED='\033[0;31m'
12+
GREEN='\033[0;32m'
13+
CYAN='\033[0;36m'
14+
YELLOW='\033[1;33m'
15+
NC='\033[0m' # No Color
16+
17+
# Get build number from git
18+
if git rev-list --count HEAD &>/dev/null; then
19+
BUILD_NUMBER=$(($(git rev-list --count HEAD) + 1))
20+
else
21+
BUILD_NUMBER=1
22+
fi
23+
24+
LDFLAGS="-s -w -X main.version=$VERSION -X main.buildNumber=$BUILD_NUMBER"
25+
26+
ensure_build_dir() {
27+
if [ ! -d "$BUILD_DIR" ]; then
28+
mkdir -p "$BUILD_DIR"
29+
fi
30+
}
31+
32+
build_current() {
33+
echo -e "${CYAN}Building $BINARY_NAME for current platform...${NC}"
34+
ensure_build_dir
35+
go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/$BINARY_NAME" .
36+
echo -e "${GREEN}Build complete: $BUILD_DIR/$BINARY_NAME${NC}"
37+
}
38+
39+
build_windows() {
40+
echo -e "${CYAN}Building $BINARY_NAME for Windows...${NC}"
41+
ensure_build_dir
42+
GOOS=windows GOARCH=amd64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/$BINARY_NAME-windows-amd64.exe" .
43+
echo -e "${GREEN}Build complete: $BUILD_DIR/$BINARY_NAME-windows-amd64.exe${NC}"
44+
}
45+
46+
build_linux() {
47+
echo -e "${CYAN}Building $BINARY_NAME for Linux...${NC}"
48+
ensure_build_dir
49+
GOOS=linux GOARCH=amd64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/$BINARY_NAME-linux-amd64" .
50+
GOOS=linux GOARCH=arm64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/$BINARY_NAME-linux-aarch64" .
51+
echo -e "${GREEN}Build complete: $BUILD_DIR/$BINARY_NAME-linux-amd64 and $BUILD_DIR/$BINARY_NAME-linux-aarch64${NC}"
52+
}
53+
54+
build_darwin() {
55+
echo -e "${CYAN}Building $BINARY_NAME for macOS...${NC}"
56+
ensure_build_dir
57+
GOOS=darwin GOARCH=amd64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/$BINARY_NAME-darwin-amd64" .
58+
GOOS=darwin GOARCH=arm64 go build -ldflags="$LDFLAGS" -o "$BUILD_DIR/$BINARY_NAME-darwin-arm64" .
59+
echo -e "${GREEN}Build complete: $BUILD_DIR/$BINARY_NAME-darwin-amd64 and $BUILD_DIR/$BINARY_NAME-darwin-arm64${NC}"
60+
}
61+
62+
build_all() {
63+
build_windows
64+
build_linux
65+
build_darwin
66+
echo -e "${GREEN}All platform builds complete!${NC}"
67+
}
68+
69+
run_tests() {
70+
echo -e "${CYAN}Running tests...${NC}"
71+
go test ./... -v
72+
}
73+
74+
run_clean() {
75+
echo -e "${CYAN}Cleaning build artifacts...${NC}"
76+
rm -rf "$BUILD_DIR"
77+
rm -f coverage.out coverage.html
78+
echo -e "${GREEN}Clean complete${NC}"
79+
}
80+
81+
show_help() {
82+
echo -e "${CYAN}Terminal Intelligence (TI) Build Script${NC}"
83+
echo ""
84+
echo -e "${YELLOW}Usage: ./build.sh [target]${NC}"
85+
echo ""
86+
echo -e "${YELLOW}Targets:${NC}"
87+
echo " build - Build for current platform (default)"
88+
echo " windows - Build for Windows (amd64)"
89+
echo " linux - Build for Linux (amd64 and arm64)"
90+
echo " darwin - Build for macOS (amd64 and arm64)"
91+
echo " all - Build for all platforms"
92+
echo " test - Run all tests"
93+
echo " clean - Remove build artifacts"
94+
echo " help - Show this help message"
95+
}
96+
97+
# Execute target
98+
TARGET="${1:-build}"
99+
100+
case "$TARGET" in
101+
build)
102+
build_current
103+
;;
104+
windows)
105+
build_windows
106+
;;
107+
linux)
108+
build_linux
109+
;;
110+
darwin)
111+
build_darwin
112+
;;
113+
all)
114+
build_all
115+
;;
116+
test)
117+
run_tests
118+
;;
119+
clean)
120+
run_clean
121+
;;
122+
help)
123+
show_help
124+
;;
125+
*)
126+
echo -e "${RED}Unknown target: $TARGET${NC}"
127+
show_help
128+
exit 1
129+
;;
130+
esac

0 commit comments

Comments
 (0)