Skip to content

Commit

Permalink
Dev merge (#68)
Browse files Browse the repository at this point in the history
* Very various code revork and refactoring (#29)

* Smoke try...

* Fix problem with user name and icon

* WIP: rework, bug fixes, renaming, etc

* WIP: refactor, stage 0

* WIP: refactor, stage 1

* WIP: refactor, stage 2

* WIP: refactor, web API separated

* API extended

* WIP: AI-generated stuff for user table just for test:)

* Web API extended for devices

* Fix and rework auth

* Some hacks about development described

* Add columns to users table

* Small fixes: delete user and device menu button

* Config and logging refactored

* Latest key API endpoind extended

* Load config from file + API + Settings page(WIP)

* Create user API fix: return code for existing user

* Add functionality to users page

* Fix add user

* Add latest key time

* Basic slack notifyer implemented

* Fix slack warning

* Devices pages added

* Added "Delete device" functionality + some fixes

* Add settings JSON editor

* Pylint + Autopep8 fixes

---------

Co-authored-by: Anna Deeva <[email protected]>

* Delete file, forgotten from previous commit

* Fixed docker file and add ci for build docker images

* change to correct tag

* Implement build docker image with correct tags

* Implement creating database on start if not exist

* Flash RFID readers from admin panel (#42)

* Automatic RFID reader code reworked and refined.

Also some bug fixes etc...

* Fix pylint

* Fix problem with settings editor

* Docker configuration changed

- Now config file is specified in dockerfile
- Readme updated

* Improve plugin subsystem

* Now we have plugin manager, which handles plugin loading
* Crash in plugin load does not lead to crash in app
* Plugins has separate branch in config
* Plugin has its own directory

* Fix UUID generator

Built-in UUID generator does not work in some cases

* Fix AddUser() bug

* Some changes to login and database create logic

- If no admin users are present force to go to init_app page
- Only one admin user is allowed(to prevent to anyone to register)

* UI fixes (#53)

* Make navigation sticky

* Add time and date selectors

* Add paddings

* Fix buttons with on smaller screens

* Docker-related stuff changed

Mainly because Docker worked poorly with automated reader device
flasher, so:
- ttyUSB device made accessed in container
- reader firmware now automatically downloaded from git
... other changes

* Fix add user functionality

* Some fixes (#57)

* Fix add user functionality

* Set wifi credentials in settings

* Fix pylint

* Add image and other fix (#58)

* Fix add user functionality

* Set wifi credentials in settings

* Fix pylint

* Add image and some fixes

* Another fixes

* Fix add user functionality

* Set wifi credentials in settings

* Fix pylint

* Add image and some fixes

* Fix log representation issues

- Remove links in logs
- Device name instead device ID
- Heh, remove checkbox on init page with DB restore:)

* Several changes, revealed during smoke test on final stages (#61)

* Several changes, revealed during smoke test on final stages

+ documentation start

* Added instructions steps

* Contributors added

* Tool door split (#65)

* Several changes, revealed during smoke test on final stages

+ documentation start

* Added instructions steps

* Contributors added

* Added tool and door devices split

* Latest activity added (#66)

* Slack notifier separately notify door and tool (#67)

* Slack notifier separately notify door and tool

* Fix pylint

---------

Co-authored-by: Anna Deeva <[email protected]>
Co-authored-by: vovastelmashchuk <[email protected]>
Co-authored-by: Volodymyr Stelmashchuk <[email protected]>
Co-authored-by: Anna Deeva <[email protected]>
  • Loading branch information
5 people authored Feb 14, 2024
1 parent 6f22ff5 commit ac728f1
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 25 deletions.
3 changes: 2 additions & 1 deletion app/config_default.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
],
"PLUGINS": {
"slack_notifier": {
"SLACK_CHANNEL": "#prismo-debug",
"SLACK_TOOL_CHANNEL": "#prismo-debug",
"SLACK_DOOR_CHANNEL": "#prismo-door-channel",
"SLACK_TOKEN": "xoxb-this-is-not-areal-slack-token"
}
},
Expand Down
26 changes: 24 additions & 2 deletions app/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,38 @@ def get_permissions(cls, user_key=None):
# Fetch user data and permissions
user_data = []
# Filter by user_key if provided
# Here we also add "Latest Activity" column, for reporting latest any tool/door use by user
if user_key:
cursor.execute(
"SELECT users.name, users.key FROM users WHERE users.key = ?",
"""
SELECT users.name, users.key,
(SELECT operation_time
FROM event_logs
WHERE user_key = users.key
ORDER BY operation_time DESC
LIMIT 1) AS latest_activity
FROM users
WHERE users.key = ?
""",
(user_key,),
)
else:
cursor.execute("SELECT users.name, users.key FROM users")
cursor.execute(
"""
SELECT users.name, users.key,
(SELECT operation_time
FROM event_logs
WHERE user_key = users.key
ORDER BY operation_time DESC
LIMIT 1) AS latest_activity
FROM users
"""
)

for row in cursor.fetchall():
user_name = row[0]
user_key = row[1]
latest_activity = row[2]

# Get device permissions for the current user
device_permissions = []
Expand Down Expand Up @@ -118,6 +139,7 @@ def get_permissions(cls, user_key=None):
"user_name": user_name,
"user_key": user_key,
"permissions": device_permissions,
"latest_activity": latest_activity,
}
user_data.append(user_record)

Expand Down
87 changes: 81 additions & 6 deletions app/plugins/slack_notifier/slack_notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,48 @@ def unlock_message_block_constructor(tool, user):
}]


def door_message_block_constructor(door, user):
return [
{
"type": "rich_text",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "emoji",
"name": "door",
"unicode": "1f6aa"
},
{
"type": "text",
"text": " "
},
{
"type": "text",
"text": "%s" % user,
"style": {
"bold": True
}
},
{
"type": "text",
"text": " entered through the "
},
{
"type": "text",
"text": "%s" % door,
"style": {
"bold": True
}
}
]
}
]
}
]


