Files
Agri-tech/agri_seedling/agri_seedling/api/mobile_auth.py
T
2026-08-27 10:59:50 +03:00

87 lines
3.1 KiB
Python

import json
import secrets
import frappe
from datetime import datetime, timedelta
from frappe import _
from frappe.utils import now_datetime
from frappe.utils.password import check_password, get_decrypted_password
TOKEN_TTL_HOURS = 24
TOKEN_CACHE_PREFIX = "worker_token"
ACTIVITY_LABEL_TO_DOCTYPE = {
"Scouting": "Scouting Report", "Order Patching": "Order Patch",
"Order Dispatching": "Dispatch Record", "Mixing Media": "Media Mix Batch",
"Planting": "Planting Record", "Covering Seedlings": "Cover Log",
"Watering Greenhouse": "Greenhouse Watering Log", "Watering Seedlings": "Seedling Watering Log",
"Spraying Morning": "Spray Log Morning", "Spraying Evening": "Spray Log Evening",
"Customer Engagement": "Customer Interaction", "Soaking": "Soaking Record",
"Recycling Trays": "Tray Recycling Record",
}
def issue_token(worker_id):
token = secrets.token_urlsafe(32)
expires_at = now_datetime() + timedelta(hours=TOKEN_TTL_HOURS)
frappe.cache().set_value(
f"{TOKEN_CACHE_PREFIX}:{token}",
json.dumps({"worker_id": worker_id, "expires_at": expires_at.isoformat()}),
expires_in_sec=TOKEN_TTL_HOURS * 3600,
)
return token, expires_at
@frappe.whitelist(allow_guest=True, methods=["POST"])
def field_worker_login(worker_id, pin):
worker_id = (worker_id or "").strip()
pin = (pin or "").strip()
if not frappe.db.exists("Worker Profile", worker_id):
frappe.throw(f"Worker ID '{worker_id}' not found in system.", frappe.AuthenticationError)
# --- UNIVERSAL PIN CHECK ---
authenticated = False
# Method 1: Standard Frappe Check
try:
check_password(worker_id, pin, "Worker Profile", "pin_hash")
authenticated = True
except:
pass
# Method 2: Decryption Check (for Desk-saved PINs)
if not authenticated:
try:
stored = get_decrypted_password("Worker Profile", worker_id, fieldname="pin_hash")
if stored and str(stored) == str(pin):
authenticated = True
except:
pass
# Method 3: Direct DB Match (Fallback)
if not authenticated:
db_val = frappe.db.get_value("Worker Profile", worker_id, "pin_hash")
if db_val == pin: # Sometimes saved as plain text in dev
authenticated = True
if not authenticated:
frappe.throw(f"The PIN you entered for {worker_id} is incorrect.", frappe.AuthenticationError)
profile = frappe.get_doc("Worker Profile", worker_id)
if not profile.active:
frappe.throw("This worker account is deactivated.", frappe.AuthenticationError)
assigned_activities = [
ACTIVITY_LABEL_TO_DOCTYPE.get(row.activity_type, row.activity_type)
for row in (profile.get("assigned_activity_types") or []) if row.get("activity_type")
]
token, expires_at = issue_token(worker_id)
return {
"token": token,
"expires_at": expires_at.isoformat(),
"worker_id": profile.name,
"full_name": profile.full_name,
"assigned_zones": [row.zone for row in (profile.get("assigned_zones") or []) if row.get("zone")],
"assigned_activity_types": assigned_activities
}