├── .env-example ├── .flake8 ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── pythonapp.yml │ └── pythonpublish.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── codecov.yml ├── craft ├── description ├── makefile ├── masonite.sqlite3 ├── pyproject.toml ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── setup.py ├── src ├── __init__.py └── masonite_audit │ ├── __init__.py │ ├── config │ └── masonite_audit.py │ ├── masonite_audit.py │ ├── migrations │ └── create_audit_logs_table.py │ ├── mixins │ ├── __init__.py │ └── audit.py │ ├── models │ └── audit_log.py │ ├── observer │ ├── __init__.py │ └── audit_observer.py │ └── providers │ ├── AuditProvider.py │ └── __init__.py ├── tests ├── __init__.py ├── integrations │ ├── Kernel.py │ ├── app │ │ ├── __init__.py │ │ ├── controllers │ │ │ ├── WelcomeController.py │ │ │ └── __init__.py │ │ ├── middlewares │ │ │ └── VerifyCsrfToken.py │ │ └── models │ │ │ └── User.py │ ├── config │ │ ├── __init__.py │ │ ├── application.py │ │ ├── auth.py │ │ ├── broadcast.py │ │ ├── cache.py │ │ ├── database.py │ │ ├── exceptions.py │ │ ├── filesystem.py │ │ ├── mail.py │ │ ├── masonite-audit.py │ │ ├── masonite_audit.py │ │ ├── notification.py │ │ ├── providers.py │ │ ├── queue.py │ │ └── session.py │ ├── databases │ │ ├── migrations │ │ │ ├── 2021_01_09_033202_create_password_reset_table.py │ │ │ ├── 2021_01_09_043202_create_users_table.py │ │ │ └── 2022_04_21_110158_create_audit_logs_table.py │ │ └── seeds │ │ │ ├── __init__.py │ │ │ ├── database_seeder.py │ │ │ └── user_table_seeder.py │ ├── resources │ │ ├── css │ │ │ └── app.css │ │ └── js │ │ │ ├── app.js │ │ │ └── bootstrap.js │ ├── routes │ │ └── web.py │ ├── storage │ │ ├── .gitignore │ │ └── public │ │ │ ├── favicon.ico │ │ │ ├── logo.png │ │ │ └── robots.txt │ └── templates │ │ ├── __init__.py │ │ ├── base.html │ │ ├── maintenance.html │ │ └── welcome.html └── unit │ ├── __init__.py │ └── test_audit.py └── wsgi.py /.env-example: -------------------------------------------------------------------------------- 1 | APP_DEBUG=True 2 | APP_ENV=development 3 | APP_KEY=plyUWY8iZnEH9_8WrVjl-LS3B8aRtHK9UAB35fGAq0M= 4 | DB_CONFIG_PATH=tests/integrations/config/database 5 | DB_CONNECTION=sqlite 6 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | per-file-ignores = __init__.py:F401 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | 9 | # Maintain dependencies for cookiecutter repo 10 | - package-ecosystem: "pip" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | # Allow up to 10 open pull requests for pip dependencies 15 | open-pull-requests-limit: 10 16 | -------------------------------------------------------------------------------- /.github/workflows/pythonapp.yml: -------------------------------------------------------------------------------- 1 | name: Test Application 2 | 3 | on: 4 | pull_request: 5 | branches: [master] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | python-version: ["3.7", "3.8", "3.9", "3.10"] 13 | name: Python ${{ matrix.python-version }} 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install dependencies 21 | run: | 22 | make init 23 | - name: Test with pytest and Build coverage 24 | run: | 25 | make coverage 26 | - name: Upload coverage 27 | uses: codecov/codecov-action@v3 28 | with: 29 | token: ${{ secrets.CODECOV_TOKEN }} 30 | fail_ci_if_error: false 31 | 32 | lint: 33 | runs-on: ubuntu-latest 34 | name: Lint 35 | steps: 36 | - uses: actions/checkout@v3 37 | - name: Set up Python 3.8 38 | uses: actions/setup-python@v4 39 | with: 40 | python-version: 3.8 41 | - name: Intall Flake8 42 | run: | 43 | pip install flake8 44 | - name: Lint 45 | run: make lint 46 | -------------------------------------------------------------------------------- /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Set up Python 13 | uses: actions/setup-python@v4 14 | with: 15 | python-version: "3.x" 16 | - name: Install dependencies 17 | run: | 18 | make init 19 | - name: Publish only packages passing test 20 | run: | 21 | make test 22 | - name: Build and publish 23 | env: 24 | TWINE_USERNAME: meyubaraj 25 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 26 | run: | 27 | make publish 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | venv2 3 | .vscode 4 | .idea/ 5 | build/ 6 | .pypirc 7 | .coverage 8 | coverage.xml 9 | .pytest_* 10 | **/*__pycache__* 11 | **/*.DS_Store* 12 | **.pyc 13 | dist 14 | .env 15 | *.db 16 | src/masonite_masonite_permission.egg-info -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022, Yubaraj Shrestha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/MANIFEST.in -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Masonite Audit 2 | 3 |

4 | 5 |

6 | 7 |

8 | GitHub Workflow Status 9 | PyPI 10 | issues 11 | Python Version 12 | GitHub release (latest by date including pre-releases) 13 | License 14 | star 15 | downloads 16 | Code style: black 17 |