class SlackNotifierPlugin:
def __init__(self, app_context):
# Configure the Slack client with your token
Expand Down Expand Up @@ -102,19 +144,52 @@ def get_device_name(self, device_id):

return None

def get_device_type(self, device_id):
"""
Get device name based on its ID
"""
connection = sqlite3.connect(self.db_uri)
cursor = connection.cursor()

cursor.execute("SELECT type from devices WHERE id = ?",
(device_id,),
)
connection.commit()
result = cursor.fetchone()

connection.close()
if result:
return result[0]

return None

def access_log_entry_added(self, event):
try:
if event["operation"] == "unlock":
user_name = self.get_user_name(event["user_key"])
device_name = self.get_device_name(event["device_id"])
device_type = self.get_device_type(event["device_id"])
self.logger.info("Access log entry added")
self.logger.info("User name: %s", user_name)
self.logger.info("Device name: %s", device_name)
text_message = "🔓 * %s Tool was unlocked* by %s" % (device_name, user_name)
blocks = unlock_message_block_constructor(device_name, user_name)
self.slack_app.client.chat_postMessage(channel=self.config["SLACK_CHANNEL"],
text=text_message,
blocks=blocks)
if device_type == "tool":
self.logger.info("Device name: %s", device_name)
text_message = "🔓 * %s Tool was unlocked* by %s" % (device_name, user_name)
blocks = unlock_message_block_constructor(device_name, user_name)
self.slack_app.client.chat_postMessage(
channel=self.config["SLACK_TOOL_CHANNEL"],
text=text_message,
blocks=blocks)
elif device_type == "door":
self.logger.info("Door opened: %s", device_name)
text_message = "🔓 * %s Door opened by * by %s" % (device_name, user_name)
blocks = door_message_block_constructor(device_name, user_name)
self.slack_app.client.chat_postMessage(
channel=self.config["SLACK_DOOR_CHANNEL"],
text=text_message,
blocks=blocks)
else:
self.logger.error("Unknown reader type! %s", device_type)

except Exception as e:
self.logger.error("Error in SlackNotifierPlugin.access_log_entry_added: %s", e)
raise e
Expand Down
13 changes: 7 additions & 6 deletions app/static/js/devices.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Global: Device ID, which is pending for update
let deviceIDForUpdate = null;

