Brijesh's Git Server — argus-core @ 7fe70b23373de45a57bafd87ca06a5688da4c3f6

Logging service

internal/database/database.go (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
package database

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/gocql/gocql"
	_ "github.com/joho/godotenv/autoload"
)

type User struct {
	ID           gocql.UUID `json:"id"`
	Email        string     `json:"email"`
	PasswordHash string     `json:"-"`
	CreatedAt    time.Time  `json:"created_at"`
	UpdatedAt    time.Time  `json:"updated_at"`
}

type Application struct {
	ID          gocql.UUID `json:"id"`
	UserID      gocql.UUID `json:"user_id"`
	Name        string     `json:"name"`
	Description string     `json:"description"`
	KeyHash     string     `json:"-"`
	CreatedAt   time.Time  `json:"created_at"`
	UpdatedAt   time.Time  `json:"updated_at"`
}

type Service interface {
	Health() map[string]string
	Close() error

	CreateUser(email, passwordHash string) (*User, error)
	GetUserByEmail(email string) (*User, error)
	GetUserByID(id gocql.UUID) (*User, error)

	CreateApplication(userID gocql.UUID, name, description, keyHash string) (*Application, error)
	GetApplication(id gocql.UUID) (*Application, error)
	ListApplications(userID gocql.UUID) ([]Application, error)
	UpdateApplication(id gocql.UUID, name, description string) (*Application, error)
	DeleteApplication(id gocql.UUID) error
	RegenerateApplicationKey(appID gocql.UUID, newKeyHash string) error
}

type service struct {
	session *gocql.Session
}

func New() Service {
	// Load environment variables
	cassandraHost := os.Getenv("CASSANDRA_HOST")
	cassandraUsername := os.Getenv("CASSANDRA_USERNAME")
	cassandraPassword := os.Getenv("CASSANDRA_PASSWORD")
	cassandraCaPath := os.Getenv("CASSANDRA_CA_PATH")
	cassandraKeyspace := os.Getenv("CASSANDRA_KEYSPACE")

	cluster := gocql.NewCluster(cassandraHost)
	cluster.Port = 9142
	cluster.Authenticator = gocql.PasswordAuthenticator{
		Username: cassandraUsername,
		Password: cassandraPassword,
	}
	cluster.SslOpts = &gocql.SslOptions{
		CaPath:                 cassandraCaPath,
		EnableHostVerification: false,
	}
	cluster.Consistency = gocql.LocalQuorum
	cluster.Keyspace = cassandraKeyspace

	session, err := cluster.CreateSession()
	if err != nil {
		log.Fatal("Failed to create Cassandra session:", err)
	}

	return &service{session: session}
}

func (s *service) Health() map[string]string {
	stats := make(map[string]string)

	if err := s.session.Query("SELECT release_version FROM system.local").Exec(); err != nil {
		stats["status"] = "down"
		stats["error"] = fmt.Sprintf("Cassandra down: %v", err)
		return stats
	}

	stats["status"] = "up"
	stats["message"] = "Cassandra is healthy"
	return stats
}

func (s *service) Close() error {
	s.session.Close()
	return nil
}

// User operations
func (s *service) CreateUser(email, passwordHash string) (*User, error) {
	user := &User{
		ID:           gocql.TimeUUID(),
		Email:        email,
		PasswordHash: passwordHash,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}

	if err := s.session.Query(`
								INSERT INTO users (id, email, password_hash, created_at, updated_at)
								VALUES (?, ?, ?, ?, ?)`,
		user.ID, user.Email, user.PasswordHash, user.CreatedAt, user.UpdatedAt,
	).Exec(); err != nil {
		return nil, fmt.Errorf("error creating user: %w", err)
	}

	return user, nil
}

func (s *service) GetUserByEmail(email string) (*User, error) {
	var user User
	if err := s.session.Query(`
								SELECT id, email, password_hash, created_at, updated_at
								FROM users WHERE email = ? ALLOW FILTERING`,
		email,
	).Scan(&user.ID, &user.Email, &user.PasswordHash, &user.CreatedAt, &user.UpdatedAt); err != nil {
		return nil, fmt.Errorf("user not found: %w", err)
	}
	return &user, nil
}

func (s *service) GetUserByID(id gocql.UUID) (*User, error) {
	var user User
	if err := s.session.Query(`
								SELECT id, email, password_hash, created_at, updated_at
								FROM users WHERE id = ?`,
		id,
	).Scan(&user.ID, &user.Email, &user.PasswordHash, &user.CreatedAt, &user.UpdatedAt); err != nil {
		return nil, fmt.Errorf("user not found: %w", err)
	}
	return &user, nil
}

func (s *service) CreateApplication(userID gocql.UUID, name, description, keyHash string) (*Application, error) {
	app := &Application{
		ID:          gocql.TimeUUID(),
		UserID:      userID,
		Name:        name,
		Description: description,
		KeyHash:     keyHash,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}

	if err := s.session.Query(`
								INSERT INTO applications (id, user_id, name, description, key_hash, created_at, updated_at)
								VALUES (?, ?, ?, ?, ?, ?, ?)`,
		app.ID, app.UserID, app.Name, app.Description, app.KeyHash, app.CreatedAt, app.UpdatedAt,
	).Exec(); err != nil {
		return nil, fmt.Errorf("error creating application: %w", err)
	}

	return app, nil
}

func (s *service) GetApplication(id gocql.UUID) (*Application, error) {
	var app Application
	if err := s.session.Query(`
								SELECT id, user_id, name, description, key_hash, created_at, updated_at
								FROM applications WHERE id = ?`,
		id,
	).Scan(
		&app.ID, &app.UserID, &app.Name, &app.Description,
		&app.KeyHash, &app.CreatedAt, &app.UpdatedAt,
	); err != nil {
		return nil, fmt.Errorf("application not found: %w", err)
	}
	return &app, nil
}

func (s *service) ListApplications(userID gocql.UUID) ([]Application, error) {
	iter := s.session.Query(`
								SELECT id, user_id, name, description, key_hash, created_at, updated_at
								FROM applications WHERE user_id = ? ALLOW FILTERING`,
		userID,
	).Iter()

	var apps []Application
	var app Application
	for iter.Scan(
		&app.ID, &app.UserID, &app.Name, &app.Description,
		&app.KeyHash, &app.CreatedAt, &app.UpdatedAt,
	) {
		apps = append(apps, app)
	}

	if err := iter.Close(); err != nil {
		return nil, fmt.Errorf("error listing applications: %w", err)
	}

	return apps, nil
}

func (s *service) UpdateApplication(id gocql.UUID, name, description string) (*Application, error) {
	now := time.Now()
	if err := s.session.Query(`
        UPDATE applications
        SET name = ?,
            description = ?,
            updated_at = ?
        WHERE id = ?`,
		name, description, now, id,
	).Exec(); err != nil {
		return nil, fmt.Errorf("error updating application: %w", err)
	}

	return s.GetApplication(id)
}

func (s *service) DeleteApplication(id gocql.UUID) error {
	if err := s.session.Query(`
								DELETE FROM applications WHERE id = ?`,
		id,
	).Exec(); err != nil {
		return fmt.Errorf("error deleting application: %w", err)
	}

	return nil
}

func (s *service) RegenerateApplicationKey(appID gocql.UUID, newKeyHash string) error {
	now := time.Now()
	if err := s.session.Query(`
        UPDATE applications
        SET key_hash = ?,
            updated_at = ?
        WHERE id = ?`,
		newKeyHash, now, appID,
	).Exec(); err != nil {
		return fmt.Errorf("error regenerating application key: %w", err)
	}
	return nil
}