18 | 19 | ## Introduction 20 | 21 | Keep track of all your model changes with ease. 22 | 23 | ## Getting Started 24 | 25 | Install the package using pip: 26 | 27 | ```bash 28 | pip install masonite-audit 29 | ``` 30 | 31 | Add AuditProvider to your project in `config/providers.py`: 32 | 33 | ```python 34 | # config/providers.py 35 | # ... 36 | from masonite_audit import AuditProvider 37 | 38 | # ... 39 | PROVIDERS = [ 40 | # ... 41 | # Third Party Providers 42 | AuditProvider, 43 | # ... 44 | ] 45 | ``` 46 | 47 | Publish the package configuration files. 48 | 49 | ```bash 50 | python craft package:publish masonite-audit 51 | ``` 52 | 53 | This will add migrations and other `masonite-audit` related configuration to your project. Run your migrations to create the related database tables. 54 | 55 | ```bash 56 | python craft migrate 57 | ``` 58 | 59 | Finally, inherit `Audit` mixin into all the models for which you need audit logging. 60 | 61 | ```python 62 | from masonite_audit.mixins import Audit 63 | class YourModel(Audit): 64 | pass 65 | ``` 66 | 67 | If you want to get the audit history for a model, you can use the `history` method: 68 | 69 | ```python 70 | user = User.find(1) 71 | user.history() 72 | ``` 73 | 74 | In order to rollback to previous versions of a model, you can use the `rollback` method: 75 | 76 | ```python 77 | user = User.find(1) 78 | user.rollback() # to rollback to previous version 79 | # or 80 | user.rollback(step=4) # to rollback to version 4 81 | ``` 82 | 83 | 84 | 85 | ## License 86 | 87 | Masonite Audit is open-sourced software licensed under the [MIT license](LICENSE). 88 | 89 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: no 3 | 4 | github_checks: false 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: "70...100" 10 | 11 | parsers: 12 | gcov: 13 | branch_detection: 14 | conditional: yes 15 | loop: yes 16 | method: no 17 | macro: no 18 | 19 | comment: 20 | layout: "footer" 21 | behavior: default 22 | require_changes: no 23 | -------------------------------------------------------------------------------- /craft: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Craft Command. 3 | This module is really used for backup only if the masonite CLI cannot import this for you. 4 | This can be used by running "python craft". This module is not ran when the CLI can 5 | successfully import commands for you. 6 | """ 7 | 8 | from wsgi import application 9 | 10 | if __name__ == '__main__': 11 | application.make('commands').run() 12 | -------------------------------------------------------------------------------- /description: -------------------------------------------------------------------------------- 1 | Unnamed repository; edit this file 'description' to name the repository. 2 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help 2 | help: ## Show this help 3 | @egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' 4 | 5 | init: ## Install package dependencies 6 | cp .env-example .env 7 | pip install --upgrade pip 8 | # install test project and package dependencies 9 | pip install -r requirements.txt 10 | # install package and dev dependencies (see setup.py) 11 | pip install '.[dev]' 12 | test: ## Run package tests 13 | python -m pytest tests 14 | ci: ## [CI] Run package tests and lint 15 | make test 16 | make lint 17 | lint: ## Run code linting 18 | python -m flake8 . 19 | format: ## Format code with Black 20 | black src 21 | black tests 22 | coverage: ## Run package tests and upload coverage reports 23 | python -m pytest --cov-report term --cov-report xml --cov=src/masonite/masonite_audit tests 24 | publish: ## Publish package to pypi 25 | python setup.py sdist bdist_wheel 26 | twine upload dist/* --verbose 27 | rm -fr build dist .egg src/masonite_audit.egg-info 28 | pypirc: ## Copy the template .pypirc in the repo to your home directory 29 | cp .pypirc ~/.pypirc -------------------------------------------------------------------------------- /masonite.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/masonite.sqlite3 -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 99 3 | target-version = ['py38'] 4 | include = '\.pyi?$' 5 | exclude = ''' 6 | /( 7 | \.git 8 | \.github 9 | \.vscode 10 | | \.venv 11 | | docs 12 | | node_modules 13 | | tests/integrations/templates 14 | )/ 15 | ''' 16 | 17 | [tool.isort] 18 | profile = "black" 19 | multi_line_output = 3 20 | include_trailing_comma = true -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | filterwarnings = 3 | ignore::DeprecationWarning 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | masonite>=4,<5 2 | masonite-orm>=2,<3 3 | psycopg2-binary==2.9.5 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = 3 | .git, 4 | .github, 5 | .vscode, 6 | __pycache__, 7 | templates, 8 | node_modules, 9 | venv 10 | max-complexity = 10 11 | max-line-length = 99 12 | 13 | omit = 14 | */config/* 15 | setup.py 16 | */stubs/* 17 | wsgi.py 18 | tests/ -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setup( 7 | name="masonite-audit", 8 | # Versions should comply with PEP440. For a discussion on single-sourcing 9 | # the version across setup.py and the project code, see 10 | # https://packaging.python.org/en/latest/single_source_version.html 11 | version="0.0.6", 12 | packages=[ 13 | "masonite_audit", 14 | "masonite_audit.config", 15 | "masonite_audit.migrations", 16 | "masonite_audit.mixins", 17 | "masonite_audit.models", 18 | "masonite_audit.observer", 19 | "masonite_audit.providers" 20 | ], 21 | package_dir={"": "src"}, 22 | description="Keep track of all your model changes with ease.", 23 | long_description=long_description, 24 | long_description_content_type="text/markdown", 25 | # The project's main homepage. 26 | url="https://github.com/py-package/masonite-audit", 27 | # Author details 28 | author="Yubaraj Shrestha", 29 | author_email="yubaraj@pypackage.com", 30 | # Choose your license 31 | license="MIT license", 32 | # If your package should include things you specify in your MANIFEST.in file 33 | # Use this option if your package needs to include files that are not python files 34 | # like html templates or css files 35 | include_package_data=True, 36 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 37 | classifiers=[ 38 | # How mature is this project? Common values are 39 | # 3 - Alpha 40 | # 4 - Beta 41 | # 5 - Production/Stable 42 | "Development Status :: 5 - Production/Stable", 43 | # Indicate who your project is intended for 44 | "Intended Audience :: Developers", 45 | "Environment :: Web Environment", 46 | # Pick your license as you wish (should match "license" above) 47 | "License :: OSI Approved :: MIT License", 48 | "Operating System :: OS Independent", 49 | # Specify the Python versions you support here. In particular, ensure 50 | # that you indicate whether you support Python 2, Python 3 or both. 51 | "Programming Language :: Python :: 3.7", 52 | "Programming Language :: Python :: 3.8", 53 | "Programming Language :: Python :: 3.9", 54 | "Topic :: Software Development :: Libraries :: Application Frameworks", 55 | "Topic :: Software Development :: Libraries :: Python Modules", 56 | # List package on masonite packages website 57 | "Framework :: Masonite", 58 | ], 59 | # What does your project relate to? 60 | keywords="Masonite, Python, Development, Audit, Audit Log", 61 | # List run-time dependencies here. These will be installed by pip when 62 | # your project is installed. For an analysis of "install_requires" vs pip's 63 | # requirements files see: 64 | # https://packaging.python.org/en/latest/requirements.html 65 | install_requires=["masonite>=4.0<5.0"], 66 | # List additional groups of dependencies here (e.g. development 67 | # dependencies). You can install these using the following syntax, 68 | # for example: 69 | # $ pip install -e .[dev,test] 70 | # $ pip install your-package[dev,test] 71 | extras_require={ 72 | "dev": [ 73 | "black", 74 | "flake8", 75 | "coverage", 76 | "pytest", 77 | "pytest-cov", 78 | "twine>=1.5.0", 79 | "wheel", 80 | ], 81 | }, 82 | # If there are data files included in your packages that need to be 83 | # installed, specify them here. If using Python 2.6 or less, then these 84 | # have to be included in MANIFEST.in as well. 85 | package_data={ 86 | 'templates/index.html': [], 87 | }, 88 | ) 89 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/src/__init__.py -------------------------------------------------------------------------------- /src/masonite_audit/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa F401 2 | from .providers.AuditProvider import AuditProvider 3 | -------------------------------------------------------------------------------- /src/masonite_audit/config/masonite_audit.py: -------------------------------------------------------------------------------- 1 | """Masonite Audit Settings""" 2 | 3 | """ 4 | |-------------------------------------------------------------------------- 5 | | A Heading of The Setting Being Set 6 | |-------------------------------------------------------------------------- 7 | | 8 | | A quick description 9 | | 10 | """ 11 | -------------------------------------------------------------------------------- /src/masonite_audit/masonite_audit.py: -------------------------------------------------------------------------------- 1 | from .observer.audit_observer import AuditObserver 2 | 3 | 4 | class MasoniteAudit: 5 | def observe(self): 6 | from .mixins import Audit 7 | 8 | for model in Audit.__subclasses__(): 9 | model.observe(AuditObserver()) 10 | -------------------------------------------------------------------------------- /src/masonite_audit/migrations/create_audit_logs_table.py: -------------------------------------------------------------------------------- 1 | from masoniteorm.migrations import Migration 2 | 3 | 4 | class CreateAuditLogsTable(Migration): 5 | def up(self): 6 | """Run the migrations.""" 7 | 8 | with self.schema.create("audit_logs") as table: 9 | table.increments("id") 10 | table.integer("editor_id").unsigned().nullable() 11 | table.integer("model_id").unsigned() 12 | table.string("model_name") 13 | table.string("action") 14 | table.json("columns").default("{}") 15 | table.json("old_value").default("{}") 16 | table.json("new_value").default("{}") 17 | table.timestamps() 18 | 19 | def down(self): 20 | """Revert the migrations.""" 21 | 22 | self.schema.drop("audit_logs") 23 | -------------------------------------------------------------------------------- /src/masonite_audit/mixins/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: E501 2 | from .audit import Audit as Audit 3 | -------------------------------------------------------------------------------- /src/masonite_audit/mixins/audit.py: -------------------------------------------------------------------------------- 1 | from ..models.audit_log import AuditLog 2 | 3 | 4 | class Audit(object): 5 | def history(self): 6 | """Returns a list of logs for the model.""" 7 | return ( 8 | AuditLog.where("model_name", self.__class__.get_table_name()) 9 | .where("model_id", self.id) 10 | .order_by("created_at", "desc") 11 | .get() 12 | ) 13 | 14 | def rollback(self, step=1): 15 | """Rolls back a log to the model.""" 16 | logs = self.history() 17 | if len(logs) >= step: 18 | log = logs[step - 1] # because step is index+1 19 | 20 | if log.old_value is not None and "updated_at" in log.old_value: 21 | log.old_value.pop("updated_at") 22 | 23 | self.update(log.old_value) 24 | self.save() 25 | -------------------------------------------------------------------------------- /src/masonite_audit/models/audit_log.py: -------------------------------------------------------------------------------- 1 | from masoniteorm.models import Model 2 | 3 | 4 | class AuditLog(Model): 5 | 6 | __fillable__ = [ 7 | "editor_id", 8 | "model_id", 9 | "model_name", 10 | "action", 11 | "columns", 12 | "old_value", 13 | "new_value", 14 | "created_at", 15 | "updated_at", 16 | ] 17 | 18 | __casts__ = { 19 | "columns": "json", 20 | "old_value": "json", 21 | "new_value": "json", 22 | } 23 | -------------------------------------------------------------------------------- /src/masonite_audit/observer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/src/masonite_audit/observer/__init__.py -------------------------------------------------------------------------------- /src/masonite_audit/observer/audit_observer.py: -------------------------------------------------------------------------------- 1 | import json 2 | from ..models.audit_log import AuditLog 3 | from datetime import datetime 4 | 5 | 6 | class AuditObserver: 7 | def _parse_model(self, model, action): 8 | """Parse the model to get the table name and primary key. 9 | Args: 10 | model (masoniteorm.models.Model): model model. 11 | """ 12 | new_value = model.__attributes__ 13 | old_value = model.__original_attributes__ 14 | 15 | for key in new_value: 16 | if isinstance(new_value.get(key), datetime): 17 | new_value[key] = new_value.get(key).to_datetime_string() 18 | if isinstance(old_value.get(key), datetime): 19 | old_value[key] = old_value.get(key).to_datetime_string() 20 | 21 | data = { 22 | "action": action, 23 | "model_id": model.id, 24 | "model_name": model.get_table_name(), 25 | "columns": json.dumps(model.get_columns()), 26 | "new_value": json.dumps(new_value), 27 | "old_value": json.dumps(old_value), 28 | } 29 | 30 | AuditLog.create(data) 31 | 32 | def created(self, model): 33 | """Handle the model "created" event. 34 | Args: 35 | model (masoniteorm.models.Model): model model. 36 | """ 37 | self._parse_model(model, "CREATED") 38 | 39 | def saved(self, model): 40 | """Handle the model "saved" event. 41 | Args: 42 | model (masoniteorm.models.Model): model model. 43 | """ 44 | self._parse_model(model, "SAVED") 45 | 46 | def updated(self, model): 47 | """Handle the model "updated" event. 48 | Args: 49 | model (masoniteorm.models.Model): model model. 50 | """ 51 | self._parse_model(model, "UPDATED") 52 | 53 | def deleted(self, model): 54 | """Handle the model "deleted" event. 55 | Args: 56 | model (masoniteorm.models.Model): model model. 57 | """ 58 | self._parse_model(model, "DELETED") 59 | -------------------------------------------------------------------------------- /src/masonite_audit/providers/AuditProvider.py: -------------------------------------------------------------------------------- 1 | """A AuditProvider Service Provider.""" 2 | 3 | from masonite.packages import PackageProvider 4 | 5 | from ..masonite_audit import MasoniteAudit 6 | 7 | 8 | class AuditProvider(PackageProvider, MasoniteAudit): 9 | def configure(self): 10 | """Register objects into the Service Container.""" 11 | ( 12 | self.root("masonite_audit") 13 | .name("masonite-audit") 14 | .config("config/masonite_audit.py", publish=True) 15 | .migrations("migrations/create_audit_logs_table.py") 16 | ) 17 | 18 | def register(self): 19 | super().register() 20 | self.observe() 21 | 22 | def boot(self): 23 | """Boots services required by the container.""" 24 | pass 25 | -------------------------------------------------------------------------------- /src/masonite_audit/providers/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa: E501 2 | from .AuditProvider import AuditProvider 3 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/__init__.py -------------------------------------------------------------------------------- /tests/integrations/Kernel.py: -------------------------------------------------------------------------------- 1 | from masonite.foundation import response_handler 2 | from masonite.storage import StorageCapsule 3 | from masonite.auth import Sign 4 | from masonite.environment import LoadEnvironment 5 | from masonite.utils.structures import load 6 | from masonite.utils.location import base_path 7 | from masonite.middleware import ( 8 | SessionMiddleware, 9 | EncryptCookies, 10 | LoadUserMiddleware, 11 | MaintenanceModeMiddleware, 12 | ) 13 | from masonite.routes import Route 14 | from masonite.configuration.Configuration import Configuration 15 | from masonite.configuration import config 16 | 17 | from .app.middlewares.VerifyCsrfToken import VerifyCsrfToken 18 | 19 | 20 | class Kernel: 21 | 22 | http_middleware = [MaintenanceModeMiddleware, EncryptCookies] 23 | 24 | route_middleware = { 25 | "web": [SessionMiddleware, LoadUserMiddleware, VerifyCsrfToken], 26 | } 27 | 28 | def __init__(self, app): 29 | self.application = app 30 | 31 | def register(self): 32 | # Register routes 33 | self.load_environment() 34 | self.register_configurations() 35 | self.register_middleware() 36 | self.register_routes() 37 | self.register_database() 38 | self.register_templates() 39 | self.register_storage() 40 | 41 | def load_environment(self): 42 | LoadEnvironment() 43 | 44 | def register_configurations(self): 45 | # load configuration 46 | self.application.bind("config.location", "tests/integrations/config") 47 | configuration = Configuration(self.application) 48 | configuration.load() 49 | self.application.bind("config", configuration) 50 | key = config("application.key") 51 | self.application.bind("key", key) 52 | self.application.bind("sign", Sign(key)) 53 | # set locations 54 | self.application.bind("resources.location", "tests/integrations/resources/") 55 | self.application.bind("controllers.location", "tests/integrations/app/controllers") 56 | self.application.bind("jobs.location", "tests/integrations/jobs") 57 | self.application.bind("providers.location", "tests/integrations/providers") 58 | self.application.bind("mailables.location", "tests/integrations/mailables") 59 | self.application.bind("listeners.location", "tests/integrations/listeners") 60 | self.application.bind("validation.location", "tests/integrations/validation") 61 | self.application.bind("notifications.location", "tests/integrations/notifications") 62 | self.application.bind("events.location", "tests/integrations/events") 63 | self.application.bind("tasks.location", "tests/integrations/tasks") 64 | self.application.bind("models.location", "tests/integrations/app/models") 65 | self.application.bind("observers.location", "tests/integrations/models/observers") 66 | self.application.bind("policies.location", "tests/integrations/policies") 67 | self.application.bind("commands.location", "tests/integrations/commands") 68 | self.application.bind("middlewares.location", "tests/integrations/app/middlewares") 69 | 70 | self.application.bind("server.runner", "masonite.commands.ServeCommand.main") 71 | 72 | def register_middleware(self): 73 | self.application.make("middleware").add(self.route_middleware).add(self.http_middleware) 74 | 75 | def register_routes(self): 76 | Route.set_controller_locations(self.application.make("controllers.location")) 77 | self.application.bind("routes.location", "tests/integrations/routes/web") 78 | self.application.make("router").add( 79 | Route.group( 80 | load(self.application.make("routes.location"), "ROUTES"), middleware=["web"] 81 | ) 82 | ) 83 | 84 | def register_database(self): 85 | from masoniteorm.query import QueryBuilder 86 | 87 | self.application.bind( 88 | "builder", 89 | QueryBuilder(connection_details=config("database.databases")), 90 | ) 91 | 92 | self.application.bind("migrations.location", "tests/integrations/databases/migrations") 93 | self.application.bind("seeds.location", "tests/integrations/databases/seeds") 94 | 95 | self.application.bind("resolver", config("database.db")) 96 | 97 | def register_templates(self): 98 | self.application.bind("views.location", "tests/integrations/templates/") 99 | 100 | def register_storage(self): 101 | storage = StorageCapsule() 102 | storage.add_storage_assets(config("filesystem.staticfiles")) 103 | self.application.bind("storage_capsule", storage) 104 | 105 | self.application.set_response_handler(response_handler) 106 | self.application.use_storage_path(base_path("storage")) 107 | -------------------------------------------------------------------------------- /tests/integrations/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/app/__init__.py -------------------------------------------------------------------------------- /tests/integrations/app/controllers/WelcomeController.py: -------------------------------------------------------------------------------- 1 | """A WelcomeController Module.""" 2 | import random 3 | from masonite.views import View 4 | from masonite.controllers import Controller 5 | 6 | from tests.integrations.app.models.User import User 7 | 8 | 9 | class WelcomeController(Controller): 10 | """WelcomeController Controller Class.""" 11 | 12 | def show(self, view: View): 13 | user = User.first() 14 | user.update({"name": f"John {random.randint(100, 900)}"}) 15 | user.rollback() 16 | return user 17 | 18 | def test(self): 19 | user = User.first() 20 | 21 | return user 22 | -------------------------------------------------------------------------------- /tests/integrations/app/controllers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/app/controllers/__init__.py -------------------------------------------------------------------------------- /tests/integrations/app/middlewares/VerifyCsrfToken.py: -------------------------------------------------------------------------------- 1 | from masonite.middleware import VerifyCsrfToken as Middleware 2 | 3 | 4 | class VerifyCsrfToken(Middleware): 5 | 6 | exempt = [] 7 | -------------------------------------------------------------------------------- /tests/integrations/app/models/User.py: -------------------------------------------------------------------------------- 1 | """User Model.""" 2 | from masoniteorm.models import Model 3 | from masoniteorm.scopes import SoftDeletesMixin 4 | from masonite.authentication import Authenticates 5 | from src.masonite_audit.mixins import Audit 6 | 7 | 8 | class User(Model, SoftDeletesMixin, Authenticates, Audit): 9 | """User Model.""" 10 | 11 | __fillable__ = ["name", "email", "password"] 12 | __hidden__ = ["password"] 13 | __auth__ = "email" 14 | -------------------------------------------------------------------------------- /tests/integrations/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/config/__init__.py -------------------------------------------------------------------------------- /tests/integrations/config/application.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | KEY = env("APP_KEY", "-RkDOqXojJIlsF_I8wWiUq_KRZ0PtGWTOZ676u5HtLg=") 5 | 6 | HASHING = { 7 | "default": env("HASHING_FUNCTION", "bcrypt"), 8 | "bcrypt": {"rounds": 10}, 9 | "argon2": {"memory": 1024, "threads": 2, "time": 2}, 10 | } 11 | 12 | APP_URL = env("APP_URL", "http://localhost:8000/") 13 | -------------------------------------------------------------------------------- /tests/integrations/config/auth.py: -------------------------------------------------------------------------------- 1 | from ..app.models.User import User 2 | 3 | GUARDS = { 4 | "default": "web", 5 | "web": {"model": User}, 6 | "password_reset_table": "password_resets", 7 | "password_reset_expiration": 1440, # in minutes. 24 hours. None if disabled 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/config/broadcast.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | BROADCASTS = { 5 | "default": "pusher", 6 | "pusher": { 7 | "driver": "pusher", 8 | "client": env("PUSHER_CLIENT"), 9 | "app_id": env("PUSHER_APP_ID"), 10 | "secret": env("PUSHER_SECRET"), 11 | "cluster": env("PUSHER_CLUSTER"), 12 | "ssl": False, 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /tests/integrations/config/cache.py: -------------------------------------------------------------------------------- 1 | # from masonite.environment import env 2 | 3 | 4 | STORES = { 5 | "default": "local", 6 | "local": { 7 | "driver": "file", 8 | "location": "storage/framework/cache" 9 | # 10 | }, 11 | "redis": { 12 | "driver": "redis", 13 | "host": "127.0.0.1", 14 | "port": "6379", 15 | "password": "", 16 | "name": "project_name", 17 | }, 18 | "memcache": { 19 | "driver": "memcache", 20 | "host": "127.0.0.1", 21 | "port": "11211", 22 | "password": "", 23 | "name": "project_name", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /tests/integrations/config/database.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import LoadEnvironment, env 2 | from masoniteorm.connections import ConnectionResolver 3 | 4 | # Loads in the environment variables when this page is imported. 5 | LoadEnvironment() 6 | 7 | """ 8 | The connections here don't determine the database but determine the "connection". 9 | They can be named whatever you want. 10 | """ 11 | DATABASES = { 12 | "default": env("DB_CONNECTION", "sqlite"), 13 | "sqlite": { 14 | "driver": "sqlite", 15 | "database": env("SQLITE_DB_DATABASE", "masonite.sqlite3"), 16 | "prefix": "", 17 | "log_queries": env("DB_LOG"), 18 | }, 19 | "mysql": { 20 | "host": "127.0.0.1", 21 | "driver": "mysql", 22 | "database": "masonite_permission", 23 | "user": "meyubaraj", 24 | "password": "MDB@123#go", 25 | "port": 3306, 26 | "prefix": "", 27 | "grammar": "mysql", 28 | "options": { 29 | "charset": "utf8mb4", 30 | }, 31 | "log_queries": env("DB_LOG"), 32 | }, 33 | "postgres": { 34 | "host": "127.0.0.1", 35 | "driver": "postgres", 36 | "database": "masonite_permission", 37 | "user": "meyubaraj", 38 | "password": "MDB@123#go", 39 | "port": 5432, 40 | "prefix": "", 41 | "grammar": "postgres", 42 | "log_queries": env("DB_LOG"), 43 | }, 44 | "mssql": { 45 | "driver": "mssql", 46 | "host": env("MSSQL_DATABASE_HOST"), 47 | "user": env("MSSQL_DATABASE_USER"), 48 | "password": env("MSSQL_DATABASE_PASSWORD"), 49 | "database": env("MSSQL_DATABASE_DATABASE"), 50 | "port": env("MSSQL_DATABASE_PORT"), 51 | "prefix": "", 52 | "log_queries": env("DB_LOG"), 53 | }, 54 | } 55 | 56 | DB = ConnectionResolver().set_connection_details(DATABASES) 57 | -------------------------------------------------------------------------------- /tests/integrations/config/exceptions.py: -------------------------------------------------------------------------------- 1 | HANDLERS = {"stack_overflow": True, "solutions": True} 2 | -------------------------------------------------------------------------------- /tests/integrations/config/filesystem.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | from masonite.utils.location import base_path 3 | 4 | 5 | DISKS = { 6 | "default": "local", 7 | "local": {"driver": "file", "path": base_path("storage/framework/filesystem")}, 8 | "s3": { 9 | "driver": "s3", 10 | "client": env("AWS_CLIENT"), 11 | "secret": env("AWS_SECRET"), 12 | "bucket": env("AWS_BUCKET"), 13 | }, 14 | } 15 | 16 | STATICFILES = { 17 | "tests/integrations/storage/static": "static/", 18 | "tests/integrations/storage/compiled": "assets/", 19 | "tests/integrations/storage/public": "/", 20 | } 21 | -------------------------------------------------------------------------------- /tests/integrations/config/mail.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | FROM_EMAIL = env("MAIL_FROM", "no-reply@masonite.com") 5 | 6 | DRIVERS = { 7 | "default": env("MAIL_DRIVER", "terminal"), 8 | "smtp": { 9 | "host": env("MAIL_HOST"), 10 | "port": env("MAIL_PORT"), 11 | "username": env("MAIL_USERNAME"), 12 | "password": env("MAIL_PASSWORD"), 13 | "from": FROM_EMAIL, 14 | }, 15 | "mailgun": { 16 | "domain": env("MAILGUN_DOMAIN"), 17 | "secret": env("MAILGUN_SECRET"), 18 | "from": FROM_EMAIL, 19 | }, 20 | "terminal": { 21 | "from": FROM_EMAIL, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /tests/integrations/config/masonite-audit.py: -------------------------------------------------------------------------------- 1 | """Masonite Audit Settings""" 2 | 3 | """ 4 | |-------------------------------------------------------------------------- 5 | | A Heading of The Setting Being Set 6 | |-------------------------------------------------------------------------- 7 | | 8 | | A quick description 9 | | 10 | """ 11 | -------------------------------------------------------------------------------- /tests/integrations/config/masonite_audit.py: -------------------------------------------------------------------------------- 1 | """Masonite Audit Settings""" 2 | 3 | """ 4 | |-------------------------------------------------------------------------- 5 | | A Heading of The Setting Being Set 6 | |-------------------------------------------------------------------------- 7 | | 8 | | A quick description 9 | | 10 | """ 11 | -------------------------------------------------------------------------------- /tests/integrations/config/notification.py: -------------------------------------------------------------------------------- 1 | from masonite.environment import env 2 | 3 | 4 | DRIVERS = { 5 | "slack": { 6 | "token": env("SLACK_TOKEN", ""), # used for API mode 7 | "webhook": env("SLACK_WEBHOOK", ""), # used for webhook mode 8 | }, 9 | "vonage": { 10 | "key": env("VONAGE_KEY", ""), 11 | "secret": env("VONAGE_SECRET", ""), 12 | "sms_from": env("VONAGE_SMS_FROM", "+33000000000"), 13 | }, 14 | "database": { 15 | "connection": "sqlite", 16 | "table": "notifications", 17 | }, 18 | } 19 | 20 | DRY = False 21 | -------------------------------------------------------------------------------- /tests/integrations/config/providers.py: -------------------------------------------------------------------------------- 1 | from masonite.providers import ( 2 | RouteProvider, 3 | FrameworkProvider, 4 | ViewProvider, 5 | WhitenoiseProvider, 6 | ExceptionProvider, 7 | MailProvider, 8 | SessionProvider, 9 | QueueProvider, 10 | CacheProvider, 11 | EventProvider, 12 | StorageProvider, 13 | HelpersProvider, 14 | BroadcastProvider, 15 | AuthenticationProvider, 16 | AuthorizationProvider, 17 | HashServiceProvider, 18 | ORMProvider, 19 | ) 20 | 21 | 22 | from masonite.scheduling.providers import ScheduleProvider 23 | from masonite.notification.providers import NotificationProvider 24 | from masonite.validation.providers import ValidationProvider 25 | 26 | from src.masonite_audit.providers import AuditProvider 27 | 28 | PROVIDERS = [ 29 | FrameworkProvider, 30 | HelpersProvider, 31 | RouteProvider, 32 | ViewProvider, 33 | WhitenoiseProvider, 34 | ExceptionProvider, 35 | MailProvider, 36 | NotificationProvider, 37 | SessionProvider, 38 | CacheProvider, 39 | QueueProvider, 40 | ScheduleProvider, 41 | EventProvider, 42 | StorageProvider, 43 | BroadcastProvider, 44 | HashServiceProvider, 45 | AuthenticationProvider, 46 | ValidationProvider, 47 | AuthorizationProvider, 48 | ORMProvider, 49 | AuditProvider, 50 | ] 51 | -------------------------------------------------------------------------------- /tests/integrations/config/queue.py: -------------------------------------------------------------------------------- 1 | # from masonite.environment import env 2 | 3 | 4 | DRIVERS = { 5 | "default": "async", 6 | "database": { 7 | "connection": "sqlite", 8 | "table": "jobs", 9 | "failed_table": "failed_jobs", 10 | "attempts": 3, 11 | "poll": 5, 12 | }, 13 | "redis": { 14 | # 15 | }, 16 | "amqp": { 17 | "username": "guest", 18 | "password": "guest", 19 | "port": "5672", 20 | "vhost": "", 21 | "host": "localhost", 22 | "channel": "default", 23 | "queue": "masonite4", 24 | }, 25 | "async": { 26 | "blocking": True, 27 | "callback": "handle", 28 | "mode": "threading", 29 | "workers": 1, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /tests/integrations/config/session.py: -------------------------------------------------------------------------------- 1 | # from masonite.environment import env 2 | 3 | 4 | DRIVERS = { 5 | "default": "cookie", 6 | "cookie": {}, 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/databases/migrations/2021_01_09_033202_create_password_reset_table.py: -------------------------------------------------------------------------------- 1 | from masoniteorm.migrations import Migration 2 | 3 | 4 | class CreatePasswordResetTable(Migration): 5 | def up(self): 6 | """Run the migrations.""" 7 | with self.schema.create("password_resets") as table: 8 | table.string("email").unique() 9 | table.string("token") 10 | table.datetime("expires_at").nullable() 11 | table.datetime("created_at") 12 | 13 | def down(self): 14 | """Revert the migrations.""" 15 | self.schema.drop("password_resets") 16 | -------------------------------------------------------------------------------- /tests/integrations/databases/migrations/2021_01_09_043202_create_users_table.py: -------------------------------------------------------------------------------- 1 | from masoniteorm.migrations import Migration 2 | 3 | 4 | class CreateUsersTable(Migration): 5 | def up(self): 6 | """Run the migrations.""" 7 | with self.schema.create("users") as table: 8 | table.increments("id") 9 | table.string("name") 10 | table.string("email").unique() 11 | table.string("password") 12 | table.string("second_password").nullable() 13 | table.string("remember_token").nullable() 14 | table.string("phone").nullable() 15 | table.timestamp("verified_at").nullable() 16 | table.timestamps() 17 | table.soft_deletes() 18 | 19 | def down(self): 20 | """Revert the migrations.""" 21 | self.schema.drop("users") 22 | -------------------------------------------------------------------------------- /tests/integrations/databases/migrations/2022_04_21_110158_create_audit_logs_table.py: -------------------------------------------------------------------------------- 1 | """CreateAuditLogsTable Migration.""" 2 | 3 | from masoniteorm.migrations import Migration 4 | 5 | 6 | class CreateAuditLogsTable(Migration): 7 | def up(self): 8 | """ 9 | Run the migrations. 10 | """ 11 | with self.schema.create("audit_logs") as table: 12 | table.increments("id") 13 | table.integer("editor_id").unsigned().nullable() 14 | table.integer("model_id").unsigned() 15 | table.string("model_name") 16 | table.string("action") 17 | table.json("columns").default("{}") 18 | table.json("old_value").default("{}") 19 | table.json("new_value").default("{}") 20 | table.timestamps() 21 | 22 | def down(self): 23 | """ 24 | Revert the migrations. 25 | """ 26 | self.schema.drop("audit_logs") 27 | -------------------------------------------------------------------------------- /tests/integrations/databases/seeds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/databases/seeds/__init__.py -------------------------------------------------------------------------------- /tests/integrations/databases/seeds/database_seeder.py: -------------------------------------------------------------------------------- 1 | """Base Database Seeder Module.""" 2 | from masoniteorm.seeds import Seeder 3 | 4 | from .user_table_seeder import UserTableSeeder 5 | 6 | 7 | class DatabaseSeeder(Seeder): 8 | def run(self): 9 | """Run the database seeds.""" 10 | self.call(UserTableSeeder) 11 | -------------------------------------------------------------------------------- /tests/integrations/databases/seeds/user_table_seeder.py: -------------------------------------------------------------------------------- 1 | """UserTableSeeder Seeder.""" 2 | from masoniteorm.seeds import Seeder 3 | from masonite.facades import Hash 4 | 5 | from tests.integrations.app.models.User import User 6 | 7 | 8 | class UserTableSeeder(Seeder): 9 | def run(self): 10 | """Run the database seeds.""" 11 | User.create( 12 | { 13 | "name": "Joe", 14 | "email": "user@example.com", 15 | "password": Hash.make("secret"), 16 | "phone": "+123456789", 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /tests/integrations/resources/css/app.css: -------------------------------------------------------------------------------- 1 | /* Put your CSS here */ 2 | -------------------------------------------------------------------------------- /tests/integrations/resources/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | require("./bootstrap.js") 3 | -------------------------------------------------------------------------------- /tests/integrations/resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We'll load the axios HTTP library which allows us to easily issue requests 3 | * to our Masonite back-end. This library automatically handles sending the 4 | * CSRF token as a header based on the value of the "XSRF" token cookie. 5 | */ 6 | 7 | window.axios = require('axios'); 8 | 9 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 10 | 11 | /** 12 | * Next we will register the CSRF Token as a common header with Axios so that 13 | * all outgoing HTTP requests automatically have it attached. This is just 14 | * a simple convenience so we don't have to attach every token manually. 15 | */ 16 | 17 | let token = document.head.querySelector('meta[name="csrf-token"]'); 18 | 19 | if (token) { 20 | window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; 21 | } else { 22 | console.error('CSRF token not found: https://docs.masoniteproject.com/features/csrf#ajax-vue-axios'); 23 | } 24 | -------------------------------------------------------------------------------- /tests/integrations/routes/web.py: -------------------------------------------------------------------------------- 1 | from masonite.routes import Route 2 | 3 | ROUTES = [ 4 | Route.get("/", "WelcomeController@show"), 5 | Route.get("/test", "WelcomeController@test"), 6 | ] 7 | -------------------------------------------------------------------------------- /tests/integrations/storage/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/storage/.gitignore -------------------------------------------------------------------------------- /tests/integrations/storage/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/storage/public/favicon.ico -------------------------------------------------------------------------------- /tests/integrations/storage/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/storage/public/logo.png -------------------------------------------------------------------------------- /tests/integrations/storage/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /tests/integrations/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/integrations/templates/__init__.py -------------------------------------------------------------------------------- /tests/integrations/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% block title %}Masonite 4{% endblock %} 10 | 11 | 12 | {% block head %} 13 | 14 | {% endblock %} 15 | 16 | 17 | 18 | {% block content %}{% endblock %} 19 | {% block js %} 20 | 21 | {% endblock %} 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/integrations/templates/maintenance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Server Maintenance 9 | 10 | 11 | 12 | 13 | {% block content %} 14 |
15 |

