Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mibs510 committed Jun 11, 2023
0 parents commit f1535e3
Show file tree
Hide file tree
Showing 1,338 changed files with 219,527 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git
__pycache__
*.pyc
*.pyo
*.pyd
21 changes: 21 additions & 0 deletions .github/workflows/python-semantic-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Semantic Release

on:
push:
branches:
- master

jobs:
release:
runs-on: ubuntu-latest
concurrency: release

steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Python Semantic Release
uses: relekang/python-semantic-release@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
52 changes: 52 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# tests and coverage
*.pytest_cache
.coverage

# database & logs
*.db
*.sqlite3
*.log
*.sqlite

# venv
env
venv

# other
.DS_Store

# sphinx docs
_build
_static
_templates

# javascript
package-lock.json
.vscode/symbols.json

apps/static/assets/node_modules
apps/static/assets/yarn.lock
apps/static/assets/.temp

.env
migrations

# Secrets
secrets.ini
ipban.ini

# Illustrator files
*.ai

# PyCharm
.idea

# Uploads directory
apps/uploads/*

# Synology Drive Client
.sync-exclude.lst
Empty file added CHANGELOG.md
Empty file.
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM python:3.9

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV FLASK_APP run.py
ENV DEBUG True

COPY requirements.txt .

# install python dependencies
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt

COPY env.sample .env

COPY . .

RUN flask db init
RUN flask db migrate
RUN flask db upgrade

# gunicorn
CMD ["gunicorn", "--config", "gunicorn-cfg.py", "run:app"]
127 changes: 127 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
![OpenALPR-Webhook](media/openalpr-webhook-preview.png)

OpenALPR-Webhook is a self-hosted web application that accepts [Rekor Scout™](https://cloud.openalpr.com/) POST data allowing longer data retention.
It was designed with an emphasis on security to meet organization/business needs.

- 👉 Simple clean dashboard with statistics
- 👉 Custom unlimited alerts
- 👉 Notifications via email or SMS (Twilio)
- 👉 Customize report branding
- 👉 User management and roles
- 👉 Forced camera focus (Dahua IPCs)
- 👉 IPBan (fail2ban for Flask) with [IPAbuseDB.com](https://ipabusedb.com) integration
- 👉 Webhook endpoint security


# 🐛 Known Bugs
- Manually requeuing jobs fail
- ~~Searching plates will only work if pagination position is on page 1~~
- ~~This is a grid.js issue [#1314](https://github.com/grid-js/gridjs/issues/1314) [#1344](https://github.com/grid-js/gridjs/pull/1334) [#1311](https://github.com/grid-js/gridjs/issues/1311).~~

# ✨ Upcoming Features
- Integrate [Apprise](https://github.com/caronc/apprise)
- Enhance search functionality
- Add
- Direction
- Color
- From/To Date
- Camera
- Location
- License Plate Region
- Improve user management
- Add a password reset form for admins
- Add email notifications for new users
- Beautify email notifications with HTML
- View alerts publicly without authentication using a secure expirable routing method.
- Ability for admins to
- Export databases
- Export/import settings
- Add audit logs for each action
- Add support for 2FA/MFA


# Installation

### Docker
TBD

### Bare Server
1. apt-install python3.10 redis-server && systemctl enable redis-server && systemctl start redis-server
2. git https://github.com/mibs510/OpenALPR-Webhook
3. cd OpenALPR-Webhook
4. pip3 install -r requirements.txt
5. ./venv/Scripts/activate
6. ./app.py --host=0.0.0.0 --port=8080

### New Instance
Head over to the URL of your server. You will be required to login. Click on 'register' to create a super admin account.
<br>
After creating a super admin account, the register link will disappear as a protective measure against unauthorized account creation.
<br>
Accounts will need to be created manually by an administrator under Settings/Users

# Documentation
### Dashboard
___

### Alerts/Blacklist
___
### Alerts/Search
___
### Search
___
### Settings/Agents
___
> Available to administrators only.
>
### Settings/Cameras
___
> Available to administrators only.
>
### Settings/General
___
> Available to administrators only.
>
Disable uuid_img download (pulls from agent if available real-time when viewing/printing reports)
### Settings/Maintenance/App
___
Reinitiate cache
<br>
Redownload all missing uuid_imgs to db/locally
<br>
Trim database to keep X months of plates
<br>
Trim database to keep x months of high-res/uuid_imgs
<br>
Remove all high-res/uuid_imgs from db
> Available to administrators only.
>
### Settings/Maintenance/Redis
___
> Available to administrators only.
>
### Settings/Profile
___
Users can edit basic information about themselves such as name, website, email address, phone number, time zone, etc.
<br>
Each user has a unique `API_KEY`. The `API_KEY` key used to authorize Rekor Scout to POST data onto the webhook endpoint.
<br>
Administrators can set a global setting to limit which `API_KEY`'s can POST data. Refer to Settings/General.
To begin receiving data into OpenALPR-Webhook, copy your `API_KEY` into [Rekor Scout](cloud.openalpr.com) > Configuration > WebHooks Configuration > Add New Webhook > Custom Data
<br>
`API_KEY: vvvvvvvv-wwww-xxxx-yyyy-zzzzzzzzzzzz`
<br>
Be sure to fill in all other fields such as Destination URL, Description, check Send All Plate Reads, check Send Matching Alerts, and check Send Reads missing plate.

### Settings/Notifications
___
> Available to administrators only.
>
### Settings/Users
___
> Available to administrators only.
>
Administrators can create users, edit users, change user roles, and suspend user accounts.
<br>
Once an account has been created, it cannot be deleted. This is to preserve accounts and their API tokens for audit
purposes.
37 changes: 37 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from apps import create_app, db
from apps.config import config_dict
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from flask_minify import Minify
import os

# WARNING: Don't run with debug turned on in production!
DEBUG = os.getenv('DEBUG', 'False') == 'True'

# The configuration
get_config_mode = 'Debug' if DEBUG else 'Production'

# Load the configuration using the default values
app_config = config_dict[get_config_mode.capitalize()]

app = create_app(app_config)
app.config['ENV'] = get_config_mode.capitalize()
Migrate(app, db)

# DB Migration
manager = Manager(app)
manager.add_command('db', MigrateCommand)

if not DEBUG:
Minify(app=app, html=True, js=False, cssless=False)


if DEBUG:
app.logger.info('DEBUG = ' + str(DEBUG))
app.logger.info('Page Compression = ' + 'FALSE' if DEBUG else 'TRUE')
app.logger.info('DBMS = ' + app_config.SQLALCHEMY_DATABASE_URI)


if __name__ == "__main__":
with app.app_context():
app.run(host="0.0.0.0", port=8080)
102 changes: 102 additions & 0 deletions apps/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import platform
import subprocess
from datetime import datetime

from flask_ipban import IpBan
from flask_migrate import Migrate
from redis import Redis
from rq import Queue
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy, declarative_base
from importlib import import_module
from flask_mail import Mail

import version
from apps.alpr.ipban_config import IPBanConfig
from apps.alpr.enums import WorkerType

mail = Mail()
db = SQLAlchemy()
migrate = Migrate()
default_q = Queue(WorkerType.General.value, connection=Redis())
camera_q = Queue(WorkerType.Camera.value, connection=Redis())
Base = declarative_base()
login_manager = LoginManager()
ip_ban_config = IPBanConfig()
ip_ban = IpBan(ban_count=ip_ban_config.ban_count, ban_seconds=ip_ban_config.ban_seconds, persist=ip_ban_config.persist,
record_dir=ip_ban_config.record_dir, ip_header=ip_ban_config.ip_header)


def register_extensions(app):
db.init_app(app)
login_manager.init_app(app)


def register_blueprints(app):
for module_name in ('api', 'authentication', 'alpr.routes.alert', 'alpr.routes.alerts',
'alpr.routes.alerts.custom', 'alpr.routes.alerts.rekor', 'alpr.routes.capture',
'alpr.routes.search', 'alpr.routes.settings', 'alpr.routes.settings.agents',
'alpr.routes.settings.cameras', 'alpr.routes.settings.general',
'alpr.routes.settings.maintenance', 'alpr.routes.settings.maintenance.rq_dashboard',
'alpr.routes.settings.notifications', 'alpr.routes.settings.profile',
'alpr.routes.settings.users', 'home'):
module = import_module('apps.{}.routes'.format(module_name))
app.register_blueprint(module.blueprint)


def configure_database(app):
@app.context_processor
def inject_global_vars():
return dict(app_version=version.__version__)

@app.before_first_request
def initialize_databases():
# db.create_all()
pass

@app.before_first_request
def initialize_settings():
# Initiate cache when needed
from apps.alpr.models.cache import Cache
now = datetime.now()
last_year = now.year - 1
this_year = now.year
next_year = now.year + 1

Cache.filter_by_year(last_year)
Cache.filter_by_year(this_year)
Cache.filter_by_year(next_year)

# Create default settings when needed
from apps.alpr.models.settings import GeneralSettings
settings = GeneralSettings.get_settings()

if settings is None:
settings = GeneralSettings()
settings.save()

# Start redis workers on Linux only
if platform.system() == "Linux":
from apps.alpr.models.settings import CameraSettings
camera_workers = len(CameraSettings.get_all_enabled())
workers_cmd = subprocess.run(["./workers.py", "-c", camera_workers, "-g", camera_workers])
print("workers_cmd.returncode = {}".format(workers_cmd.returncode))
print("workers_cmd.stdout = {}".format(workers_cmd.stdout))
print("workers_cmd.stderr = {}".format(workers_cmd.stderr))


def create_app(config) -> Flask:
app = Flask(__name__)
app.config.from_object(config)
mail.init_app(app)
ip_ban.init_app(app)
register_extensions(app)
register_blueprints(app)
db.init_app(app)
migrate.init_app(app, db, render_as_batch=True)
configure_database(app)

with app.app_context():
db.create_all()
return app
11 changes: 11 additions & 0 deletions apps/alpr/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Database
__alpr_alert_db__ = "apps/db/alpr_alert.db"
__alpr_group_db__ = "apps/db/alpr_group.db"
__cache_db__ = "apps/db/cache.db"
__vehicle_db__ = "apps/db/vehicle.db"

# Json
__alpr_alert_json__ = "apps/db/alpr_alert.json"
__alpr_group_json__ = "apps/db/alpr_group.json"
__cache_json__ = "apps/db/cache.json"
__vehicle_json__ = "apps/db/vehicle.json"
Loading

0 comments on commit f1535e3

Please sign in to comment.