mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-19 23:32:53 +00:00
audit logs because neon messed it up
This commit is contained in:
parent
7bd2f0b18d
commit
1dcba5fb10
12 changed files with 552 additions and 8 deletions
73
app/controllers/admin/trust_level_audit_logs_controller.rb
Normal file
73
app/controllers/admin/trust_level_audit_logs_controller.rb
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
class Admin::TrustLevelAuditLogsController < Admin::BaseController
|
||||
before_action :require_admin
|
||||
|
||||
def index
|
||||
@audit_logs = TrustLevelAuditLog.includes(:user, :changed_by)
|
||||
.recent
|
||||
.limit(100) # if there are more actions, fuck off man
|
||||
|
||||
if params[:user_id].present?
|
||||
user = User.find_by(id: params[:user_id])
|
||||
if user
|
||||
@audit_logs = @audit_logs.for_user(user)
|
||||
@filtered_user = user
|
||||
end
|
||||
end
|
||||
|
||||
if params[:admin_id].present?
|
||||
admin = User.find_by(id: params[:admin_id])
|
||||
if admin
|
||||
@audit_logs = @audit_logs.by_admin(admin)
|
||||
@filtered_admin = admin
|
||||
end
|
||||
end
|
||||
|
||||
if params[:user_search].present?
|
||||
search_term = params[:user_search].strip
|
||||
user_ids = User.joins(:email_addresses)
|
||||
.where("LOWER(users.username) LIKE ? OR LOWER(users.slack_username) LIKE ? OR LOWER(users.github_username) LIKE ? OR LOWER(email_addresses.email) LIKE ? OR CAST(users.id AS TEXT) LIKE ?",
|
||||
"%#{search_term.downcase}%", "%#{search_term.downcase}%", "%#{search_term.downcase}%", "%#{search_term.downcase}%", "%#{search_term}%")
|
||||
.pluck(:id)
|
||||
@audit_logs = @audit_logs.where(user_id: user_ids)
|
||||
@user_search = search_term
|
||||
end
|
||||
|
||||
if params[:admin_search].present?
|
||||
search_term = params[:admin_search].strip
|
||||
admin_ids = User.joins(:email_addresses)
|
||||
.where("LOWER(users.username) LIKE ? OR LOWER(users.slack_username) LIKE ? OR LOWER(users.github_username) LIKE ? OR LOWER(email_addresses.email) LIKE ? OR CAST(users.id AS TEXT) LIKE ?",
|
||||
"%#{search_term.downcase}%", "%#{search_term.downcase}%", "%#{search_term.downcase}%", "%#{search_term.downcase}%", "%#{search_term}%")
|
||||
.pluck(:id)
|
||||
@audit_logs = @audit_logs.where(changed_by_id: admin_ids)
|
||||
@admin_search = search_term
|
||||
end
|
||||
|
||||
if params[:trust_level_filter].present? && params[:trust_level_filter] != "all"
|
||||
case params[:trust_level_filter]
|
||||
when "to_convicted"
|
||||
@audit_logs = @audit_logs.where(new_trust_level: "red")
|
||||
when "to_trusted"
|
||||
@audit_logs = @audit_logs.where(new_trust_level: "green")
|
||||
when "to_suspected"
|
||||
@audit_logs = @audit_logs.where(new_trust_level: "yellow")
|
||||
when "to_unscored"
|
||||
@audit_logs = @audit_logs.where(new_trust_level: "blue")
|
||||
end
|
||||
@trust_level_filter = params[:trust_level_filter]
|
||||
end
|
||||
|
||||
@audit_logs = @audit_logs.to_a
|
||||
end
|
||||
|
||||
def show
|
||||
@audit_log = TrustLevelAuditLog.find(params[:id])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_admin
|
||||
unless current_user&.admin?
|
||||
redirect_to root_path, alert: "no perms lmaooo"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -78,10 +78,32 @@ class UsersController < ApplicationController
|
|||
@user = User.find(params[:id])
|
||||
require_admin
|
||||
|
||||
if @user && current_user.admin? && params[:trust_level].present?
|
||||
if User.trust_levels.key?(params[:trust_level])
|
||||
@user.set_trust(params[:trust_level])
|
||||
render json: { success: true, message: "updated", trust_level: @user.trust_level }
|
||||
trust_level = params[:trust_level]
|
||||
reason = params[:reason]
|
||||
notes = params[:notes]
|
||||
|
||||
if @user && current_user.admin? && trust_level.present?
|
||||
unless User.trust_levels.key?(trust_level)
|
||||
return render json: { error: "you fucked it up lmaooo" }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
if trust_level == "red" && !current_user.can_convict_users?
|
||||
return render json: { error: "no perms lmaooo" }, status: :forbidden
|
||||
end
|
||||
|
||||
success = @user.set_trust(
|
||||
trust_level,
|
||||
changed_by_user: current_user,
|
||||
reason: reason,
|
||||
notes: notes
|
||||
)
|
||||
|
||||
if success
|
||||
render json: {
|
||||
success: true,
|
||||
message: "updated",
|
||||
trust_level: @user.trust_level
|
||||
}
|
||||
else
|
||||
render json: { error: "402 invalid" }, status: :unprocessable_entity
|
||||
end
|
||||
|
|
|
|||
35
app/models/trust_level_audit_log.rb
Normal file
35
app/models/trust_level_audit_log.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
class TrustLevelAuditLog < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :changed_by, class_name: "User"
|
||||
|
||||
validates :previous_trust_level, presence: true
|
||||
validates :new_trust_level, presence: true
|
||||
validates :user_id, presence: true
|
||||
validates :changed_by_id, presence: true
|
||||
|
||||
enum :previous_trust_level, {
|
||||
blue: "blue",
|
||||
red: "red",
|
||||
green: "green",
|
||||
yellow: "yellow"
|
||||
}, prefix: :previous
|
||||
|
||||
enum :new_trust_level, {
|
||||
blue: "blue",
|
||||
red: "red",
|
||||
green: "green",
|
||||
yellow: "yellow"
|
||||
}, prefix: :new
|
||||
|
||||
scope :recent, -> { order(created_at: :desc) }
|
||||
scope :for_user, ->(user) { where(user: user) }
|
||||
scope :by_admin, ->(admin) { where(changed_by: admin) }
|
||||
|
||||
def trust_level_change_description
|
||||
"#{previous_trust_level.capitalize} → #{new_trust_level.capitalize}"
|
||||
end
|
||||
|
||||
def admin_name
|
||||
changed_by.display_name
|
||||
end
|
||||
end
|
||||
|
|
@ -27,8 +27,30 @@ class User < ApplicationRecord
|
|||
yellow: 3 # suspected (invisible to user)
|
||||
}
|
||||
|
||||
def set_trust(level)
|
||||
update!(trust_level: level)
|
||||
def set_trust(level, changed_by_user: nil, reason: nil, notes: nil)
|
||||
return false unless level.present?
|
||||
|
||||
previous_level = trust_level
|
||||
|
||||
if changed_by_user.present? && level.to_s == "red" && !changed_by_user.superadmin?
|
||||
return false
|
||||
end
|
||||
|
||||
if previous_level != level.to_s
|
||||
if changed_by_user.present?
|
||||
trust_level_audit_logs.create!(
|
||||
changed_by: changed_by_user,
|
||||
previous_trust_level: previous_level,
|
||||
new_trust_level: level.to_s,
|
||||
reason: reason,
|
||||
notes: notes
|
||||
)
|
||||
end
|
||||
|
||||
update!(trust_level: level)
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
# ex: .set_trust(:green) or set_trust(1) setting it to red
|
||||
|
||||
|
|
@ -59,6 +81,9 @@ class User < ApplicationRecord
|
|||
|
||||
has_many :wakatime_mirrors, dependent: :destroy
|
||||
|
||||
has_many :trust_level_audit_logs, dependent: :destroy
|
||||
has_many :trust_level_changes_made, class_name: "TrustLevelAuditLog", foreign_key: "changed_by_id", dependent: :destroy
|
||||
|
||||
def streak_days
|
||||
@streak_days ||= heartbeats.daily_streaks_for_users([ id ]).values.first
|
||||
end
|
||||
|
|
@ -148,17 +173,37 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def admin?
|
||||
is_admin
|
||||
is_admin || is_superadmin
|
||||
end
|
||||
|
||||
def superadmin?
|
||||
is_superadmin
|
||||
end
|
||||
|
||||
def make_admin!
|
||||
update!(is_admin: true)
|
||||
end
|
||||
|
||||
def make_superadmin!
|
||||
update!(is_superadmin: true, is_admin: true)
|
||||
end
|
||||
|
||||
def remove_admin!
|
||||
update!(is_admin: false)
|
||||
end
|
||||
|
||||
def remove_superadmin!
|
||||
update!(is_superadmin: false)
|
||||
end
|
||||
|
||||
def can_convict_users?
|
||||
superadmin?
|
||||
end
|
||||
|
||||
def can_moderate_trust_levels?
|
||||
admin?
|
||||
end
|
||||
|
||||
def raw_github_user_info
|
||||
return nil unless github_uid.present?
|
||||
return nil unless github_access_token.present?
|
||||
|
|
|
|||
193
app/views/admin/trust_level_audit_logs/index.html.erb
Normal file
193
app/views/admin/trust_level_audit_logs/index.html.erb
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
<% content_for :title, "admin aboose logs" %>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold text-white mb-2">admin aboose logs</h1>
|
||||
<p class="text-gray-400">look at all the funky shit that admins do</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-6 bg-dark rounded-lg p-6">
|
||||
<%= form_with url: admin_trust_level_audit_logs_path, method: :get, local: true, class: "space-y-4" do |form| %>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<%= form.label :user_search, "user lookup", class: "block text-sm font-medium text-gray-300 mb-2" %>
|
||||
<%= form.text_field :user_search,
|
||||
value: @user_search,
|
||||
placeholder: "just put anything here or something",
|
||||
class: "w-full px-3 py-2 bg-darkless rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= form.label :admin_search, "admin lookup", class: "block text-sm font-medium text-gray-300 mb-2" %>
|
||||
<%= form.text_field :admin_search,
|
||||
value: @admin_search,
|
||||
placeholder: "just put anything here or something",
|
||||
class: "w-full px-3 py-2 bg-darkless rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= form.label :trust_level_filter, "filter by trust updates", class: "block text-sm font-medium text-gray-300 mb-2" %>
|
||||
<%= form.select :trust_level_filter,
|
||||
options_for_select([
|
||||
['All Changes', 'all'],
|
||||
['🔴 Set to Convicted', 'to_convicted'],
|
||||
['🟢 Set to Trusted', 'to_trusted'],
|
||||
['🟡 Set to Suspected', 'to_suspected'],
|
||||
['🔵 Set to Unscored', 'to_unscored']
|
||||
], @trust_level_filter || 'all'),
|
||||
{},
|
||||
{ class: "w-full px-3 py-2 bg-darkless rounded-md text-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<%= form.submit "run that shit", class: "bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md font-medium transition-colors" %>
|
||||
<%= link_to "alt+f4", admin_trust_level_audit_logs_path, class: "bg-gray-600 hover:bg-darkless text-white px-4 py-2 rounded-md font-medium transition-colors" %>
|
||||
<span class="text-md text-gray-400">
|
||||
found <%= @audit_logs.length %> result<%= @audit_logs.length == 1 ? '' : 's' %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @filtered_user || @filtered_admin %>
|
||||
<div class="mb-6 p-4 bg-dark border border-blue-500/30 rounded-lg">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<% if @filtered_user %>
|
||||
<p class="text-sm text-blue-300">
|
||||
filtering logs by <strong><%= @filtered_user.display_name %></strong>
|
||||
</p>
|
||||
<% end %>
|
||||
<% if @filtered_admin %>
|
||||
<p class="text-sm text-blue-300">
|
||||
filtering logs by admin: <strong><%= @filtered_admin.display_name %></strong>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= link_to "fuckin abort",
|
||||
admin_trust_level_audit_logs_path,
|
||||
class: "text-sm bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="bg-dark rounded-lg overflow-hidden shadow-xl">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full">
|
||||
<thead class="">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-sm font-medium text-gray-300">
|
||||
time
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-sm font-medium text-gray-300">
|
||||
goober
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-sm font-medium text-gray-300">
|
||||
change
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-sm font-medium text-gray-300">
|
||||
goobed by
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-sm font-medium text-gray-300">
|
||||
why
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-sm font-medium text-gray-300">
|
||||
link
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-950">
|
||||
<% @audit_logs.each do |log| %>
|
||||
<tr class="hover:bg-darkless">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
||||
<%= log.created_at.strftime("%b %d, %Y at %I:%M %p") %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<% if log.user.avatar_url %>
|
||||
<img class="h-8 w-8 rounded-full mr-3" src="<%= log.user.avatar_url %>" alt="">
|
||||
<% end %>
|
||||
<div>
|
||||
<div class="text-sm font-medium text-white">
|
||||
<%= log.user.display_name %>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400">
|
||||
ID: <%= log.user.id %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<span class="text-sm text-gray-300">
|
||||
<%
|
||||
previous_emoji = case log.previous_trust_level
|
||||
when "blue" then "🔵"
|
||||
when "red" then "🔴"
|
||||
when "green" then "🟢"
|
||||
when "yellow" then "🟡"
|
||||
end
|
||||
|
||||
new_emoji = case log.new_trust_level
|
||||
when "blue" then "🔵"
|
||||
when "red" then "🔴"
|
||||
when "green" then "🟢"
|
||||
when "yellow" then "🟡"
|
||||
end
|
||||
%>
|
||||
<%= previous_emoji %> <strong><%= log.previous_trust_level.capitalize %></strong>
|
||||
→
|
||||
<%= new_emoji %> <strong><%= log.new_trust_level.capitalize %></strong>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<% if log.changed_by.avatar_url %>
|
||||
<img class="h-6 w-6 rounded-full mr-2" src="<%= log.changed_by.avatar_url %>" alt="">
|
||||
<% end %>
|
||||
<div>
|
||||
<div class="text-sm text-white">
|
||||
<%= log.changed_by.display_name %>
|
||||
<% if log.changed_by.superadmin? %>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800">
|
||||
supa admin
|
||||
</span>
|
||||
<% elsif log.changed_by.admin? %>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">
|
||||
admin
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-300">
|
||||
<% if log.reason.present? %>
|
||||
<div class="max-w-xs truncate" title="<%= log.reason %>">
|
||||
<%= log.reason %>
|
||||
</div>
|
||||
<% else %>
|
||||
<span class="text-gray-500 italic">plead the 5th</span>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<%= link_to "the deets",
|
||||
admin_trust_level_audit_log_path(log),
|
||||
class: "text-blue-400 hover:text-blue-300" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<% if @audit_logs.empty? %>
|
||||
<div class="text-center py-12">
|
||||
<div class="text-gray-400 text-lg mb-2">theres nothin</div>
|
||||
<p class="text-gray-500">new shit will be seen here</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
124
app/views/admin/trust_level_audit_logs/show.html.erb
Normal file
124
app/views/admin/trust_level_audit_logs/show.html.erb
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<% content_for :title, "title" %>
|
||||
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-3xl font-bold text-white">looking at a single audit log</h1>
|
||||
<%= link_to "get me outta here",
|
||||
admin_trust_level_audit_logs_path,
|
||||
class: "bg-darkless text-white px-4 py-2 rounded-lg" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-darkless rounded-lg shadow-xl p-8">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-white mb-4">user</h2>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<%= render "shared/user_mention", user: @audit_log.user %>
|
||||
<div class="text-sm text-gray-400">
|
||||
id: <%= @audit_log.user.id %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4">
|
||||
<%= link_to "actions on this goober",
|
||||
admin_trust_level_audit_logs_path(user_id: @audit_log.user.id),
|
||||
class: "text-blue-400 hover:text-blue-300" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-white mb-4">updated by</h2>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<%= render "shared/user_mention", user: @audit_log.changed_by %>
|
||||
<% if @audit_log.changed_by.superadmin? %>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800 ml-2">
|
||||
supa admin
|
||||
</span>
|
||||
<% elsif @audit_log.changed_by.admin? %>
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800 ml-2">
|
||||
admin
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400">
|
||||
id: <%= @audit_log.changed_by.id %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4">
|
||||
<%= link_to "changes by this goober",
|
||||
admin_trust_level_audit_logs_path(admin_id: @audit_log.changed_by.id),
|
||||
class: "text-blue-400 hover:text-blue-300" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">the deets</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">executed at</label>
|
||||
<div class="text-white">
|
||||
<%= @audit_log.created_at.strftime("%B %d, %Y at %I:%M %p %Z") %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">before</label>
|
||||
<div class="text-white text-lg">
|
||||
<%
|
||||
a = case @audit_log.previous_trust_level
|
||||
when "blue" then "🔵"
|
||||
when "red" then "🔴"
|
||||
when "green" then "🟢"
|
||||
when "yellow" then "🟡"
|
||||
end
|
||||
%>
|
||||
<%= a %> <strong><%= @audit_log.previous_trust_level.capitalize %></strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">after</label>
|
||||
<div class="text-white text-lg">
|
||||
<%
|
||||
b = case @audit_log.new_trust_level
|
||||
when "blue" then "🔵"
|
||||
when "red" then "🔴"
|
||||
when "green" then "🟢"
|
||||
when "yellow" then "🟡"
|
||||
end
|
||||
%>
|
||||
<%= b %> <strong><%= @audit_log.new_trust_level.capitalize %></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if @audit_log.reason.present? %>
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Reason</label>
|
||||
<div class="text-white bg-gray-800 p-4 rounded-lg">
|
||||
<%= simple_format(@audit_log.reason) %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @audit_log.notes.present? %>
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Additional Notes</label>
|
||||
<div class="text-white bg-gray-800 p-4 rounded-lg">
|
||||
<%= simple_format(@audit_log.notes) %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -39,6 +39,11 @@
|
|||
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
<% if current_user %>
|
||||
<meta name="user-is-superadmin" content="<%= current_user.superadmin? %>">
|
||||
<meta name="user-is-admin" content="<%= current_user.admin? %>">
|
||||
<% end %>
|
||||
|
||||
<%= yield :head %>
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@
|
|||
Feature Flags
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% admin_tool(nil, "div") do %>
|
||||
<%= link_to admin_trust_level_audit_logs_path, class: "block px-2 py-1 rounded-lg transition #{current_page?(admin_trust_level_audit_logs_path) || request.path.start_with?('/admin/trust_level_audit_logs') ? 'bg-primary/50 text-primary' : 'hover:bg-[#23272a]'}", data: { action: "click->nav#clickLink" } do %>
|
||||
Trust Level Logs
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ Rails.application.routes.draw do
|
|||
get "post_reviews/:post_id/date/:date", to: "post_reviews#show", as: :post_review_on_date
|
||||
|
||||
get "ysws_reviews/:record_id", to: "ysws_reviews#show", as: :ysws_review
|
||||
|
||||
resources :trust_level_audit_logs, only: [ :index, :show ]
|
||||
end
|
||||
|
||||
if Rails.env.development?
|
||||
|
|
|
|||
5
db/migrate/20250630000001_add_is_superadmin_to_users.rb
Normal file
5
db/migrate/20250630000001_add_is_superadmin_to_users.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class AddIsSuperadminToUsers < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_column :users, :is_superadmin, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
||||
17
db/migrate/20250630000002_create_trust_level_audit_logs.rb
Normal file
17
db/migrate/20250630000002_create_trust_level_audit_logs.rb
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
class CreateTrustLevelAuditLogs < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :trust_level_audit_logs do |t|
|
||||
t.references :user, null: false, foreign_key: true, index: true
|
||||
t.references :changed_by, null: false, foreign_key: { to_table: :users }, index: true
|
||||
t.string :previous_trust_level, null: false
|
||||
t.string :new_trust_level, null: false
|
||||
t.text :reason, null: true
|
||||
t.text :notes, null: true
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
add_index :trust_level_audit_logs, [ :user_id, :created_at ], name: 'index_trust_level_audit_logs_on_user_and_created_at'
|
||||
add_index :trust_level_audit_logs, [ :changed_by_id, :created_at ], name: 'index_trust_level_audit_logs_on_changed_by_and_created_at'
|
||||
end
|
||||
end
|
||||
20
db/schema.rb
generated
20
db/schema.rb
generated
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_06_28_011017) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_06_30_000002) do
|
||||
create_schema "pganalyze"
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
|
|
@ -483,6 +483,21 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_28_011017) do
|
|||
t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
|
||||
end
|
||||
|
||||
create_table "trust_level_audit_logs", force: :cascade do |t|
|
||||
t.bigint "user_id", null: false
|
||||
t.bigint "changed_by_id", null: false
|
||||
t.string "previous_trust_level", null: false
|
||||
t.string "new_trust_level", null: false
|
||||
t.text "reason"
|
||||
t.text "notes"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["changed_by_id", "created_at"], name: "index_trust_level_audit_logs_on_changed_by_and_created_at"
|
||||
t.index ["changed_by_id"], name: "index_trust_level_audit_logs_on_changed_by_id"
|
||||
t.index ["user_id", "created_at"], name: "index_trust_level_audit_logs_on_user_and_created_at"
|
||||
t.index ["user_id"], name: "index_trust_level_audit_logs_on_user_id"
|
||||
end
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "slack_uid"
|
||||
t.datetime "created_at", null: false
|
||||
|
|
@ -506,6 +521,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_28_011017) do
|
|||
t.string "mailing_address_otc"
|
||||
t.boolean "allow_public_stats_lookup", default: true, null: false
|
||||
t.boolean "default_timezone_leaderboard", default: true, null: false
|
||||
t.boolean "is_superadmin", default: false, null: false
|
||||
t.index ["github_uid", "github_access_token"], name: "index_users_on_github_uid_and_access_token"
|
||||
t.index ["github_uid"], name: "index_users_on_github_uid"
|
||||
t.index ["slack_uid"], name: "index_users_on_slack_uid", unique: true
|
||||
|
|
@ -552,5 +568,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_28_011017) do
|
|||
add_foreign_key "project_repo_mappings", "users"
|
||||
add_foreign_key "repo_host_events", "users"
|
||||
add_foreign_key "sign_in_tokens", "users"
|
||||
add_foreign_key "trust_level_audit_logs", "users"
|
||||
add_foreign_key "trust_level_audit_logs", "users", column: "changed_by_id"
|
||||
add_foreign_key "wakatime_mirrors", "users"
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue