Updates the user registration and new account creation endpoints to require email confirmation. - Sets the 'confirmed' flag to 'false' by default for all new user accounts. - Generates a unique confirmation token for each new user. - Logs the confirmation link to the console for development purposes. This change ensures that users cannot log in without first verifying their email address, enhancing account security.
608 lines
24 KiB
Python
608 lines
24 KiB
Python
import secrets
|
|
import random
|
|
import string
|
|
import os
|
|
import shutil
|
|
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from database import get_db
|
|
from models import Customer_Customer, Customer_Description, Customer_Tank_Inspection, Customer_Stats, Account_User
|
|
from schemas import NewCustomerCreate, UserResponse, CustomerCreateStep1, CustomerAccountCreate
|
|
from passlib.context import CryptContext
|
|
from datetime import datetime
|
|
from PIL import Image
|
|
import io
|
|
|
|
pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
|
|
|
|
def generate_random_number_string(length):
|
|
if length < 1:
|
|
raise ValueError("Length must be at least 1")
|
|
random_number = ''.join(random.choices(string.digits, k=length))
|
|
return random_number
|
|
|
|
def get_password_hash(password):
|
|
# Truncate password to 72 bytes max for bcrypt compatibility
|
|
truncated_password = password.encode('utf-8')[:72].decode('utf-8', errors='ignore')
|
|
return pwd_context.hash(truncated_password)
|
|
|
|
def resize_image(image_data, max_size=(1024, 1024), quality=85):
|
|
"""
|
|
Resize image to fit within max_size while maintaining aspect ratio.
|
|
Convert to JPEG format and strip metadata for security.
|
|
"""
|
|
try:
|
|
# Open image from bytes
|
|
img = Image.open(io.BytesIO(image_data))
|
|
|
|
# Convert to RGB if necessary (for PNG with transparency, etc.)
|
|
if img.mode in ('RGBA', 'LA', 'P'):
|
|
img = img.convert('RGB')
|
|
|
|
# Resize if larger than max_size
|
|
if img.width > max_size[0] or img.height > max_size[1]:
|
|
img.thumbnail(max_size, Image.Resampling.LANCZOS)
|
|
|
|
# Save as JPEG with specified quality
|
|
output = io.BytesIO()
|
|
img.save(output, format='JPEG', quality=quality, optimize=True)
|
|
output.seek(0)
|
|
|
|
return output.getvalue()
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=400, detail=f"Invalid image file: {str(e)}")
|
|
|
|
router = APIRouter()
|
|
|
|
@router.post("/new", response_model=UserResponse)
|
|
async def register_new_customer(customer: NewCustomerCreate, db: AsyncSession = Depends(get_db)):
|
|
# Verify passwords match
|
|
if customer.password != customer.confirm_password:
|
|
raise HTTPException(status_code=400, detail="Passwords do not match")
|
|
|
|
# Check if email already registered
|
|
result = await db.execute(select(Account_User).where(Account_User.email == customer.customer_email))
|
|
if result.scalar_one_or_none():
|
|
raise HTTPException(status_code=400, detail="Email already registered")
|
|
|
|
while True:
|
|
random_part = generate_random_number_string(6)
|
|
account_number = 'AO-' + random_part
|
|
result = await db.execute(select(Customer_Customer).where(Customer_Customer.account_number == account_number))
|
|
existing_customer = result.scalar_one_or_none()
|
|
if not existing_customer:
|
|
break
|
|
|
|
# Create customer
|
|
customer_data = customer.model_dump()
|
|
customer_data.pop('password')
|
|
customer_data.pop('confirm_password')
|
|
customer_data.update({
|
|
'account_number': account_number,
|
|
'customer_state': 1, # Default
|
|
'customer_automatic': 0,
|
|
'company_id': 1, # Default
|
|
'customer_latitude': '0',
|
|
'customer_longitude': '0',
|
|
'correct_address': True,
|
|
'customer_first_call': datetime.utcnow()
|
|
})
|
|
db_customer = Customer_Customer(**customer_data)
|
|
db.add(db_customer)
|
|
await db.commit()
|
|
await db.refresh(db_customer)
|
|
|
|
# Extract house number from customer address (first part before space)
|
|
house_number = customer.customer_address.split()[0] if customer.customer_address else ''
|
|
|
|
# Create account user
|
|
username = account_number
|
|
hashed_password = get_password_hash(customer.password)
|
|
token = secrets.token_urlsafe(32)
|
|
db_user = Account_User(
|
|
username=username,
|
|
account_number=account_number,
|
|
house_number=house_number,
|
|
password_hash=hashed_password,
|
|
member_since=datetime.utcnow(),
|
|
email=customer.customer_email,
|
|
last_seen=datetime.utcnow(),
|
|
admin=0,
|
|
admin_role=0,
|
|
confirmed=0,
|
|
active=1,
|
|
user_id=db_customer.id,
|
|
confirmation_token=token,
|
|
confirmation_sent_at=datetime.utcnow()
|
|
)
|
|
db.add(db_user)
|
|
await db.commit()
|
|
await db.refresh(db_user)
|
|
|
|
# In a real application, you would send an email here
|
|
# For now, we'll just print the confirmation URL to the console
|
|
confirmation_url = f"http://localhost:3000/confirm-email?token={token}"
|
|
print(f"Confirmation URL: {confirmation_url}")
|
|
|
|
return db_user
|
|
|
|
@router.post("/step3")
|
|
async def upload_tank_images(
|
|
account_number: str = Form(...),
|
|
tank_image_1: UploadFile = File(...),
|
|
tank_image_2: UploadFile = File(...),
|
|
tank_image_3: UploadFile = File(...),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
print("=== STEP3 DEBUG START ===")
|
|
print(f"Account number received: '{account_number}'")
|
|
|
|
# Debug: Check all parameters received
|
|
images = [tank_image_1, tank_image_2, tank_image_3]
|
|
for i, image in enumerate(images, 1):
|
|
print(f"Image {i}: filename='{image.filename}', content_type='{image.content_type}', size={image.size}")
|
|
|
|
# Validate account number
|
|
if not account_number:
|
|
print("ERROR: Account number is empty")
|
|
raise HTTPException(status_code=400, detail="Account number is required")
|
|
|
|
# Get customer info for description record
|
|
customer_result = await db.execute(select(Customer_Customer).where(Customer_Customer.account_number == account_number))
|
|
customer = customer_result.scalar_one_or_none()
|
|
if not customer:
|
|
raise HTTPException(status_code=400, detail="Customer not found")
|
|
|
|
print(f"Creating directory: /images/{account_number}")
|
|
# Create directory for account number in the mounted images volume
|
|
account_dir = f"/images/{account_number}"
|
|
os.makedirs(account_dir, exist_ok=True)
|
|
|
|
current_datetime = datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S") # YYYY-MM-DD_HH-MM-SS format
|
|
|
|
# Track if images were uploaded successfully
|
|
images_uploaded_successfully = False
|
|
|
|
# Validate and save images
|
|
saved_files = []
|
|
|
|
try:
|
|
for i, image in enumerate(images, 1):
|
|
print(f"Processing image {i}...")
|
|
# Read image data
|
|
image_data = await image.read()
|
|
print(f"Image {i} data read: {len(image_data)} bytes")
|
|
|
|
# Validate file size (max 20MB before processing)
|
|
if len(image_data) > 20 * 1024 * 1024:
|
|
print(f"ERROR: Image {i} too large: {len(image_data)} bytes")
|
|
raise HTTPException(status_code=400, detail=f"File {i} is too large (max 20MB)")
|
|
|
|
# Resize and process image (this also validates it's a valid image)
|
|
print(f"Resizing image {i}...")
|
|
processed_image_data = resize_image(image_data, max_size=(1024, 1024), quality=85)
|
|
print(f"Image {i} resized: {len(processed_image_data)} bytes")
|
|
|
|
# Save processed image with datetime-based filename
|
|
filename = f"{current_datetime}-{i}.jpg"
|
|
file_path = os.path.join(account_dir, filename)
|
|
print(f"Saving image {i} to: {file_path}")
|
|
|
|
with open(file_path, "wb") as buffer:
|
|
buffer.write(processed_image_data)
|
|
|
|
saved_files.append(filename)
|
|
print(f"Image {i} saved successfully")
|
|
|
|
images_uploaded_successfully = True
|
|
print(f"All images processed successfully. Saved files: {saved_files}")
|
|
|
|
except Exception as e:
|
|
print(f"ERROR processing images: {str(e)}")
|
|
# Don't raise exception - we want to track the failure in the database
|
|
images_uploaded_successfully = False
|
|
|
|
# Update or create customer tank inspection record with tank_images status
|
|
print("Updating customer tank inspection record...")
|
|
tank_result = await db.execute(
|
|
select(Customer_Tank_Inspection).where(Customer_Tank_Inspection.customer_id == customer.id)
|
|
)
|
|
existing_tank = tank_result.scalar_one_or_none()
|
|
|
|
if existing_tank:
|
|
# Update existing record
|
|
if images_uploaded_successfully:
|
|
existing_tank.tank_images += 1 # Increment count of image sets
|
|
# Append current datetime to upload dates list
|
|
current_dates = existing_tank.tank_image_upload_dates or []
|
|
current_dates.append(current_datetime)
|
|
existing_tank.tank_image_upload_dates = current_dates
|
|
await db.commit()
|
|
print(f"Updated existing tank inspection record with tank_images = {existing_tank.tank_images}, dates = {existing_tank.tank_image_upload_dates}")
|
|
else:
|
|
# Create new record
|
|
new_tank = Customer_Tank_Inspection(
|
|
customer_id=customer.id,
|
|
tank_images=1 if images_uploaded_successfully else 0,
|
|
tank_image_upload_dates=[current_datetime] if images_uploaded_successfully else []
|
|
# Other fields will be null/None initially
|
|
)
|
|
db.add(new_tank)
|
|
await db.commit()
|
|
print(f"Created new tank inspection record with tank_images = {new_tank.tank_images}, dates = {new_tank.tank_image_upload_dates}")
|
|
|
|
print("=== STEP3 DEBUG END ===")
|
|
|
|
if images_uploaded_successfully:
|
|
return {
|
|
"message": "Tank images uploaded successfully",
|
|
"account_number": account_number,
|
|
"uploaded_files": saved_files
|
|
}
|
|
else:
|
|
return {
|
|
"message": "Tank images upload skipped or failed",
|
|
"account_number": account_number,
|
|
"uploaded_files": []
|
|
}
|
|
|
|
@router.post("/upload-tank-images")
|
|
async def upload_additional_tank_images(
|
|
account_number: str = Form(...),
|
|
tank_image_1: UploadFile = File(...),
|
|
tank_image_2: UploadFile = File(...),
|
|
tank_image_3: UploadFile = File(...),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""
|
|
Endpoint for customers to upload additional sets of 3 tank images after registration.
|
|
Similar to step3 but for existing customers.
|
|
"""
|
|
print("=== UPLOAD ADDITIONAL TANK IMAGES DEBUG START ===")
|
|
print(f"Account number received: '{account_number}'")
|
|
|
|
# Debug: Check all parameters received
|
|
images = [tank_image_1, tank_image_2, tank_image_3]
|
|
for i, image in enumerate(images, 1):
|
|
print(f"Image {i}: filename='{image.filename}', content_type='{image.content_type}', size={image.size}")
|
|
|
|
# Validate account number
|
|
if not account_number:
|
|
print("ERROR: Account number is empty")
|
|
raise HTTPException(status_code=400, detail="Account number is required")
|
|
|
|
# Get customer info
|
|
customer_result = await db.execute(select(Customer_Customer).where(Customer_Customer.account_number == account_number))
|
|
customer = customer_result.scalar_one_or_none()
|
|
if not customer:
|
|
raise HTTPException(status_code=400, detail="Customer not found")
|
|
|
|
print(f"Creating directory: /images/{account_number}")
|
|
# Create directory for account number in the mounted images volume
|
|
account_dir = f"/images/{account_number}"
|
|
os.makedirs(account_dir, exist_ok=True)
|
|
|
|
current_datetime = datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S") # YYYY-MM-DD_HH-MM-SS format
|
|
|
|
# Track if images were uploaded successfully
|
|
images_uploaded_successfully = False
|
|
|
|
# Validate and save images
|
|
saved_files = []
|
|
|
|
try:
|
|
for i, image in enumerate(images, 1):
|
|
print(f"Processing image {i}...")
|
|
# Read image data
|
|
image_data = await image.read()
|
|
print(f"Image {i} data read: {len(image_data)} bytes")
|
|
|
|
# Validate file size (max 20MB before processing)
|
|
if len(image_data) > 20 * 1024 * 1024:
|
|
print(f"ERROR: Image {i} too large: {len(image_data)} bytes")
|
|
raise HTTPException(status_code=400, detail=f"File {i} is too large (max 20MB)")
|
|
|
|
# Resize and process image (this also validates it's a valid image)
|
|
print(f"Resizing image {i}...")
|
|
processed_image_data = resize_image(image_data, max_size=(1024, 1024), quality=85)
|
|
print(f"Image {i} resized: {len(processed_image_data)} bytes")
|
|
|
|
# Save processed image with datetime-based filename
|
|
filename = f"{current_datetime}-{i}.jpg"
|
|
file_path = os.path.join(account_dir, filename)
|
|
print(f"Saving image {i} to: {file_path}")
|
|
|
|
with open(file_path, "wb") as buffer:
|
|
buffer.write(processed_image_data)
|
|
|
|
saved_files.append(filename)
|
|
print(f"Image {i} saved successfully")
|
|
|
|
images_uploaded_successfully = True
|
|
print(f"All images processed successfully. Saved files: {saved_files}")
|
|
|
|
except Exception as e:
|
|
print(f"ERROR processing images: {str(e)}")
|
|
# Don't raise exception - we want to track the failure in the database
|
|
images_uploaded_successfully = False
|
|
|
|
# Update customer tank inspection record
|
|
print("Updating customer tank inspection record...")
|
|
tank_result = await db.execute(
|
|
select(Customer_Tank_Inspection).where(Customer_Tank_Inspection.customer_id == customer.id)
|
|
)
|
|
existing_tank = tank_result.scalar_one_or_none()
|
|
|
|
if existing_tank:
|
|
# Update existing record
|
|
if images_uploaded_successfully:
|
|
existing_tank.tank_images += 1 # Increment count of image sets
|
|
# Append current datetime to upload dates list
|
|
current_dates = existing_tank.tank_image_upload_dates or []
|
|
current_dates.append(current_datetime)
|
|
existing_tank.tank_image_upload_dates = current_dates
|
|
await db.commit()
|
|
print(f"Updated existing tank inspection record with tank_images = {existing_tank.tank_images}, dates = {existing_tank.tank_image_upload_dates}")
|
|
else:
|
|
# This shouldn't happen for additional uploads, but handle it just in case
|
|
new_tank = Customer_Tank_Inspection(
|
|
customer_id=customer.id,
|
|
tank_images=1 if images_uploaded_successfully else 0,
|
|
tank_image_upload_dates=[current_datetime] if images_uploaded_successfully else []
|
|
)
|
|
db.add(new_tank)
|
|
await db.commit()
|
|
print(f"Created new tank inspection record with tank_images = {new_tank.tank_images}, dates = {new_tank.tank_image_upload_dates}")
|
|
|
|
print("=== UPLOAD ADDITIONAL TANK IMAGES DEBUG END ===")
|
|
|
|
if images_uploaded_successfully:
|
|
return {
|
|
"message": "Additional tank images uploaded successfully",
|
|
"account_number": account_number,
|
|
"uploaded_files": saved_files,
|
|
"upload_date": current_datetime
|
|
}
|
|
else:
|
|
raise HTTPException(status_code=400, detail="Failed to upload tank images")
|
|
|
|
@router.get("/tank-images/{account_number}")
|
|
async def get_tank_images(account_number: str, db: AsyncSession = Depends(get_db)):
|
|
"""
|
|
Get tank images information for a customer including upload dates.
|
|
"""
|
|
# Get customer info
|
|
customer_result = await db.execute(select(Customer_Customer).where(Customer_Customer.account_number == account_number))
|
|
customer = customer_result.scalar_one_or_none()
|
|
if not customer:
|
|
raise HTTPException(status_code=404, detail="Customer not found")
|
|
|
|
# Get tank inspection record
|
|
tank_result = await db.execute(
|
|
select(Customer_Tank_Inspection).where(Customer_Tank_Inspection.customer_id == customer.id)
|
|
)
|
|
tank_record = tank_result.scalar_one_or_none()
|
|
|
|
# Build image sets - first from database, then scan for any additional files
|
|
image_sets = []
|
|
upload_dates = tank_record.tank_image_upload_dates or [] if tank_record else []
|
|
|
|
# Add sets from database
|
|
for i, upload_date in enumerate(upload_dates):
|
|
# Handle backward compatibility: old format used date-only strings and tank_*.jpg files
|
|
# New format uses datetime strings and {datetime}-*.jpg files
|
|
if "_" in upload_date:
|
|
# New datetime format (YYYY-MM-DD_HH-MM-SS)
|
|
image_set = {
|
|
"date": upload_date,
|
|
"images": [
|
|
f"/images/{account_number}/{upload_date}-1.jpg",
|
|
f"/images/{account_number}/{upload_date}-2.jpg",
|
|
f"/images/{account_number}/{upload_date}-3.jpg"
|
|
]
|
|
}
|
|
else:
|
|
# Old date-only format (YYYY-MM-DD) - uses tank_*.jpg files
|
|
image_set = {
|
|
"date": upload_date,
|
|
"images": [
|
|
f"/images/{account_number}/tank_1.jpg",
|
|
f"/images/{account_number}/tank_2.jpg",
|
|
f"/images/{account_number}/tank_3.jpg"
|
|
]
|
|
}
|
|
image_sets.append(image_set)
|
|
|
|
# Scan for any additional image sets that might not be in database
|
|
account_dir = f"/images/{account_number}"
|
|
if os.path.exists(account_dir):
|
|
# Find all image files
|
|
all_files = os.listdir(account_dir)
|
|
image_files = [f for f in all_files if f.endswith('.jpg')]
|
|
|
|
# Group by datetime prefix
|
|
datetime_groups = {}
|
|
for file in image_files:
|
|
if file.startswith('tank_'):
|
|
# Old tank_*.jpg files - already handled above
|
|
continue
|
|
elif '_' in file and file.endswith('.jpg'): # datetime format like 2026-01-08_23-25-31-1.jpg
|
|
# Extract datetime prefix by removing the image number and .jpg
|
|
# Example: 2026-01-08_23-25-31-1.jpg -> 2026-01-08_23-25-31
|
|
datetime_prefix = file.rsplit('-', 1)[0] # Remove everything after last dash
|
|
if datetime_prefix not in datetime_groups:
|
|
datetime_groups[datetime_prefix] = []
|
|
datetime_groups[datetime_prefix].append(file)
|
|
|
|
# Add any datetime groups not already in database
|
|
for datetime_prefix, files in datetime_groups.items():
|
|
if datetime_prefix not in upload_dates and len(files) >= 3:
|
|
# Sort files to ensure correct order
|
|
sorted_files = sorted(files)
|
|
image_set = {
|
|
"date": datetime_prefix,
|
|
"images": [
|
|
f"/images/{account_number}/{sorted_files[0]}",
|
|
f"/images/{account_number}/{sorted_files[1]}",
|
|
f"/images/{account_number}/{sorted_files[2]}"
|
|
]
|
|
}
|
|
image_sets.append(image_set)
|
|
|
|
# Also check for date-prefixed files (like 2026-01-08-1.jpg)
|
|
date_groups = {}
|
|
for file in image_files:
|
|
if file.startswith('tank_') or '_' in file:
|
|
continue # Skip old format and datetime format
|
|
parts = file.split('-')
|
|
if len(parts) == 4 and parts[3] in ['1.jpg', '2.jpg', '3.jpg']:
|
|
date_prefix = '-'.join(parts[:3]) # 2026-01-08
|
|
if date_prefix not in date_groups:
|
|
date_groups[date_prefix] = []
|
|
date_groups[date_prefix].append(file)
|
|
|
|
# Add date groups not already in database
|
|
for date_prefix, files in date_groups.items():
|
|
if date_prefix not in upload_dates and len(files) >= 3:
|
|
# Sort files to ensure correct order
|
|
sorted_files = sorted(files)
|
|
image_set = {
|
|
"date": date_prefix,
|
|
"images": [
|
|
f"/images/{account_number}/{sorted_files[0]}",
|
|
f"/images/{account_number}/{sorted_files[1]}",
|
|
f"/images/{account_number}/{sorted_files[2]}"
|
|
]
|
|
}
|
|
image_sets.append(image_set)
|
|
|
|
# Sort image sets by date descending (newest first)
|
|
def sort_key(item):
|
|
date_str = item['date']
|
|
try:
|
|
if '_' in date_str:
|
|
return datetime.strptime(date_str, "%Y-%m-%d_%H-%M-%S")
|
|
else:
|
|
return datetime.strptime(date_str, "%Y-%m-%d")
|
|
except:
|
|
return datetime.min
|
|
|
|
image_sets.sort(key=sort_key, reverse=True)
|
|
|
|
return {
|
|
"account_number": account_number,
|
|
"image_sets": image_sets
|
|
}
|
|
|
|
@router.post("/step1")
|
|
async def create_customer_step1(customer: CustomerCreateStep1, db: AsyncSession = Depends(get_db)):
|
|
while True:
|
|
random_part = generate_random_number_string(6)
|
|
account_number = 'AO-' + random_part
|
|
result = await db.execute(select(Customer_Customer).where(Customer_Customer.account_number == account_number))
|
|
existing_customer = result.scalar_one_or_none()
|
|
if not existing_customer:
|
|
break
|
|
|
|
# Extract house_description for separate table
|
|
house_description = customer.house_description
|
|
|
|
# Create customer
|
|
customer_data = customer.model_dump()
|
|
customer_data.pop('house_description') # Remove from customer data
|
|
customer_data.update({
|
|
'account_number': account_number,
|
|
'customer_state': 0, # Default
|
|
'customer_automatic': 0,
|
|
'company_id': 1, # Default
|
|
'customer_latitude': '0',
|
|
'customer_longitude': '0',
|
|
'correct_address': True,
|
|
'customer_first_call': datetime.utcnow()
|
|
})
|
|
db_customer = Customer_Customer(**customer_data)
|
|
db.add(db_customer)
|
|
await db.commit()
|
|
await db.refresh(db_customer)
|
|
|
|
# Create customer description if house_description provided
|
|
if house_description:
|
|
db_description = Customer_Description(
|
|
customer_id=db_customer.id,
|
|
account_number=account_number,
|
|
company_id=1, # Default
|
|
fill_location=None, # Will work on this later
|
|
description=house_description
|
|
# tank_images is now tracked in customer_tank table
|
|
)
|
|
db.add(db_description)
|
|
await db.commit()
|
|
|
|
# Create customer stats record for tracking metrics
|
|
db_stats = Customer_Stats(
|
|
customer_id=db_customer.id
|
|
# All other fields default to 0/0.00 as defined in the model
|
|
)
|
|
db.add(db_stats)
|
|
await db.commit()
|
|
|
|
return {"account_number": account_number}
|
|
|
|
@router.post("/step2", response_model=UserResponse)
|
|
async def create_customer_account(account_data: CustomerAccountCreate, db: AsyncSession = Depends(get_db)):
|
|
# Verify passwords match
|
|
if account_data.password != account_data.confirm_password:
|
|
raise HTTPException(status_code=400, detail="Passwords do not match")
|
|
|
|
# Check if customer exists
|
|
result = await db.execute(select(Customer_Customer).where(Customer_Customer.account_number == account_data.account_number))
|
|
customer = result.scalar_one_or_none()
|
|
if not customer:
|
|
raise HTTPException(status_code=400, detail="Customer not found")
|
|
|
|
# Check if email already registered
|
|
result = await db.execute(select(Account_User).where(Account_User.email == account_data.customer_email))
|
|
if result.scalar_one_or_none():
|
|
raise HTTPException(status_code=400, detail="Email already registered")
|
|
|
|
# Update customer email
|
|
customer.customer_email = account_data.customer_email
|
|
db.add(customer)
|
|
await db.commit()
|
|
|
|
# Extract house number from customer address (first part before space)
|
|
house_number = customer.customer_address.split()[0] if customer.customer_address else ''
|
|
|
|
# Create account user
|
|
username = account_data.account_number
|
|
hashed_password = get_password_hash(account_data.password)
|
|
token = secrets.token_urlsafe(32)
|
|
db_user = Account_User(
|
|
username=username,
|
|
account_number=account_data.account_number,
|
|
house_number=house_number,
|
|
password_hash=hashed_password,
|
|
member_since=datetime.utcnow(),
|
|
email=account_data.customer_email,
|
|
last_seen=datetime.utcnow(),
|
|
admin=0,
|
|
admin_role=0,
|
|
confirmed=0,
|
|
active=1,
|
|
user_id=customer.id,
|
|
confirmation_token=token,
|
|
confirmation_sent_at=datetime.utcnow()
|
|
)
|
|
db.add(db_user)
|
|
await db.commit()
|
|
await db.refresh(db_user)
|
|
|
|
# In a real application, you would send an email here
|
|
# For now, we'll just print the confirmation URL to the console
|
|
confirmation_url = f"http://localhost:3000/confirm-email?token={token}"
|
|
print(f"Confirmation URL: {confirmation_url}")
|
|
|
|
return db_user
|