mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-01 22:46:38 +08:00
299ac30ee4
- Purify core micro-kernel by removing context hardcoded helpers and reverse dependencies - Eliminate init() side effects in infra plugins with reversible lifecycle disposal - Completely isolate plugins by removing cross-plugin imports and using core/contracts - Introduce TaskService and RiskControlService contracts for unified cross-plugin APIs - Regenerate Swagger documentation and update developer guide matrix - Achieve 0 violations in check_cordis_architecture.sh and 100% test pass
154 lines
4.4 KiB
Go
154 lines
4.4 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package logstore
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
|
)
|
|
|
|
type clickhouseUserAccessLogStore struct {
|
|
skipFreeze bool
|
|
}
|
|
|
|
func newClickHouseUserAccessLogStore() *clickhouseUserAccessLogStore {
|
|
return &clickhouseUserAccessLogStore{}
|
|
}
|
|
|
|
var (
|
|
_ UserAccessLogStore = (*clickhouseUserAccessLogStore)(nil)
|
|
_ StatusStore = (*clickhouseUserAccessLogStore)(nil)
|
|
)
|
|
|
|
func (s *clickhouseUserAccessLogStore) ActiveDatabase(_ context.Context) (string, error) {
|
|
return dbNameClickHouse, nil
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) ensureWritable(ctx context.Context) error {
|
|
if !s.skipFreeze && Migrating(ctx) {
|
|
return ErrMigrating
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) BatchInsert(ctx context.Context, logs []UserAccessLog) error {
|
|
if len(logs) == 0 {
|
|
return nil
|
|
}
|
|
if err := s.ensureWritable(ctx); err != nil {
|
|
return err
|
|
}
|
|
return BatchInsert(ctx, logs)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) DeleteAll(ctx context.Context) (int64, error) {
|
|
if err := s.ensureWritable(ctx); err != nil {
|
|
return 0, err
|
|
}
|
|
return DeleteAllUserAccessLogs(ctx)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) DeleteBefore(ctx context.Context, cutoff time.Time) (int64, error) {
|
|
if err := s.ensureWritable(ctx); err != nil {
|
|
return 0, err
|
|
}
|
|
return DeleteUserAccessLogsBefore(ctx, cutoff)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) Count(ctx context.Context, filter AccessLogFilter) (uint64, error) {
|
|
return CountAccessLogs(ctx, filter)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) List(ctx context.Context, filter AccessLogFilter, page, pageSize int) ([]UserAccessLog, uint64, error) {
|
|
return ListAccessLogs(ctx, filter, page, pageSize)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) GetDailyTrend(ctx context.Context, days int) ([]DailyTrend, error) {
|
|
return GetDailyTrend(ctx, days)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) GetBrowserDistribution(ctx context.Context, startTime time.Time) ([]BrowserShare, error) {
|
|
return GetBrowserDistribution(ctx, startTime)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) GetTopActiveUsers(ctx context.Context, startTime time.Time, limit int) ([]TopUser, error) {
|
|
return GetTopActiveUsers(ctx, startTime, limit)
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) EnsurePartitions(_ context.Context, _, _ time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) DropEmptyPartitions(_ context.Context, _ time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) DropExpiredPartitions(_ context.Context, _ time.Time) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) MigrationRange(ctx context.Context) (time.Time, time.Time, error) {
|
|
conn := getChConn()
|
|
if conn == nil {
|
|
return time.Time{}, time.Time{}, fmt.Errorf("clickhouse connection is not initialized")
|
|
}
|
|
table := UserAccessLog{}.TableName()
|
|
var minTime, maxTime *time.Time
|
|
if err := conn.QueryRow(ctx, "SELECT min(created_at), max(created_at) FROM "+table).Scan(&minTime, &maxTime); err != nil {
|
|
return time.Time{}, time.Time{}, fmt.Errorf("query migration range %s: %w", table, err)
|
|
}
|
|
if minTime == nil || maxTime == nil {
|
|
return time.Time{}, time.Time{}, nil
|
|
}
|
|
return minTime.UTC(), maxTime.UTC(), nil
|
|
}
|
|
|
|
func (s *clickhouseUserAccessLogStore) ListForMigration(ctx context.Context, afterID uint64, limit int) ([]UserAccessLog, error) {
|
|
conn := getChConn()
|
|
if conn == nil {
|
|
return nil, fmt.Errorf("clickhouse connection is not initialized")
|
|
}
|
|
if limit <= 0 {
|
|
limit = migrationPageSize
|
|
}
|
|
table := UserAccessLog{}.TableName()
|
|
columns := UserAccessLog{}.InsertColumns()
|
|
rows, err := conn.Query(ctx, fmt.Sprintf(
|
|
"SELECT %s FROM %s WHERE id > ? ORDER BY id ASC LIMIT ?",
|
|
columns, table,
|
|
), afterID, limit)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list user access logs for migration: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
return scanUserAccessLogs(rows)
|
|
}
|
|
|
|
func scanUserAccessLogs(rows driver.Rows) ([]UserAccessLog, error) {
|
|
var result []UserAccessLog
|
|
for rows.Next() {
|
|
var item UserAccessLog
|
|
if err := rows.Scan(
|
|
&item.ID,
|
|
&item.UserID,
|
|
&item.Path,
|
|
&item.Method,
|
|
&item.IP,
|
|
&item.UserAgent,
|
|
&item.Headers,
|
|
&item.Status,
|
|
&item.Latency,
|
|
&item.CreatedAt,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("scan user access log row: %w", err)
|
|
}
|
|
item.CreatedAt = item.CreatedAt.UTC()
|
|
result = append(result, item)
|
|
}
|
|
return result, nil
|
|
}
|