From 72e060d783c4ede096e75e63d8a3bbd5f19c1992 Mon Sep 17 00:00:00 2001 From: Lilfade Date: Sat, 28 Jun 2025 04:45:36 -0500 Subject: [PATCH] rough new admin panel, more changes pending --- app/__init__.py | 36 +- nip.zip => beta-0.0.1.zip | Bin 278561 -> 338538 bytes .../versions/01876f89899b_auto_migrate.py | 28 ++ .../versions/03650b9a0f3a_auto_migrate.py | 28 ++ .../versions/0514fb24a61e_auto_migrate.py | 28 ++ .../versions/08ebb5577232_auto_migrate.py | 28 ++ .../versions/0efc1a18285f_auto_migrate.py | 28 ++ .../versions/30376c514135_auto_migrate.py | 28 ++ .../versions/390c977fe679_auto_migrate.py | 28 ++ .../versions/4082065b932b_auto_migrate.py | 28 ++ .../versions/51640ecd70ee_auto_migrate.py | 28 ++ .../versions/807ca973d0cf_auto_migrate.py | 28 ++ .../versions/886aa234b3b7_auto_migrate.py | 28 ++ .../versions/892da654697c_auto_migrate.py | 28 ++ .../versions/a69f613f9cd5_auto_migrate.py | 28 ++ .../versions/a79453aefa45_auto_migrate.py | 39 ++ .../versions/a87d4c1df4e5_auto_migrate.py | 28 ++ .../versions/b56cd5e57987_auto_migrate.py | 28 ++ .../versions/c23c31ae3a1d_auto_migrate.py | 36 ++ .../versions/f7f41136c073_auto_migrate.py | 28 ++ .../versions/f81a9a44a7fb_auto_migrate.py | 28 ++ .../versions/fe0ebdec3255_auto_migrate.py | 28 ++ plugins/admin/__init__.py | 2 +- plugins/admin/forms.py | 31 ++ plugins/admin/models.py | 17 + plugins/admin/plugin.json | 8 +- plugins/admin/routes.py | 333 +++++++++++++++++- .../templates/admin/admin_dashboard.html | 11 - plugins/admin/templates/admin/dashboard.html | 244 +++++++++++++ plugins/admin/templates/admin/users/form.html | 58 +++ plugins/admin/templates/admin/users/list.html | 244 +++++++++++++ plugins/auth/models.py | 7 + plugins/core_ui/templates/core_ui/base.html | 4 +- 33 files changed, 1550 insertions(+), 24 deletions(-) rename nip.zip => beta-0.0.1.zip (69%) create mode 100644 migrations/versions/01876f89899b_auto_migrate.py create mode 100644 migrations/versions/03650b9a0f3a_auto_migrate.py create mode 100644 migrations/versions/0514fb24a61e_auto_migrate.py create mode 100644 migrations/versions/08ebb5577232_auto_migrate.py create mode 100644 migrations/versions/0efc1a18285f_auto_migrate.py create mode 100644 migrations/versions/30376c514135_auto_migrate.py create mode 100644 migrations/versions/390c977fe679_auto_migrate.py create mode 100644 migrations/versions/4082065b932b_auto_migrate.py create mode 100644 migrations/versions/51640ecd70ee_auto_migrate.py create mode 100644 migrations/versions/807ca973d0cf_auto_migrate.py create mode 100644 migrations/versions/886aa234b3b7_auto_migrate.py create mode 100644 migrations/versions/892da654697c_auto_migrate.py create mode 100644 migrations/versions/a69f613f9cd5_auto_migrate.py create mode 100644 migrations/versions/a79453aefa45_auto_migrate.py create mode 100644 migrations/versions/a87d4c1df4e5_auto_migrate.py create mode 100644 migrations/versions/b56cd5e57987_auto_migrate.py create mode 100644 migrations/versions/c23c31ae3a1d_auto_migrate.py create mode 100644 migrations/versions/f7f41136c073_auto_migrate.py create mode 100644 migrations/versions/f81a9a44a7fb_auto_migrate.py create mode 100644 migrations/versions/fe0ebdec3255_auto_migrate.py create mode 100644 plugins/admin/forms.py create mode 100644 plugins/admin/models.py delete mode 100644 plugins/admin/templates/admin/admin_dashboard.html create mode 100644 plugins/admin/templates/admin/dashboard.html create mode 100644 plugins/admin/templates/admin/users/form.html create mode 100644 plugins/admin/templates/admin/users/list.html diff --git a/app/__init__.py b/app/__init__.py index 71e16a5..4ced773 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -5,13 +5,16 @@ import json import glob import importlib import importlib.util +import time -from flask import Flask +from flask import Flask,request from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_login import LoginManager from flask_wtf.csrf import CSRFProtect from dotenv import load_dotenv +from datetime import datetime + # Load environment variables from .env or system load_dotenv() @@ -113,7 +116,36 @@ def create_app(): def inject_current_year(): from datetime import datetime return {'current_year': datetime.now().year} - + + @app.context_processor + def inject_now(): + return {'utcnow': datetime.utcnow()} + + @app.before_request + def start_timer(): + request._start_time = time.time() + + @app.after_request + def log_analytics(response): + # import here to avoid circular at module‐load time + from plugins.admin.models import AnalyticsEvent + try: + duration = time.time() - getattr(request, '_start_time', time.time()) + ev = AnalyticsEvent( + method=request.method, + path=request.path, + status_code=response.status_code, + response_time=duration, + user_agent=request.headers.get('User-Agent'), + referer=request.headers.get('Referer'), + accept_language=request.headers.get('Accept-Language'), + ) + db.session.add(ev) + db.session.commit() + except Exception: + db.session.rollback() + return response + app.jinja_env.globals['generate_image_url'] = generate_image_url return app diff --git a/nip.zip b/beta-0.0.1.zip similarity index 69% rename from nip.zip rename to beta-0.0.1.zip index 150116bc3fa8b50be5f3ce991fd6eedf82d99556..2db5a883f42773576e1a6bad7667a60058046e51 100644 GIT binary patch delta 72354 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%0Dpg{htMj?Km8q2W7w6u0rN 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 KsRAo7c>f2`-h=4? delta 18179 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 M)HvrN03Fi*KeY7E^#A|> diff --git a/migrations/versions/01876f89899b_auto_migrate.py b/migrations/versions/01876f89899b_auto_migrate.py new file mode 100644 index 0000000..fbd7c57 --- /dev/null +++ b/migrations/versions/01876f89899b_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 01876f89899b +Revises: a69f613f9cd5 +Create Date: 2025-06-28 08:15:57.708963 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '01876f89899b' +down_revision = 'a69f613f9cd5' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/03650b9a0f3a_auto_migrate.py b/migrations/versions/03650b9a0f3a_auto_migrate.py new file mode 100644 index 0000000..354750f --- /dev/null +++ b/migrations/versions/03650b9a0f3a_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 03650b9a0f3a +Revises: 85b7ca21ec19 +Create Date: 2025-06-28 07:57:56.370633 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '03650b9a0f3a' +down_revision = '85b7ca21ec19' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/0514fb24a61e_auto_migrate.py b/migrations/versions/0514fb24a61e_auto_migrate.py new file mode 100644 index 0000000..86a22cf --- /dev/null +++ b/migrations/versions/0514fb24a61e_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 0514fb24a61e +Revises: fe0ebdec3255 +Create Date: 2025-06-28 09:25:58.833912 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '0514fb24a61e' +down_revision = 'fe0ebdec3255' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/08ebb5577232_auto_migrate.py b/migrations/versions/08ebb5577232_auto_migrate.py new file mode 100644 index 0000000..1754483 --- /dev/null +++ b/migrations/versions/08ebb5577232_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 08ebb5577232 +Revises: 0efc1a18285f +Create Date: 2025-06-28 09:34:14.207419 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '08ebb5577232' +down_revision = '0efc1a18285f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/0efc1a18285f_auto_migrate.py b/migrations/versions/0efc1a18285f_auto_migrate.py new file mode 100644 index 0000000..0462bd3 --- /dev/null +++ b/migrations/versions/0efc1a18285f_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 0efc1a18285f +Revises: 886aa234b3b7 +Create Date: 2025-06-28 09:30:43.185721 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '0efc1a18285f' +down_revision = '886aa234b3b7' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/30376c514135_auto_migrate.py b/migrations/versions/30376c514135_auto_migrate.py new file mode 100644 index 0000000..2250508 --- /dev/null +++ b/migrations/versions/30376c514135_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 30376c514135 +Revises: 01876f89899b +Create Date: 2025-06-28 08:20:23.577743 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '30376c514135' +down_revision = '01876f89899b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/390c977fe679_auto_migrate.py b/migrations/versions/390c977fe679_auto_migrate.py new file mode 100644 index 0000000..e88b619 --- /dev/null +++ b/migrations/versions/390c977fe679_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 390c977fe679 +Revises: c23c31ae3a1d +Create Date: 2025-06-28 08:34:18.914877 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '390c977fe679' +down_revision = 'c23c31ae3a1d' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/4082065b932b_auto_migrate.py b/migrations/versions/4082065b932b_auto_migrate.py new file mode 100644 index 0000000..974abd6 --- /dev/null +++ b/migrations/versions/4082065b932b_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 4082065b932b +Revises: 08ebb5577232 +Create Date: 2025-06-28 09:41:11.323777 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '4082065b932b' +down_revision = '08ebb5577232' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/51640ecd70ee_auto_migrate.py b/migrations/versions/51640ecd70ee_auto_migrate.py new file mode 100644 index 0000000..44615c0 --- /dev/null +++ b/migrations/versions/51640ecd70ee_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 51640ecd70ee +Revises: 390c977fe679 +Create Date: 2025-06-28 08:35:37.016653 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '51640ecd70ee' +down_revision = '390c977fe679' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/807ca973d0cf_auto_migrate.py b/migrations/versions/807ca973d0cf_auto_migrate.py new file mode 100644 index 0000000..2ae5fe0 --- /dev/null +++ b/migrations/versions/807ca973d0cf_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 807ca973d0cf +Revises: 51640ecd70ee +Create Date: 2025-06-28 08:46:56.744709 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '807ca973d0cf' +down_revision = '51640ecd70ee' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/886aa234b3b7_auto_migrate.py b/migrations/versions/886aa234b3b7_auto_migrate.py new file mode 100644 index 0000000..c9c3564 --- /dev/null +++ b/migrations/versions/886aa234b3b7_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 886aa234b3b7 +Revises: 0514fb24a61e +Create Date: 2025-06-28 09:27:59.962665 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '886aa234b3b7' +down_revision = '0514fb24a61e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/892da654697c_auto_migrate.py b/migrations/versions/892da654697c_auto_migrate.py new file mode 100644 index 0000000..c266a01 --- /dev/null +++ b/migrations/versions/892da654697c_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: 892da654697c +Revises: f7f41136c073 +Create Date: 2025-06-28 08:56:06.592485 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '892da654697c' +down_revision = 'f7f41136c073' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/a69f613f9cd5_auto_migrate.py b/migrations/versions/a69f613f9cd5_auto_migrate.py new file mode 100644 index 0000000..f879130 --- /dev/null +++ b/migrations/versions/a69f613f9cd5_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: a69f613f9cd5 +Revises: a87d4c1df4e5 +Create Date: 2025-06-28 08:13:00.288626 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a69f613f9cd5' +down_revision = 'a87d4c1df4e5' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/a79453aefa45_auto_migrate.py b/migrations/versions/a79453aefa45_auto_migrate.py new file mode 100644 index 0000000..7a251b6 --- /dev/null +++ b/migrations/versions/a79453aefa45_auto_migrate.py @@ -0,0 +1,39 @@ +"""auto-migrate + +Revision ID: a79453aefa45 +Revises: 892da654697c +Create Date: 2025-06-28 09:18:37.918669 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a79453aefa45' +down_revision = '892da654697c' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('analytics_event', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('timestamp', sa.DateTime(), nullable=False), + sa.Column('method', sa.String(length=10), nullable=False), + sa.Column('path', sa.String(length=200), nullable=False), + sa.Column('status_code', sa.Integer(), nullable=False), + sa.Column('response_time', sa.Float(), nullable=False), + sa.Column('user_agent', sa.String(length=200), nullable=True), + sa.Column('referer', sa.String(length=200), nullable=True), + sa.Column('accept_language', sa.String(length=200), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('analytics_event') + # ### end Alembic commands ### diff --git a/migrations/versions/a87d4c1df4e5_auto_migrate.py b/migrations/versions/a87d4c1df4e5_auto_migrate.py new file mode 100644 index 0000000..39b52cc --- /dev/null +++ b/migrations/versions/a87d4c1df4e5_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: a87d4c1df4e5 +Revises: 03650b9a0f3a +Create Date: 2025-06-28 08:04:13.547254 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a87d4c1df4e5' +down_revision = '03650b9a0f3a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/b56cd5e57987_auto_migrate.py b/migrations/versions/b56cd5e57987_auto_migrate.py new file mode 100644 index 0000000..1faa786 --- /dev/null +++ b/migrations/versions/b56cd5e57987_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: b56cd5e57987 +Revises: a79453aefa45 +Create Date: 2025-06-28 09:20:55.842491 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b56cd5e57987' +down_revision = 'a79453aefa45' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/c23c31ae3a1d_auto_migrate.py b/migrations/versions/c23c31ae3a1d_auto_migrate.py new file mode 100644 index 0000000..3f81382 --- /dev/null +++ b/migrations/versions/c23c31ae3a1d_auto_migrate.py @@ -0,0 +1,36 @@ +"""auto-migrate + +Revision ID: c23c31ae3a1d +Revises: 30376c514135 +Create Date: 2025-06-28 08:31:46.351949 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c23c31ae3a1d' +down_revision = '30376c514135' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.add_column(sa.Column('is_deleted', sa.Boolean(), nullable=False)) + batch_op.add_column(sa.Column('is_banned', sa.Boolean(), nullable=False)) + batch_op.add_column(sa.Column('suspended_until', sa.DateTime(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.drop_column('suspended_until') + batch_op.drop_column('is_banned') + batch_op.drop_column('is_deleted') + + # ### end Alembic commands ### diff --git a/migrations/versions/f7f41136c073_auto_migrate.py b/migrations/versions/f7f41136c073_auto_migrate.py new file mode 100644 index 0000000..cea8e3b --- /dev/null +++ b/migrations/versions/f7f41136c073_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: f7f41136c073 +Revises: 807ca973d0cf +Create Date: 2025-06-28 08:50:28.814054 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f7f41136c073' +down_revision = '807ca973d0cf' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/f81a9a44a7fb_auto_migrate.py b/migrations/versions/f81a9a44a7fb_auto_migrate.py new file mode 100644 index 0000000..460a5f6 --- /dev/null +++ b/migrations/versions/f81a9a44a7fb_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: f81a9a44a7fb +Revises: b56cd5e57987 +Create Date: 2025-06-28 09:22:10.689435 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f81a9a44a7fb' +down_revision = 'b56cd5e57987' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/migrations/versions/fe0ebdec3255_auto_migrate.py b/migrations/versions/fe0ebdec3255_auto_migrate.py new file mode 100644 index 0000000..937c2ff --- /dev/null +++ b/migrations/versions/fe0ebdec3255_auto_migrate.py @@ -0,0 +1,28 @@ +"""auto-migrate + +Revision ID: fe0ebdec3255 +Revises: f81a9a44a7fb +Create Date: 2025-06-28 09:23:36.801994 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'fe0ebdec3255' +down_revision = 'f81a9a44a7fb' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/plugins/admin/__init__.py b/plugins/admin/__init__.py index c0bb32a..864d480 100644 --- a/plugins/admin/__init__.py +++ b/plugins/admin/__init__.py @@ -2,5 +2,5 @@ import click from flask import Flask def register_cli(app: Flask): - # CLI entry-point for admin + # No CLI commands yet for admin plugin pass diff --git a/plugins/admin/forms.py b/plugins/admin/forms.py new file mode 100644 index 0000000..6d79d8e --- /dev/null +++ b/plugins/admin/forms.py @@ -0,0 +1,31 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, SelectField, BooleanField, IntegerField, SubmitField +from wtforms.validators import DataRequired, Optional, ValidationError, NumberRange +import re + +def email_check(form, field): + pattern = re.compile(r'^[^@]+@[^@]+\.[^@]+$') + if not field.data or not pattern.match(field.data): + raise ValidationError('Invalid email address.') + +class UserForm(FlaskForm): + email = StringField('Email', validators=[DataRequired(), email_check]) + password = PasswordField('Password', validators=[Optional()]) + role = SelectField('Role', + choices=[('user','User'), ('admin','Admin')], + validators=[DataRequired()]) + is_verified = BooleanField('Verified') + excluded_from_analytics = BooleanField('Exclude from Analytics') + + # new ban/suspend fields + ban_type = SelectField('Account Status', + choices=[ + ('active','Active'), + ('temp','Temporarily Suspended'), + ('perm','Permanently Banned') + ], + validators=[DataRequired()]) + suspend_days = IntegerField('Suspend days', + default=7, + validators=[Optional(), NumberRange(min=1, max=365)]) + submit = SubmitField('Save') diff --git a/plugins/admin/models.py b/plugins/admin/models.py new file mode 100644 index 0000000..920bcd0 --- /dev/null +++ b/plugins/admin/models.py @@ -0,0 +1,17 @@ +# plugins/admin/models.py + +from app import db +from datetime import datetime + +class AnalyticsEvent(db.Model): + __tablename__ = 'analytics_event' + + id = db.Column(db.Integer, primary_key=True) + timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) + method = db.Column(db.String(10), nullable=False) + path = db.Column(db.String(200), nullable=False) + status_code = db.Column(db.Integer, nullable=False) + response_time = db.Column(db.Float, nullable=False) + user_agent = db.Column(db.String(200)) + referer = db.Column(db.String(200)) + accept_language = db.Column(db.String(200)) diff --git a/plugins/admin/plugin.json b/plugins/admin/plugin.json index 2aa8b50..d90b4b7 100644 --- a/plugins/admin/plugin.json +++ b/plugins/admin/plugin.json @@ -1,6 +1,6 @@ { "name": "admin", - "version": "1.0.0", - "description": "Administration dashboard and plugin manager", - "entry_point": null -} \ No newline at end of file + "version": "0.1.1", + "description": "Admin panel plugin for Nature In Pots", + "entry_point": "register_cli" +} diff --git a/plugins/admin/routes.py b/plugins/admin/routes.py index ff8994c..45223e4 100644 --- a/plugins/admin/routes.py +++ b/plugins/admin/routes.py @@ -1,11 +1,334 @@ -from flask import Blueprint, render_template +# plugins/admin/routes.py + +from flask import ( + Blueprint, render_template, redirect, url_for, + flash, request, jsonify +) from flask_login import login_required, current_user +from flask_wtf.csrf import generate_csrf +from sqlalchemy import func, desc +from datetime import datetime, timedelta -bp = Blueprint('admin', __name__, template_folder='templates') +from app import db +from plugins.auth.models import User +from plugins.growlog.models import GrowLog +from plugins.plant.models import Plant +from plugins.admin.models import AnalyticsEvent +from .forms import UserForm -@bp.route('/admin') +bp = Blueprint('admin', __name__, url_prefix='/admin', template_folder='templates') + + +@bp.route('/dashboard') @login_required -def admin_dashboard(): +def dashboard(): if current_user.role != 'admin': return "Access denied", 403 - return render_template('admin/admin_dashboard.html') + + now = datetime.utcnow() + week_ago = now - timedelta(days=7) + month_ago= now - timedelta(days=30) + + # ─── Overview metrics ────────────────────────────────────────────── # + + # Users + total_users = User.query.count() + active_users = User.query.filter( + User.is_deleted == False, + User.is_banned == False, + (User.suspended_until == None) | (User.suspended_until <= now) + ).count() + suspended_users = User.query.filter(User.suspended_until > now).count() + banned_users = User.query.filter_by(is_banned=True).count() + deleted_users = User.query.filter_by(is_deleted=True).count() + new_7d_users = User.query.filter(User.created_at >= week_ago).count() + + # Grow‐logs + total_logs = GrowLog.query.count() + logs_7d = GrowLog.query.filter(GrowLog.created_at >= week_ago).count() + + # Sign‐ups last 30 days + signup_dates = [(month_ago + timedelta(days=i)).date() for i in range(31)] + signup_counts = [ + User.query.filter(func.date(User.created_at)==d).count() + for d in signup_dates + ] + chart_dates = [d.strftime('%Y-%m-%d') for d in signup_dates] + + # Status distribution + role_counts = { + 'User': User.query.filter_by(role='user').count(), + 'Admin': User.query.filter_by(role='admin').count() + } + + # ─── Analytics aggregates ─────────────────────────────────────────── # + + ev_q = AnalyticsEvent.query.filter(AnalyticsEvent.timestamp >= week_ago) + total_ev = ev_q.count() or 1 + error_ev = ev_q.filter(AnalyticsEvent.status_code >= 500).count() + error_pct = round(error_ev / total_ev * 100, 1) + + top_routes = [ + {'path': path, 'count': cnt} + for path, cnt in db.session.query( + AnalyticsEvent.path, + func.count(AnalyticsEvent.id) + ) + .filter(AnalyticsEvent.timestamp >= week_ago) + .group_by(AnalyticsEvent.path) + .order_by(desc(func.count(AnalyticsEvent.id))) + .limit(10) + .all() + ] + + # Browser share + browsers = {} + ua_counts = ( + db.session.query( + func.substr(AnalyticsEvent.user_agent, 1, 200).label('ua'), + func.count(AnalyticsEvent.id).label('cnt') + ) + .filter(AnalyticsEvent.user_agent.isnot(None)) + .filter(AnalyticsEvent.timestamp >= week_ago) + .group_by(func.substr(AnalyticsEvent.user_agent, 1, 200)) + .all() + ) + for ua, cnt in ua_counts: + b = 'Other' + if 'Chrome' in ua: b = 'Chrome' + elif 'Firefox' in ua: b = 'Firefox' + elif 'Safari' in ua and 'Chrome' not in ua: b = 'Safari' + elif 'Edge' in ua: b = 'Edge' + browsers[b] = browsers.get(b, 0) + cnt + + referrers = dict( + db.session.query( + AnalyticsEvent.referer, func.count(AnalyticsEvent.id) + ) + .filter(AnalyticsEvent.referer.isnot(None)) + .filter(AnalyticsEvent.timestamp >= week_ago) + .group_by(AnalyticsEvent.referer) + .order_by(desc(func.count(AnalyticsEvent.id))) + .limit(5) + .all() + ) + + # ─── Stats metrics ────────────────────────────────────────────────── # + + # Plant totals + total_plants = Plant.query.count() + active_plants = Plant.query.filter_by(is_active=True).count() + + # Top 5 popular plant types + popular_plants = db.session.query( + Plant.plant_type, + func.count(Plant.id).label('count') + ).filter(Plant.is_active==True) \ + .group_by(Plant.plant_type) \ + .order_by(desc('count')) \ + .limit(5) \ + .all() + + # ─── Render template ──────────────────────────────────────────────── # + + return render_template( + 'admin/dashboard.html', + # Overview + total_users=total_users, + active_users=active_users, + suspended_users=suspended_users, + banned_users=banned_users, + deleted_users=deleted_users, + new_7d_users=new_7d_users, + total_logs=total_logs, + logs_7d=logs_7d, + chart_dates=chart_dates, + signup_counts=signup_counts, + role_counts=role_counts, + # Analytics + error_pct=error_pct, + top_routes=top_routes, + browsers=browsers, + referrers=referrers, + # Stats + total_plants=total_plants, + active_plants=active_plants, + popular_plants=popular_plants, + ) + + +@bp.route('/users') +@login_required +def list_users(): + if current_user.role != 'admin': + return "Access denied", 403 + + # --- parse query parameters --- + page = request.args.get('page', 1, type=int) + show_deleted = request.args.get('show_deleted', '0') == '1' + q = request.args.get('q', '', type=str).strip() + + # --- build base query --- + query = User.query + if not show_deleted: + query = query.filter_by(is_deleted=False) + if q: + like = f"%{q}%" + query = query.filter(User.email.ilike(like)) + + # --- paginate the results --- + pagination = query.order_by(User.created_at.desc()) \ + .paginate(page=page, per_page=25, error_out=False) + users = pagination.items + + # --- AJAX response (JSON) --- + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + token = generate_csrf() + users_data = [] + for u in users: + if u.is_banned: + status = 'banned' + until = None + elif u.suspended_until and u.suspended_until > datetime.utcnow(): + status = 'suspended' + until = u.suspended_until.strftime('%Y-%m-%d') + else: + status = 'active' + until = None + + users_data.append({ + 'id': u.id, + 'email': u.email, + 'role': u.role, + 'is_verified': u.is_verified, + 'excluded_from_analytics': u.excluded_from_analytics, + 'status': status, + 'suspended_until': until, + 'is_deleted': u.is_deleted, + 'created_at': u.created_at.strftime('%Y-%m-%d') + }) + + return jsonify({ + 'users': users_data, + 'csrf_token': token + }) + + # --- normal HTML response --- + return render_template( + 'admin/users/list.html', + users=users, + pagination=pagination, + show_deleted=show_deleted, + q=q + ) + + +@bp.route('/users/create', methods=['GET', 'POST']) +@login_required +def create_user(): + if current_user.role != 'admin': + return "Access denied", 403 + form = UserForm() + if form.validate_on_submit(): + u = User( + email=form.email.data, + role=form.role.data, + is_verified=form.is_verified.data, + excluded_from_analytics=form.excluded_from_analytics.data + ) + # ban/suspend logic here if you need it... + if form.password.data: + u.set_password(form.password.data) + db.session.add(u) + db.session.commit() + flash('User created.', 'success') + return redirect(url_for('admin.list_users')) + return render_template('admin/users/form.html', form=form, action='Create') + + +@bp.route('/users//edit', methods=['GET', 'POST']) +@login_required +def edit_user(user_id): + if current_user.role != 'admin': + return "Access denied", 403 + + u = User.query.get_or_404(user_id) + form = UserForm(obj=u) + + # Pre-populate on GET + if request.method == 'GET': + if u.is_banned: + form.ban_type.data = 'perm' + elif u.suspended_until and u.suspended_until > datetime.utcnow(): + form.ban_type.data = 'temp' + days = (u.suspended_until - datetime.utcnow()).days + form.suspend_days.data = days if days > 0 else 1 + else: + form.ban_type.data = 'active' + + if form.validate_on_submit(): + u.email = form.email.data + u.role = form.role.data + u.is_verified = form.is_verified.data + u.excluded_from_analytics = form.excluded_from_analytics.data + + # Ban/Suspend logic + if form.ban_type.data == 'perm': + u.is_banned = True + u.suspended_until = None + elif form.ban_type.data == 'temp': + u.is_banned = False + days = form.suspend_days.data or 7 + u.suspended_until = datetime.utcnow() + timedelta(days=days) + else: + u.is_banned = False + u.suspended_until = None + + if form.password.data: + u.set_password(form.password.data) + + db.session.commit() + flash('User updated.', 'success') + return redirect(url_for('admin.list_users', + page=request.args.get('page'), + show_deleted=request.args.get('show_deleted'), + q=request.args.get('q'))) + + return render_template( + 'admin/users/form.html', + form=form, + action='Edit' + ) + + +@bp.route('/users//delete', methods=['POST']) +@login_required +def delete_user(user_id): + if current_user.role != 'admin': + return "Access denied", 403 + u = User.query.get_or_404(user_id) + if u.id == current_user.id: + flash("Cannot delete yourself.", 'warning') + else: + u.is_deleted = True + db.session.commit() + flash('User deleted (soft).', 'success') + return redirect(url_for('admin.list_users', + page=request.args.get('page'), + show_deleted=request.args.get('show_deleted'), + q=request.args.get('q'))) + + +@bp.route('/users//undelete', methods=['POST']) +@login_required +def undelete_user(user_id): + if current_user.role != 'admin': + return "Access denied", 403 + u = User.query.get_or_404(user_id) + u.is_deleted = False + db.session.commit() + flash('User restored.', 'success') + return redirect(url_for('admin.list_users', + page=request.args.get('page'), + show_deleted=request.args.get('show_deleted'), + q=request.args.get('q'))) diff --git a/plugins/admin/templates/admin/admin_dashboard.html b/plugins/admin/templates/admin/admin_dashboard.html deleted file mode 100644 index 6cd35a0..0000000 --- a/plugins/admin/templates/admin/admin_dashboard.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'core_ui/base.html' %} -{% block title %}Admin Dashboard | Nature In Pots{% endblock %} -{% block content %} -