let deviceTypeForUpdate = null;
function flashFirmware() {
console.log("Flashing device: ", deviceIDForUpdate);
console.log("Flashing device: ", deviceIDForUpdate, deviceTypeForUpdate);
const socket = new WebSocket("ws://" + location.host + "/reader_flasher");
const logContainer = document.getElementById("log-container");

Expand All @@ -12,7 +12,7 @@ function flashFirmware() {
console.log(
"WebSocket connection established, send device id for flashing",
);
socket.send(deviceIDForUpdate);
socket.send(JSON.stringify({"device_id":deviceIDForUpdate, "device_type": deviceTypeForUpdate}));
});
function log(data) {
const obj = JSON.parse(data);
Expand Down Expand Up @@ -58,7 +58,7 @@ function generateAccordionItems(devices) {
</div>
<div>
<button type="button" class="btn btn-primary me-3 col-12 col-sm-auto mb-1" data-bs-toggle="modal" data-bs-target="#flashDeviceModal" onclick="deviceIDForUpdate='${device.id}'">Flash Connected Device</button>
<button type="button" class="btn btn-primary me-3 col-12 col-sm-auto mb-1" data-bs-toggle="modal" data-bs-target="#flashDeviceModal" onclick="deviceIDForUpdate='${device.id}';deviceTypeForUpdate='${device.type}'">Flash Connected Device</button>
<button type="button" class="btn btn-danger col-12 col-sm-auto mb-1" onclick="removeDevice('${device.id}')">Remove Device</button>
</div>
Expand Down Expand Up @@ -86,14 +86,15 @@ function generateUUID() {
return uuid
}

function addDevice(deviceName) {
function addDevice(deviceName, isDeviceTool) {
// Generate random UUID for device ID
const deviceId = generateUUID();
// Prepare device data
const deviceData = {
device_name: deviceName,
device_id: deviceId,
device_type: "tool",
// Looks like there is a bug in bootstrap toogle 5, can not just get value of toggle:(
device_type: (isDeviceTool ? "tool" : "door"),
};
// Make API call to add device
fetch("/api/devices", {
Expand Down
3 changes: 3 additions & 0 deletions app/templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
<title>Prismo</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/intro.js/2.7.0/intro.js"></script>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/js/bootstrap5-toggle.ecmas.min.js"></script>
<script src="static/js/rfid.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/css/bootstrap5-toggle.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/intro.js/2.7.0/introjs.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
Expand Down
6 changes: 4 additions & 2 deletions app/templates/prismo/devices.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
<button
class="btn btn-primary"
type="submit"
onclick="addDevice(document.getElementById('device_name').value)">
onclick="addDevice(document.querySelector('#device-name').value, document.querySelector('#device-type').checked)">
<i class="bi bi-database-add"></i> Add device
</button>
<input type="text" class="form-control" id="device_name" placeholder="Device Name" />
<input type="text" class="form-control" id="device-name" placeholder="Device Name" />
<input type="checkbox" checked data-toggle="toggle" id="device-type"
data-onlabel="<i class='bi bi-tools'></i> Tool" data-offlabel="<i c lass='bi bi-door-closed'></i> Door">
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions app/templates/prismo/users.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ <h5 class="card-title">Add last used RFID card as new user: <span id="used-key-a
}
return acc
}, {}),
latestActivity: user.latest_activity,
operation: '<button class="btn btn-sm btn-danger" onclick="deleteUser(\'' + user.user_key + '\')">Delete</button>'
}
})
Expand Down
22 changes: 14 additions & 8 deletions app/utils/fimware_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from flask import current_app as app


def update_firmware_full(socket, device_id):
def update_firmware_full(socket, device_id, device_type):
"""Run full firmware update, by running firmware updater script
This function runs firmware updater script, which erases memory, flashes
Expand All @@ -26,6 +26,7 @@ def update_firmware_full(socket, device_id):
socket -- communication websocket
device_id -- device ID string, MUST be UUID string in format
550e8400-e29b-41d4-a716-446655440000
device_type -- type of device, should be "tool" or "door"
Returns:
bool -- True if firmware update was successful, False otherwise
Expand All @@ -43,7 +44,8 @@ def update_firmware_full(socket, device_id):
run_environment["HOST_WIFI_SSID"] = app.config["PRISMO"]["WIFI_SSID"]
run_environment["HOST_WIFI_PASSWORD"] = app.config["PRISMO"]["WIFI_PASSWORD"]
script_cwd = script_path.parent
process = subprocess.Popen([script_path, device_id], cwd=script_cwd,
process = subprocess.Popen([script_path, device_id, device_type], cwd=script_cwd,

stdout=subprocess.PIPE, env=run_environment)
except FileNotFoundError:
data["text"] = "Cannot find firmware update script"
Expand Down Expand Up @@ -87,19 +89,23 @@ def update_firmware_full(socket, device_id):

def firmware_updater_route(websocket):
device_id = None
device_type = None
while device_id is None:
message = websocket.receive()
if message:
try:
json_data = json.loads(message)
# Check if we have received valid uuid string.
uuid.UUID(message)
device_id = message
uuid.UUID(json_data["device_id"])
device_id = json_data["device_id"]
device_type = json_data["device_type"]

except ValueError:
print("No uuid string was received:", message)
app.logger.error("Wrong message received: %s", message)

if device_id is not None:
update_result = update_firmware_full(websocket, device_id)
if device_id is not None and device_type is not None:
update_result = update_firmware_full(websocket, device_id, device_type)
app.logger.warning("Update result: %s", update_result)
else:
app.logger.warning("No device id was received")
app.logger.warning("No device id or device type was received")
app.logger.warning("Close websocket")

0 comments on commit ac728f1

Please sign in to comment.