mirror of
https://github.com/DarkflameUniverse/NexusDashboard.git
synced 2024-11-24 22:47:26 +00:00
commit
9cc3dbb4c4
@ -13,7 +13,7 @@ from flask_apscheduler import APScheduler
|
||||
from app.luclient import query_cdclient, register_luclient_jinja_helpers
|
||||
|
||||
from app.commands import init_db, init_accounts, load_property, gen_image_cache, gen_model_cache
|
||||
from app.models import Account, AccountInvitation
|
||||
from app.models import Account, AccountInvitation, AuditLog
|
||||
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
@ -245,3 +245,9 @@ def gm_level(gm_level):
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
def log_audit(message):
|
||||
AuditLog(
|
||||
account_id=current_user.id,
|
||||
action=message
|
||||
).save()
|
||||
|
@ -6,7 +6,7 @@ import datetime
|
||||
import time
|
||||
from app.models import Account, AccountInvitation, db
|
||||
from app.schemas import AccountSchema
|
||||
from app import gm_level
|
||||
from app import gm_level, log_audit
|
||||
from app.forms import EditGMLevelForm
|
||||
|
||||
accounts_blueprint = Blueprint('accounts', __name__)
|
||||
@ -46,8 +46,10 @@ def edit_gm_level(id):
|
||||
form = EditGMLevelForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
log_audit(f"Changed ({account_data.id}){account_data.username}'s GM Level from {account_data.gm_level} to {form.gm_level.data}")
|
||||
account_data.gm_level = form.gm_level.data
|
||||
account_data.save()
|
||||
|
||||
return redirect(url_for('accounts.view', id=account_data.id))
|
||||
|
||||
form.gm_level.data = account_data.gm_level
|
||||
@ -63,8 +65,10 @@ def lock(id):
|
||||
account.locked = not account.locked
|
||||
account.save()
|
||||
if account.locked:
|
||||
log_audit(f"Locked ({account.id}){account.username}")
|
||||
flash("Locked Account", "danger")
|
||||
else:
|
||||
log_audit(f"Unlocked ({account.id}){account.username}")
|
||||
flash("Unlocked account", "success")
|
||||
return redirect(request.referrer if request.referrer else url_for("main.index"))
|
||||
|
||||
@ -77,8 +81,10 @@ def ban(id):
|
||||
account.banned = not account.banned
|
||||
account.save()
|
||||
if account.banned:
|
||||
log_audit(f"Banned ({account.id}){account.username}")
|
||||
flash("Banned Account", "danger")
|
||||
else:
|
||||
log_audit(f"Unbanned ({account.id}){account.username}")
|
||||
flash("Unbanned account", "success")
|
||||
return redirect(request.referrer if request.referrer else url_for("main.index"))
|
||||
|
||||
@ -90,10 +96,12 @@ def mute(id, days=0):
|
||||
account = Account.query.filter(Account.id == id).first()
|
||||
if days == "0":
|
||||
account.mute_expire = 0
|
||||
log_audit(f"Unmuted ({account.id}){account.username}")
|
||||
flash("Unmuted Account", "success")
|
||||
else:
|
||||
muted_intil = datetime.datetime.now() + datetime.timedelta(days=int(days))
|
||||
account.mute_expire = muted_intil.timestamp()
|
||||
log_audit(f"Muted ({account.id}){account.username} for {days} days")
|
||||
flash(f"Muted account for {days} days", "danger")
|
||||
account.save()
|
||||
|
||||
|
@ -5,7 +5,7 @@ from datatables import ColumnDT, DataTables
|
||||
import datetime, time
|
||||
from app.models import CharacterInfo, CharacterXML, Account, db
|
||||
from app.schemas import CharacterInfoSchema
|
||||
from app import gm_level
|
||||
from app import gm_level, log_audit
|
||||
import xmltodict
|
||||
|
||||
character_blueprint = Blueprint('characters', __name__)
|
||||
@ -26,16 +26,19 @@ def approve_name(id, action):
|
||||
character = CharacterInfo.query.filter(CharacterInfo.id == id).first()
|
||||
|
||||
if action == "approve":
|
||||
log_audit(f"Approved ({character.id}){character.pending_name} from {character.name}")
|
||||
flash(
|
||||
f"Approved ({character.id}){character.pending_name} from {character.name}",
|
||||
"success"
|
||||
)
|
||||
if character.pending_name:
|
||||
character.name = character.pending_name
|
||||
character.pending_name = ""
|
||||
character.needs_rename = False
|
||||
flash(
|
||||
f"Approved name {character.name}",
|
||||
"success"
|
||||
)
|
||||
|
||||
elif action == "rename":
|
||||
character.needs_rename = True
|
||||
log_audit(f"Marked character ({character.id}){character.name} (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename")
|
||||
flash(
|
||||
f"Marked character {character.name} (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename",
|
||||
"danger"
|
||||
@ -146,6 +149,9 @@ def restrict(id, bit):
|
||||
abort(404)
|
||||
return
|
||||
|
||||
log_audit(f"Updated ({character_data.id}){character_data.name}'s permission map to \
|
||||
{character_data.permission_map ^ (1 << int(bit))} from {character_data.permission_map}")
|
||||
|
||||
character_data.permission_map ^= (1 << int(bit))
|
||||
character_data.save()
|
||||
|
||||
|
@ -3,7 +3,7 @@ from flask_user import login_required, current_user
|
||||
from app.models import db, Mail, CharacterInfo
|
||||
from datatables import ColumnDT, DataTables
|
||||
from app.forms import SendMailForm
|
||||
from app import gm_level
|
||||
from app import gm_level, log_audit
|
||||
from app.luclient import translate_from_locale, query_cdclient
|
||||
import time
|
||||
|
||||
@ -29,6 +29,7 @@ def send():
|
||||
if form.attachment.data != "0" and form.attachment_count.data == 0:
|
||||
form.attachment_count.data = 1
|
||||
if form.recipient.data == "0":
|
||||
log_audit(f"Sending {form.subject.data}: {form.body.data} to All Characters with {form.attachment_count.data} of item {form.attachment.data}")
|
||||
for character in CharacterInfo.query.all():
|
||||
Mail(
|
||||
sender_id = 0,
|
||||
@ -42,6 +43,7 @@ def send():
|
||||
attachment_lot = form.attachment.data,
|
||||
attachment_count = form.attachment_count.data
|
||||
).save()
|
||||
log_audit(f"Sent {form.subject.data}: {form.body.data} to ({character.id}){character.name} with {form.attachment_count.data} of item {form.attachment.data}")
|
||||
else:
|
||||
Mail(
|
||||
sender_id = 0,
|
||||
@ -55,6 +57,7 @@ def send():
|
||||
attachment_lot = form.attachment.data,
|
||||
attachment_count = form.attachment_count.data
|
||||
).save()
|
||||
log_audit(f"Sent {form.subject.data}: {form.body.data} to ({form.recipient.data}){CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name} with {form.attachment_count.data} of item {form.attachment.data}")
|
||||
|
||||
flash("Sent Mail", "success")
|
||||
return redirect(url_for('mail.send'))
|
||||
|
@ -1,6 +1,6 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_user import UserMixin
|
||||
from flask_user import UserMixin, current_user
|
||||
from wtforms import ValidationError
|
||||
|
||||
import logging
|
||||
@ -1010,3 +1010,42 @@ class Reports(db.Model):
|
||||
def delete(self):
|
||||
db.session.delete(self)
|
||||
db.session.commit()
|
||||
|
||||
class AuditLog(db.Model):
|
||||
__tablename__ = 'audit_logs'
|
||||
id = db.Column(
|
||||
mysql.INTEGER,
|
||||
primary_key=True
|
||||
)
|
||||
|
||||
account_id = db.Column(
|
||||
db.Integer(),
|
||||
db.ForeignKey(Account.id, ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
account = db.relationship(
|
||||
'Account',
|
||||
backref="audit_logs",
|
||||
passive_deletes=True
|
||||
)
|
||||
|
||||
action = db.Column(
|
||||
mysql.TEXT,
|
||||
nullable=False
|
||||
)
|
||||
|
||||
date = db.Column(
|
||||
mysql.TIMESTAMP,
|
||||
nullable=False,
|
||||
server_default=db.func.now()
|
||||
)
|
||||
|
||||
def save(self):
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
db.session.refresh(self)
|
||||
|
||||
def delete(self):
|
||||
db.session.delete(self)
|
||||
db.session.commit()
|
||||
|
@ -3,7 +3,7 @@ from flask_user import login_required
|
||||
from app.models import PetNames, db
|
||||
from datatables import ColumnDT, DataTables
|
||||
from app.forms import CreatePlayKeyForm, EditPlayKeyForm
|
||||
from app import gm_level
|
||||
from app import gm_level, log_audit
|
||||
|
||||
moderation_blueprint = Blueprint('moderation', __name__)
|
||||
|
||||
@ -23,6 +23,7 @@ def approve_pet(id):
|
||||
pet_data = PetNames.query.filter(PetNames.id == id).first()
|
||||
|
||||
pet_data.approved = 2
|
||||
log_audit(f"Approved pet name {pet_data.pet_name}")
|
||||
flash(f"Approved pet name {pet_data.pet_name}", "success")
|
||||
pet_data.save()
|
||||
return redirect(request.referrer if request.referrer else url_for("main.index"))
|
||||
@ -36,6 +37,7 @@ def reject_pet(id):
|
||||
pet_data = PetNames.query.filter(PetNames.id == id).first()
|
||||
|
||||
pet_data.approved = 0
|
||||
log_audit(f"Rejected pet name {pet_data.pet_name}")
|
||||
flash(f"Rejected pet name {pet_data.pet_name}", "danger")
|
||||
pet_data.save()
|
||||
return redirect(request.referrer if request.referrer else url_for("main.index"))
|
||||
|
@ -3,7 +3,7 @@ from flask_user import login_required, current_user
|
||||
from app.models import Account, AccountInvitation, PlayKey, db
|
||||
from datatables import ColumnDT, DataTables
|
||||
from app.forms import CreatePlayKeyForm, EditPlayKeyForm
|
||||
from app import gm_level
|
||||
from app import gm_level, log_audit
|
||||
|
||||
play_keys_blueprint = Blueprint('play_keys', __name__)
|
||||
|
||||
@ -21,6 +21,7 @@ def index():
|
||||
@gm_level(9)
|
||||
def create(count=1, uses=1):
|
||||
PlayKey.create(count=count, uses=uses)
|
||||
log_audit(f"Created {count} Play Key(s) with {uses} uses!")
|
||||
flash(f"Created {count} Play Key(s) with {uses} uses!", "success")
|
||||
return redirect(url_for('play_keys.index'))
|
||||
|
||||
@ -32,6 +33,8 @@ def bulk_create():
|
||||
form = CreatePlayKeyForm()
|
||||
if form.validate_on_submit():
|
||||
PlayKey.create(count=form.count.data, uses=form.uses.data)
|
||||
log_audit(f"Created {form.count.data} Play Key(s) with {form.uses.data} uses!")
|
||||
flash(f"Created {form.count.data} Play Key(s) with {form.uses.data} uses!", "success")
|
||||
return redirect(url_for('play_keys.index'))
|
||||
|
||||
return render_template('play_keys/bulk.html.j2', form=form)
|
||||
@ -43,6 +46,7 @@ def bulk_create():
|
||||
def delete(id):
|
||||
key = PlayKey.query.filter(PlayKey.id == id).first()
|
||||
associated_accounts = Account.query.filter(Account.play_key_id==id).all()
|
||||
log_audit(f"Deleted Play Key {key.key_string}")
|
||||
flash(f"Deleted Play Key {key.key_string}", "danger")
|
||||
key.delete()
|
||||
return redirect(url_for('play_keys.index'))
|
||||
@ -56,10 +60,16 @@ def edit(id):
|
||||
form = EditPlayKeyForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
log_audit(f"Updated Play key {key.id} \
|
||||
Uses: {key.key_uses}:{form.uses.data} \
|
||||
Active: {key.active}:{form.active.data} \
|
||||
Notes: {key.notes}:{form.notes.data} \
|
||||
")
|
||||
key.key_uses = form.uses.data
|
||||
key.active = form.active.data
|
||||
key.notes = form.notes.data
|
||||
key.save()
|
||||
|
||||
return redirect(url_for('play_keys.index'))
|
||||
|
||||
form.uses.data = key.key_uses
|
||||
|
@ -16,7 +16,7 @@ from datatables import ColumnDT, DataTables
|
||||
import time
|
||||
from app.models import Property, db, UGC, CharacterInfo, PropertyContent, Account
|
||||
from app.schemas import PropertySchema
|
||||
from app import gm_level
|
||||
from app import gm_level, log_audit
|
||||
from app.luclient import query_cdclient
|
||||
|
||||
import zlib
|
||||
@ -50,25 +50,29 @@ def approve(id):
|
||||
property_data.rejection_reason = ""
|
||||
|
||||
if property_data.mod_approved:
|
||||
flash(
|
||||
f"""Approved Property
|
||||
message = f"""Approved Property
|
||||
{property_data.name if property_data.name else query_cdclient(
|
||||
'select DisplayDescription from ZoneTable where zoneID = ?',
|
||||
[property_data.zone_id],
|
||||
one=True
|
||||
)[0]}
|
||||
from {CharacterInfo.query.filter(CharacterInfo.id==property_data.owner_id).first().name}""",
|
||||
from {CharacterInfo.query.filter(CharacterInfo.id==property_data.owner_id).first().name}"""
|
||||
log_audit(message)
|
||||
flash(
|
||||
message,
|
||||
"success"
|
||||
)
|
||||
else:
|
||||
flash(
|
||||
f"""Unapproved Property
|
||||
message = f"""Unapproved Property
|
||||
{property_data.name if property_data.name else query_cdclient(
|
||||
'select DisplayDescription from ZoneTable where zoneID = ?',
|
||||
[property_data.zone_id],
|
||||
one=True
|
||||
)[0]}
|
||||
from {CharacterInfo.query.filter(CharacterInfo.id==property_data.owner_id).first().name}""",
|
||||
from {CharacterInfo.query.filter(CharacterInfo.id==property_data.owner_id).first().name}"""
|
||||
log_audit(message)
|
||||
flash(
|
||||
message,
|
||||
"danger"
|
||||
)
|
||||
|
||||
|
38
migrations/versions/3132aaef7413_fix_nullables.py
Normal file
38
migrations/versions/3132aaef7413_fix_nullables.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""fix nullables
|
||||
|
||||
Revision ID: 3132aaef7413
|
||||
Revises: bd908969d8fe
|
||||
Create Date: 2022-02-11 21:51:58.479066
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3132aaef7413'
|
||||
down_revision = 'bd908969d8fe'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('audit_logs', 'account_id',
|
||||
existing_type=mysql.INTEGER(display_width=11),
|
||||
nullable=False)
|
||||
op.alter_column('audit_logs', 'action',
|
||||
existing_type=mysql.TEXT(),
|
||||
nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('audit_logs', 'action',
|
||||
existing_type=mysql.TEXT(),
|
||||
nullable=True)
|
||||
op.alter_column('audit_logs', 'account_id',
|
||||
existing_type=mysql.INTEGER(display_width=11),
|
||||
nullable=True)
|
||||
# ### end Alembic commands ###
|
35
migrations/versions/bd908969d8fe_add_audit_log_table.py
Normal file
35
migrations/versions/bd908969d8fe_add_audit_log_table.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""Add audit_log table
|
||||
|
||||
Revision ID: bd908969d8fe
|
||||
Revises: aee4c6c24811
|
||||
Create Date: 2022-02-11 21:48:03.798474
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'bd908969d8fe'
|
||||
down_revision = 'aee4c6c24811'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('audit_logs',
|
||||
sa.Column('id', mysql.INTEGER(), nullable=False),
|
||||
sa.Column('account_id', sa.Integer(), nullable=True),
|
||||
sa.Column('action', mysql.TEXT(), nullable=True),
|
||||
sa.Column('date', mysql.TIMESTAMP(), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['account_id'], ['accounts.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('audit_logs')
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user