Sorry, this site is currently down for maintenance.

16 |
17 | {% endblock %} 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/integrations/templates/welcome.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Welcome on Masonite 4{% endblock %} 3 | 4 | {% block content %} 5 |
6 | Masonite Logo 7 | 8 | 29 | 30 |
31 | {% endblock %} -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-package/masonite-audit/f3243c2a6af99bd007293c3662c6eeb9418a01b2/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/test_audit.py: -------------------------------------------------------------------------------- 1 | from masonite.tests import TestCase 2 | from masoniteorm.query import QueryBuilder 3 | from tests.integrations.app.models.User import User 4 | from masonite.facades import Hash 5 | 6 | 7 | class TestAudit(TestCase): 8 | @classmethod 9 | def setUpClass(cls): 10 | QueryBuilder().table("audit_logs").truncate(True) 11 | QueryBuilder().table("users").truncate(True) 12 | 13 | def tearDown(self): 14 | super().tearDown() 15 | QueryBuilder().table("audit_logs").truncate(True) 16 | QueryBuilder().table("users").truncate(True) 17 | 18 | def test_audit_created(self): 19 | user = User.create( 20 | { 21 | "name": "Yubaraj", 22 | "email": "user@example.com", 23 | "password": Hash.make("secret"), 24 | "phone": "+123456789", 25 | } 26 | ) 27 | 28 | self.assertDatabaseHas( 29 | "users", 30 | { 31 | "email": "user@example.com", 32 | }, 33 | ) 34 | 35 | self.assertDatabaseHas( 36 | "audit_logs", 37 | { 38 | "model_id": user.id, 39 | "model_name": "users", 40 | "action": "CREATED", 41 | }, 42 | ) 43 | 44 | def test_audit_history(self): 45 | user = User.create( 46 | { 47 | "name": "Yubaraj", 48 | "email": "user@example.com", 49 | "password": Hash.make("secret"), 50 | "phone": "+123456789", 51 | } 52 | ) 53 | 54 | self.assertTrue(user.history().count() == 1) 55 | 56 | user.update({"name": "UB"}) 57 | 58 | self.assertTrue(user.history().count() == 2) 59 | 60 | def test_audit_rollback(self): 61 | user = User.create( 62 | { 63 | "name": "Yubaraj", 64 | "email": "user@example.com", 65 | "password": Hash.make("secret"), 66 | "phone": "+123456789", 67 | } 68 | ) 69 | 70 | self.assertDatabaseHas( 71 | "users", 72 | { 73 | "name": "Yubaraj", 74 | }, 75 | ) 76 | 77 | user.update({"name": "UB"}) 78 | 79 | self.assertDatabaseHas( 80 | "users", 81 | { 82 | "name": "UB", 83 | }, 84 | ) 85 | 86 | user.rollback() 87 | 88 | self.assertDatabaseHas( 89 | "users", 90 | { 91 | "name": "Yubaraj", 92 | }, 93 | ) 94 | -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | from masonite.foundation import Application, Kernel 2 | from tests.integrations.config.providers import PROVIDERS 3 | from tests.integrations.Kernel import Kernel as ApplicationKernel 4 | 5 | 6 | """Start The Application Instance.""" 7 | application = Application("tests/integrations") 8 | 9 | """Now Bind important providers needed to make the framework work.""" 10 | application.register_providers(Kernel, ApplicationKernel) 11 | 12 | """Now Bind important application specific providers needed to make the application work.""" 13 | application.add_providers(*PROVIDERS) 14 | --------------------------------------------------------------------------------