| Server IP : 157.230.181.24 / Your IP : 216.73.217.11 Web Server : Apache/2.4.58 (Ubuntu) System : Linux conductive 6.8.0-117-generic #117-Ubuntu SMP PREEMPT_DYNAMIC Tue May 5 19:26:24 UTC 2026 x86_64 User : ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/vhosts/ceagon/open-oscar-server/state/ |
Upload File : |
package state
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"time"
)
var (
// ErrDupAPIKey is returned when attempting to insert a duplicate API key.
ErrDupAPIKey = errors.New("API key already exists")
// ErrNoAPIKey is returned when an API key is not found.
ErrNoAPIKey = errors.New("API key not found")
)
// WebAPIKey represents a Web API authentication key.
type WebAPIKey struct {
DevID string `json:"dev_id"`
DevKey string `json:"dev_key"`
AppName string `json:"app_name"`
CreatedAt time.Time `json:"created_at"`
LastUsed *time.Time `json:"last_used,omitempty"`
IsActive bool `json:"is_active"`
RateLimit int `json:"rate_limit"`
AllowedOrigins []string `json:"allowed_origins"`
Capabilities []string `json:"capabilities"`
}
// WebAPIKeyUpdate represents fields that can be updated for an API key.
type WebAPIKeyUpdate struct {
AppName *string `json:"app_name,omitempty"`
IsActive *bool `json:"is_active,omitempty"`
RateLimit *int `json:"rate_limit,omitempty"`
AllowedOrigins *[]string `json:"allowed_origins,omitempty"`
Capabilities *[]string `json:"capabilities,omitempty"`
}
// CreateAPIKey inserts a new API key into the database.
func (f SQLiteUserStore) CreateAPIKey(ctx context.Context, key WebAPIKey) error {
originsJSON, err := json.Marshal(key.AllowedOrigins)
if err != nil {
return fmt.Errorf("failed to marshal allowed origins: %w", err)
}
capabilitiesJSON, err := json.Marshal(key.Capabilities)
if err != nil {
return fmt.Errorf("failed to marshal capabilities: %w", err)
}
q := `
INSERT INTO web_api_keys (dev_id, dev_key, app_name, created_at, is_active, rate_limit, allowed_origins, capabilities)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (dev_id) DO NOTHING
`
result, err := f.db.ExecContext(ctx,
q,
key.DevID,
key.DevKey,
key.AppName,
key.CreatedAt.Unix(),
key.IsActive,
key.RateLimit,
string(originsJSON),
string(capabilitiesJSON),
)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrDupAPIKey
}
return nil
}
// GetAPIKeyByDevKey retrieves an API key by its dev_key value.
func (f *SQLiteUserStore) GetAPIKeyByDevKey(ctx context.Context, devKey string) (*WebAPIKey, error) {
q := `
SELECT dev_id, dev_key, app_name, created_at, last_used, is_active, rate_limit, allowed_origins, capabilities
FROM web_api_keys
WHERE dev_key = ? AND is_active = 1
`
var key WebAPIKey
var createdAt, lastUsed sql.NullInt64
var originsJSON, capabilitiesJSON string
err := f.db.QueryRowContext(ctx, q, devKey).Scan(
&key.DevID,
&key.DevKey,
&key.AppName,
&createdAt,
&lastUsed,
&key.IsActive,
&key.RateLimit,
&originsJSON,
&capabilitiesJSON,
)
if err == sql.ErrNoRows {
return nil, ErrNoAPIKey
}
if err != nil {
return nil, err
}
key.CreatedAt = time.Unix(createdAt.Int64, 0)
if lastUsed.Valid {
t := time.Unix(lastUsed.Int64, 0)
key.LastUsed = &t
}
if err := json.Unmarshal([]byte(originsJSON), &key.AllowedOrigins); err != nil {
return nil, fmt.Errorf("failed to unmarshal allowed origins: %w", err)
}
if err := json.Unmarshal([]byte(capabilitiesJSON), &key.Capabilities); err != nil {
return nil, fmt.Errorf("failed to unmarshal capabilities: %w", err)
}
return &key, nil
}
// GetAPIKeyByDevID retrieves an API key by its dev_id value.
func (f SQLiteUserStore) GetAPIKeyByDevID(ctx context.Context, devID string) (*WebAPIKey, error) {
q := `
SELECT dev_id, dev_key, app_name, created_at, last_used, is_active, rate_limit, allowed_origins, capabilities
FROM web_api_keys
WHERE dev_id = ?
`
var key WebAPIKey
var createdAt, lastUsed sql.NullInt64
var originsJSON, capabilitiesJSON string
err := f.db.QueryRowContext(ctx, q, devID).Scan(
&key.DevID,
&key.DevKey,
&key.AppName,
&createdAt,
&lastUsed,
&key.IsActive,
&key.RateLimit,
&originsJSON,
&capabilitiesJSON,
)
if err == sql.ErrNoRows {
return nil, ErrNoAPIKey
}
if err != nil {
return nil, err
}
key.CreatedAt = time.Unix(createdAt.Int64, 0)
if lastUsed.Valid {
t := time.Unix(lastUsed.Int64, 0)
key.LastUsed = &t
}
if err := json.Unmarshal([]byte(originsJSON), &key.AllowedOrigins); err != nil {
return nil, fmt.Errorf("failed to unmarshal allowed origins: %w", err)
}
if err := json.Unmarshal([]byte(capabilitiesJSON), &key.Capabilities); err != nil {
return nil, fmt.Errorf("failed to unmarshal capabilities: %w", err)
}
return &key, nil
}
// ListAPIKeys retrieves all API keys from the database.
func (f SQLiteUserStore) ListAPIKeys(ctx context.Context) ([]WebAPIKey, error) {
q := `
SELECT dev_id, dev_key, app_name, created_at, last_used, is_active, rate_limit, allowed_origins, capabilities
FROM web_api_keys
ORDER BY created_at DESC
`
rows, err := f.db.QueryContext(ctx, q)
if err != nil {
return nil, err
}
defer rows.Close()
var keys []WebAPIKey
for rows.Next() {
var key WebAPIKey
var createdAt, lastUsed sql.NullInt64
var originsJSON, capabilitiesJSON string
err := rows.Scan(
&key.DevID,
&key.DevKey,
&key.AppName,
&createdAt,
&lastUsed,
&key.IsActive,
&key.RateLimit,
&originsJSON,
&capabilitiesJSON,
)
if err != nil {
return nil, err
}
key.CreatedAt = time.Unix(createdAt.Int64, 0)
if lastUsed.Valid {
t := time.Unix(lastUsed.Int64, 0)
key.LastUsed = &t
}
if err := json.Unmarshal([]byte(originsJSON), &key.AllowedOrigins); err != nil {
return nil, fmt.Errorf("failed to unmarshal allowed origins: %w", err)
}
if err := json.Unmarshal([]byte(capabilitiesJSON), &key.Capabilities); err != nil {
return nil, fmt.Errorf("failed to unmarshal capabilities: %w", err)
}
keys = append(keys, key)
}
if err = rows.Err(); err != nil {
return nil, err
}
return keys, nil
}
// UpdateAPIKey updates an existing API key's fields.
func (f SQLiteUserStore) UpdateAPIKey(ctx context.Context, devID string, updates WebAPIKeyUpdate) error {
// Build dynamic UPDATE query based on provided fields
var setClauses []string
var args []interface{}
if updates.AppName != nil {
setClauses = append(setClauses, "app_name = ?")
args = append(args, *updates.AppName)
}
if updates.IsActive != nil {
setClauses = append(setClauses, "is_active = ?")
args = append(args, *updates.IsActive)
}
if updates.RateLimit != nil {
setClauses = append(setClauses, "rate_limit = ?")
args = append(args, *updates.RateLimit)
}
if updates.AllowedOrigins != nil {
originsJSON, err := json.Marshal(*updates.AllowedOrigins)
if err != nil {
return fmt.Errorf("failed to marshal allowed origins: %w", err)
}
setClauses = append(setClauses, "allowed_origins = ?")
args = append(args, string(originsJSON))
}
if updates.Capabilities != nil {
capabilitiesJSON, err := json.Marshal(*updates.Capabilities)
if err != nil {
return fmt.Errorf("failed to marshal capabilities: %w", err)
}
setClauses = append(setClauses, "capabilities = ?")
args = append(args, string(capabilitiesJSON))
}
if len(setClauses) == 0 {
return nil // No updates to perform
}
// Add WHERE clause argument
args = append(args, devID)
q := fmt.Sprintf(`
UPDATE web_api_keys
SET %s
WHERE dev_id = ?
`, joinStrings(setClauses, ", "))
result, err := f.db.ExecContext(ctx, q, args...)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrNoAPIKey
}
return nil
}
// DeleteAPIKey removes an API key from the database.
func (f SQLiteUserStore) DeleteAPIKey(ctx context.Context, devID string) error {
q := `
DELETE FROM web_api_keys WHERE dev_id = ?
`
result, err := f.db.ExecContext(ctx, q, devID)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrNoAPIKey
}
return nil
}
// UpdateLastUsed updates the last_used timestamp for an API key.
func (f *SQLiteUserStore) UpdateLastUsed(ctx context.Context, devKey string) error {
q := `
UPDATE web_api_keys
SET last_used = ?
WHERE dev_key = ?
`
_, err := f.db.ExecContext(ctx, q, time.Now().Unix(), devKey)
return err
}
// joinStrings is a helper function to join strings with a separator.
func joinStrings(strs []string, sep string) string {
if len(strs) == 0 {
return ""
}
result := strs[0]
for i := 1; i < len(strs); i++ {
result += sep + strs[i]
}
return result
}