chore: initial project scaffold with admin web, backend, desktop client, and deployment setup
Add monorepo structure for NexaVPN WireGuard control plane including: - .gitignore for node_modules, build artifacts, and environment files - README with project overview, monorepo layout, and quick start guide - Admin web UI with React, Vite, TypeScript, and nginx reverse proxy - API client with type definitions for users, devices, policies, gateways, and audit logs - Admin pages for dashboard, users, devices, policies, g
This commit is contained in:
105
backend/internal/auth/repository.go
Normal file
105
backend/internal/auth/repository.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type PGRepository struct {
|
||||
db *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewPGRepository(db *pgxpool.Pool) *PGRepository {
|
||||
return &PGRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *PGRepository) FindUserByUsername(ctx context.Context, username string) (UserRecord, error) {
|
||||
const query = `
|
||||
select u.id, u.username, u.display_name, r.name, u.password_hash, u.is_active
|
||||
from users u
|
||||
join roles r on r.id = u.role_id
|
||||
where u.username = $1 and u.deleted_at is null
|
||||
`
|
||||
|
||||
row := r.db.QueryRow(ctx, query, username)
|
||||
record := UserRecord{}
|
||||
if err := row.Scan(&record.ID, &record.Username, &record.DisplayName, &record.Role, &record.PasswordHash, &record.IsActive); err != nil {
|
||||
return UserRecord{}, err
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (r *PGRepository) CreateSession(ctx context.Context, userID uuid.UUID, expiresAt time.Time, ipAddress string, userAgent string) (uuid.UUID, error) {
|
||||
const query = `
|
||||
insert into sessions (id, user_id, ip_address, user_agent, expires_at)
|
||||
values ($1, $2, nullif($3, '')::inet, $4, $5)
|
||||
`
|
||||
|
||||
id := uuid.New()
|
||||
_, err := r.db.Exec(ctx, query, id, userID, ipAddress, userAgent, expiresAt)
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (r *PGRepository) StoreRefreshToken(ctx context.Context, sessionID uuid.UUID, userID uuid.UUID, tokenHash string, expiresAt time.Time) error {
|
||||
const query = `
|
||||
insert into refresh_tokens (id, session_id, user_id, token_hash, expires_at)
|
||||
values ($1, $2, $3, $4, $5)
|
||||
`
|
||||
|
||||
_, err := r.db.Exec(ctx, query, uuid.New(), sessionID, userID, tokenHash, expiresAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *PGRepository) FindRefreshToken(ctx context.Context, tokenHash string) (UserRecord, uuid.UUID, error) {
|
||||
const query = `
|
||||
select u.id, u.username, u.display_name, roles.name, u.password_hash, u.is_active, rt.session_id
|
||||
from refresh_tokens rt
|
||||
join users u on u.id = rt.user_id
|
||||
join roles on roles.id = u.role_id
|
||||
where rt.token_hash = $1 and rt.revoked_at is null and rt.expires_at > now()
|
||||
`
|
||||
|
||||
record := UserRecord{}
|
||||
var sessionID uuid.UUID
|
||||
row := r.db.QueryRow(ctx, query, tokenHash)
|
||||
if err := row.Scan(&record.ID, &record.Username, &record.DisplayName, &record.Role, &record.PasswordHash, &record.IsActive, &sessionID); err != nil {
|
||||
return UserRecord{}, uuid.Nil, err
|
||||
}
|
||||
if !record.IsActive {
|
||||
return UserRecord{}, uuid.Nil, errors.New("user inactive")
|
||||
}
|
||||
|
||||
return record, sessionID, nil
|
||||
}
|
||||
|
||||
func (r *PGRepository) RevokeRefreshToken(ctx context.Context, tokenHash string) error {
|
||||
const query = `update refresh_tokens set revoked_at = now() where token_hash = $1 and revoked_at is null`
|
||||
_, err := r.db.Exec(ctx, query, tokenHash)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *PGRepository) HasUsers(ctx context.Context) (bool, error) {
|
||||
var count int
|
||||
if err := r.db.QueryRow(ctx, `select count(*) from users where deleted_at is null`).Scan(&count); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *PGRepository) CreateBootstrapAdmin(ctx context.Context, username, displayName, passwordHash string) (UserRecord, error) {
|
||||
const query = `
|
||||
insert into users (id, role_id, username, display_name, password_hash, is_active)
|
||||
values ($1, (select id from roles where name = 'admin'), $2, $3, $4, true)
|
||||
returning id, username, display_name, password_hash, is_active
|
||||
`
|
||||
|
||||
record := UserRecord{Role: "admin"}
|
||||
err := r.db.QueryRow(ctx, query, uuid.New(), username, displayName, passwordHash).
|
||||
Scan(&record.ID, &record.Username, &record.DisplayName, &record.PasswordHash, &record.IsActive)
|
||||
return record, err
|
||||
}
|
||||
Reference in New Issue
Block a user