Admin Dashboard

-

Manage submissions, users, and plugin controls here.

- -{% endblock %} diff --git a/plugins/admin/templates/admin/dashboard.html b/plugins/admin/templates/admin/dashboard.html new file mode 100644 index 0000000..74cd1a1 --- /dev/null +++ b/plugins/admin/templates/admin/dashboard.html @@ -0,0 +1,244 @@ +{# plugins/admin/templates/admin/dashboard.html #} +{% extends 'core_ui/base.html' %} +{% block title %}Admin Dashboard – Nature In Pots{% endblock %} + +{% block styles %} + +{% endblock %} + +{% block content %} +

Admin Dashboard

+ +{# ─── Tab Nav ─────────────────────────────────────────────────────── #} + + +
+ {# ─── Overview Pane ─────────────────────────────────────────────── #} +
+
+ {% for label, value in [ + ('Total Users', total_users), + ('Active Users', active_users), + ('Suspended Users', suspended_users), + ('Banned Users', banned_users), + ('Deleted Users', deleted_users), + ('New Sign-ups (7d)', new_7d_users), + ('Total Logs', total_logs), + ('Logs (7d)', logs_7d), + ('Error Rate %', error_pct), + ] %} +
+
+
+
{{ label }}
+

{{ value }}

+
+
+
+ {% endfor %} +
+ +
+
+
+
Sign-ups (Last 30 days)
+
+ +
+
+
+
+
+
User Status Breakdown
+
+ +
+
+
+
+ +
+
+
+
Top Routes (7d)
+
    + {% for r in top_routes %} +
  • + {{ r.path }} + {{ r.count }} +
  • + {% endfor %} +
+
+
+
+
+
Top Referrers (7d)
+
    + {% for ref, cnt in referrers.items() %} +
  • + {{ ref }} + {{ cnt }} +
  • + {% endfor %} +
+
+
+
+
+
Browsers (7d)
+
+ +
+
+
+
+
+ + {# ─── Stats Pane ────────────────────────────────────────────────── #} +
+
+
+
+
+
Total Plants
+

{{ total_plants }}

+
+
+
+
+
+
+
Active Plants
+

{{ active_plants }}

+
+
+
+
+
+
+
Total Logs
+

{{ total_logs }}

+
+
+
+
+
+
+
Logs (7d)
+

{{ logs_7d }}

+
+
+
+
+ +
+
Top 5 Plant Types
+
    + {% for ptype, cnt in popular_plants %} +
  • + {{ ptype }} + {{ cnt }} +
  • + {% endfor %} +
+
+
+
+{% endblock %} + +{% block scripts %} + + +{% endblock %} diff --git a/plugins/admin/templates/admin/users/form.html b/plugins/admin/templates/admin/users/form.html new file mode 100644 index 0000000..1fb287a --- /dev/null +++ b/plugins/admin/templates/admin/users/form.html @@ -0,0 +1,58 @@ +{% extends 'core_ui/base.html' %} +{% block title %}{{ action }} User – Admin – Nature In Pots{% endblock %} + +{% block content %} +

{{ action }} User

+ +
+ {{ form.hidden_tag() }} + +
+ {{ form.email.label(class="form-label") }} + {{ form.email(class="form-control") }} +
+ +
+ {{ form.password.label(class="form-label") }} + {{ form.password(class="form-control") }} + {% if action == 'Edit' %} + + Leave blank to keep current password. + + {% endif %} +
+ +
+ {{ form.role.label(class="form-label") }} + {{ form.role(class="form-select") }} +
+ +
+ {{ form.is_verified(class="form-check-input") }} + {{ form.is_verified.label(class="form-check-label") }} +
+ +
+ {{ form.excluded_from_analytics(class="form-check-input") }} + {{ form.excluded_from_analytics.label(class="form-check-label") }} +
+ +
+ +
+ {{ form.ban_type.label(class="form-label") }} + {{ form.ban_type(class="form-select") }} +
+ +
+ {{ form.suspend_days.label(class="form-label") }} + {{ form.suspend_days(class="form-control") }} + + Only used if “Temporarily Suspended” is selected (default 7 days) + +
+ + + Cancel +
+{% endblock %} diff --git a/plugins/admin/templates/admin/users/list.html b/plugins/admin/templates/admin/users/list.html new file mode 100644 index 0000000..c94a33a --- /dev/null +++ b/plugins/admin/templates/admin/users/list.html @@ -0,0 +1,244 @@ +{% extends 'core_ui/base.html' %} +{% block title %}Users – Admin – Nature In Pots{% endblock %} +{% block content %} +

Users

+ +
+
+ +
+
+ +
+
+ + +
+
+ +

+ + Create New User + +

+ + + + + + + + + + + {% for u in users %} + + + + + + + + + + + {% endfor %} + +
IDEmailRoleVerifiedExcludedStatusJoinedActions
{{ u.id }}{{ u.email }}{{ u.role }}{{ '✓' if u.is_verified else '' }}{{ '✓' if u.excluded_from_analytics else '' }} + {% if u.is_banned %} + Permanently Banned + {% elif u.suspended_until and u.suspended_until > utcnow %} + + Suspended until {{ u.suspended_until.strftime('%Y-%m-%d') }} + + {% else %} + Active + {% endif %} + {{ u.created_at.strftime('%Y-%m-%d') }} + Edit + {% if u.is_deleted %} +
+ + +
+ {% else %} +
+ + +
+ {% endif %} +
+ + + +{# --- AJAX search script --- #} + +{% endblock %} diff --git a/plugins/auth/models.py b/plugins/auth/models.py index 431ac50..6cfaafd 100644 --- a/plugins/auth/models.py +++ b/plugins/auth/models.py @@ -15,6 +15,13 @@ class User(db.Model, UserMixin): excluded_from_analytics = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) + # Soft-delete flag + is_deleted = db.Column(db.Boolean, nullable=False, default=False) + # Permanent ban flag + is_banned = db.Column(db.Boolean, nullable=False, default=False) + # Temporary suspension until this UTC datetime + suspended_until = db.Column(db.DateTime, nullable=True) + # Use back_populates, not backref submitted_submissions = db.relationship( "Submission", diff --git a/plugins/core_ui/templates/core_ui/base.html b/plugins/core_ui/templates/core_ui/base.html index 29d4fec..7785299 100644 --- a/plugins/core_ui/templates/core_ui/base.html +++ b/plugins/core_ui/templates/core_ui/base.html @@ -17,6 +17,7 @@ main { flex: 1; } footer { background: #f8f9fa; padding: 1rem 0; text-align: center; } + {% block styles %}{% endblock %}