287 lines
6.1 KiB
Go
287 lines
6.1 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type ActivityLogRow struct {
|
|
CreatedAt time.Time `json:"created_at"`
|
|
RequestStartedAt *time.Time `json:"request_started_at,omitempty"`
|
|
RequestFinishedAt *time.Time `json:"request_finished_at,omitempty"`
|
|
DurationMs *int `json:"duration_ms,omitempty"`
|
|
HttpStatus *int `json:"http_status,omitempty"`
|
|
|
|
Username string `json:"username"`
|
|
RoleCode string `json:"role_code"`
|
|
|
|
ActionCategory string `json:"action_category"`
|
|
ActionType string `json:"action_type"`
|
|
ActionTarget string `json:"action_target"`
|
|
Description string `json:"description"`
|
|
|
|
// ✅ TARGET USER
|
|
TargetDfUsrID int64 `json:"target_dfusr_id"`
|
|
TargetUsername string `json:"target_username"`
|
|
|
|
// ✅ DIFF
|
|
ChangeBefore string `json:"change_before"`
|
|
ChangeAfter string `json:"change_after"`
|
|
|
|
IpAddress string `json:"ip_address"`
|
|
IsSuccess bool `json:"is_success"`
|
|
ErrorMsg string `json:"error_message"`
|
|
UserAgent string `json:"user_agent"`
|
|
SessionID string `json:"session_id"`
|
|
|
|
// audit ids
|
|
UserUUID string `json:"user_id"`
|
|
DfUsrID int64 `json:"dfusr_id"`
|
|
|
|
Email string `json:"email"`
|
|
IsActive bool `json:"is_active"`
|
|
}
|
|
|
|
type ActivityLogQuery struct {
|
|
Username string
|
|
RoleCode string
|
|
ActionCategory string
|
|
ActionType string
|
|
ActionTarget string
|
|
Success *bool
|
|
|
|
StatusMin *int
|
|
StatusMax *int
|
|
|
|
DateFrom *time.Time
|
|
DateTo *time.Time
|
|
|
|
Page int
|
|
Limit int
|
|
}
|
|
|
|
type ActivityLogResult struct {
|
|
Total int64 `json:"total"`
|
|
Items []ActivityLogRow `json:"items"`
|
|
}
|
|
|
|
func ListActivityLogs(
|
|
ctx context.Context,
|
|
db *sql.DB,
|
|
q ActivityLogQuery,
|
|
) (ActivityLogResult, error) {
|
|
|
|
// ---------- defaults ----------
|
|
if q.Page <= 0 {
|
|
q.Page = 1
|
|
}
|
|
// limit <=0 → unlimited
|
|
if q.Limit < 0 {
|
|
q.Limit = 50
|
|
}
|
|
if q.Limit > 200 {
|
|
q.Limit = 200
|
|
}
|
|
|
|
offset := 0
|
|
if q.Limit > 0 {
|
|
offset = (q.Page - 1) * q.Limit
|
|
}
|
|
|
|
where := []string{}
|
|
args := []interface{}{}
|
|
|
|
add := func(cond string, val interface{}) {
|
|
where = append(where, cond)
|
|
args = append(args, val)
|
|
}
|
|
|
|
// ---------- filters ----------
|
|
if strings.TrimSpace(q.Username) != "" {
|
|
add(fmt.Sprintf("l.username ILIKE $%d", len(args)+1),
|
|
"%"+strings.TrimSpace(q.Username)+"%")
|
|
}
|
|
if strings.TrimSpace(q.RoleCode) != "" {
|
|
add(fmt.Sprintf("r.code = $%d", len(args)+1),
|
|
strings.TrimSpace(q.RoleCode))
|
|
}
|
|
if strings.TrimSpace(q.ActionCategory) != "" {
|
|
add(fmt.Sprintf("l.action_category = $%d", len(args)+1),
|
|
strings.TrimSpace(q.ActionCategory))
|
|
}
|
|
if strings.TrimSpace(q.ActionType) != "" {
|
|
add(fmt.Sprintf("l.action_type = $%d", len(args)+1),
|
|
strings.TrimSpace(q.ActionType))
|
|
}
|
|
if strings.TrimSpace(q.ActionTarget) != "" {
|
|
add(fmt.Sprintf("l.action_target ILIKE $%d", len(args)+1),
|
|
"%"+strings.TrimSpace(q.ActionTarget)+"%")
|
|
}
|
|
if q.Success != nil {
|
|
add(fmt.Sprintf("l.is_success = $%d", len(args)+1), *q.Success)
|
|
}
|
|
if q.StatusMin != nil {
|
|
add(fmt.Sprintf("l.http_status >= $%d", len(args)+1), *q.StatusMin)
|
|
}
|
|
if q.StatusMax != nil {
|
|
add(fmt.Sprintf("l.http_status <= $%d", len(args)+1), *q.StatusMax)
|
|
}
|
|
if q.DateFrom != nil {
|
|
add(fmt.Sprintf("l.created_at >= $%d", len(args)+1), *q.DateFrom)
|
|
}
|
|
if q.DateTo != nil {
|
|
add(fmt.Sprintf("l.created_at < $%d", len(args)+1),
|
|
q.DateTo.Add(24*time.Hour))
|
|
}
|
|
|
|
whereSQL := ""
|
|
if len(where) > 0 {
|
|
whereSQL = "WHERE " + strings.Join(where, " AND ")
|
|
}
|
|
|
|
// ---------- COUNT ----------
|
|
countSQL := fmt.Sprintf(`
|
|
SELECT count(*)
|
|
FROM mk_user_activity_log l
|
|
LEFT JOIN mk_dfusr u ON u.id = l.dfusr_id
|
|
LEFT JOIN dfrole_usr ru ON ru.dfusr_id = u.id
|
|
LEFT JOIN dfrole r ON r.id = ru.dfrole_id
|
|
%s
|
|
`, whereSQL)
|
|
|
|
var total int64
|
|
if err := db.QueryRowContext(ctx, countSQL, args...).Scan(&total); err != nil {
|
|
return ActivityLogResult{}, err
|
|
}
|
|
|
|
// ---------- LIST ----------
|
|
listArgs := append([]interface{}{}, args...)
|
|
|
|
listSQL := fmt.Sprintf(`
|
|
SELECT
|
|
l.created_at,
|
|
l.request_started_at,
|
|
l.request_finished_at,
|
|
l.duration_ms,
|
|
l.http_status,
|
|
|
|
COALESCE(u.username, l.username, '') AS username,
|
|
COALESCE(r.code,''),
|
|
|
|
COALESCE(l.action_category,''),
|
|
COALESCE(l.action_type,''),
|
|
COALESCE(l.action_target,''),
|
|
COALESCE(l.description,''),
|
|
|
|
COALESCE(l.target_dfusr_id, 0),
|
|
COALESCE(l.target_username, ''),
|
|
COALESCE(l.change_before::text, ''),
|
|
COALESCE(l.change_after::text, ''),
|
|
|
|
COALESCE(l.ip_address,''),
|
|
COALESCE(l.is_success,false),
|
|
COALESCE(l.error_message,''),
|
|
COALESCE(l.user_agent,''),
|
|
COALESCE(l.session_id,''),
|
|
|
|
COALESCE(l.user_id::text,''),
|
|
COALESCE(l.dfusr_id,0),
|
|
|
|
COALESCE(u.email,''),
|
|
COALESCE(u.is_active,false)
|
|
|
|
FROM mk_user_activity_log l
|
|
LEFT JOIN mk_dfusr u ON u.id = l.dfusr_id
|
|
LEFT JOIN dfrole_usr ru ON ru.dfusr_id = u.id
|
|
LEFT JOIN dfrole r ON r.id = ru.dfrole_id
|
|
%s
|
|
ORDER BY l.created_at DESC
|
|
`, whereSQL)
|
|
// LIMIT sadece istenirse
|
|
if q.Limit > 0 {
|
|
|
|
listSQL += fmt.Sprintf(
|
|
" LIMIT $%d OFFSET $%d",
|
|
len(listArgs)+1,
|
|
len(listArgs)+2,
|
|
)
|
|
|
|
listArgs = append(listArgs, q.Limit, offset)
|
|
}
|
|
|
|
rows, err := db.QueryContext(ctx, listSQL, listArgs...)
|
|
if err != nil {
|
|
return ActivityLogResult{}, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
items := []ActivityLogRow{}
|
|
|
|
for rows.Next() {
|
|
var it ActivityLogRow
|
|
var started, finished sql.NullTime
|
|
var dur, status sql.NullInt64
|
|
|
|
if err := rows.Scan(
|
|
&it.CreatedAt,
|
|
&started,
|
|
&finished,
|
|
&dur,
|
|
&status,
|
|
|
|
&it.Username,
|
|
&it.RoleCode,
|
|
|
|
&it.ActionCategory,
|
|
&it.ActionType,
|
|
&it.ActionTarget,
|
|
&it.Description,
|
|
|
|
// ✅ NEW
|
|
&it.TargetDfUsrID,
|
|
&it.TargetUsername,
|
|
&it.ChangeBefore,
|
|
&it.ChangeAfter,
|
|
|
|
&it.IpAddress,
|
|
&it.IsSuccess,
|
|
&it.ErrorMsg,
|
|
&it.UserAgent,
|
|
&it.SessionID,
|
|
|
|
&it.UserUUID,
|
|
&it.DfUsrID,
|
|
|
|
&it.Email,
|
|
&it.IsActive,
|
|
); err != nil {
|
|
return ActivityLogResult{}, err
|
|
}
|
|
|
|
if started.Valid {
|
|
it.RequestStartedAt = &started.Time
|
|
}
|
|
if finished.Valid {
|
|
it.RequestFinishedAt = &finished.Time
|
|
}
|
|
if dur.Valid {
|
|
v := int(dur.Int64)
|
|
it.DurationMs = &v
|
|
}
|
|
if status.Valid {
|
|
v := int(status.Int64)
|
|
it.HttpStatus = &v
|
|
}
|
|
|
|
items = append(items, it)
|
|
}
|
|
|
|
return ActivityLogResult{
|
|
Total: total,
|
|
Items: items,
|
|
}, nil
|
|
}
|