ad TIMEZONE and more
This commit is contained in:
13
.env.prod
13
.env.prod
@@ -13,6 +13,8 @@ SECRET_KEY=766299d3235f79a2a9a35aafbc90bec7102f250dfe4aba83500b98e568289b7a
|
|||||||
# Usa 0.0.0.0 per permettere connessioni dall'esterno del container (essenziale per Docker/Traefik)
|
# Usa 0.0.0.0 per permettere connessioni dall'esterno del container (essenziale per Docker/Traefik)
|
||||||
HOST=0.0.0.0
|
HOST=0.0.0.0
|
||||||
PORT=8000
|
PORT=8000
|
||||||
|
# Timezone per l'applicazione (cronjobs, notifiche, ecc.)
|
||||||
|
TIMEZONE=Europe/Rome
|
||||||
|
|
||||||
# Database (SQLite path)
|
# Database (SQLite path)
|
||||||
# Percorso assoluto nel container
|
# Percorso assoluto nel container
|
||||||
@@ -25,6 +27,7 @@ DATABASE_PATH=/app/data/parking.db
|
|||||||
|
|
||||||
# JWT token expiration (minutes, default 24 hours)
|
# JWT token expiration (minutes, default 24 hours)
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES=1440
|
ACCESS_TOKEN_EXPIRE_MINUTES=1440
|
||||||
|
COOKIE_SECURE=true
|
||||||
|
|
||||||
# Logging level (DEBUG, INFO, WARNING, ERROR)
|
# Logging level (DEBUG, INFO, WARNING, ERROR)
|
||||||
LOG_LEVEL=INFO
|
LOG_LEVEL=INFO
|
||||||
@@ -64,14 +67,14 @@ AUTHELIA_LOGOUT_URL=https://auth.rocketscale.it/logout
|
|||||||
# Email Notifications
|
# Email Notifications
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Set to true to enable email sending
|
# Set to true to enable email sending
|
||||||
SMTP_ENABLED=false
|
SMTP_ENABLED=true
|
||||||
|
|
||||||
# SMTP server configuration
|
# SMTP server configuration
|
||||||
SMTP_HOST=localhost
|
SMTP_HOST="smtp.email.eu-milan-1.oci.oraclecloud.com"
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
SMTP_USER=
|
SMTP_USER="ocid1.user.oc1..aaaaaaaa6bollovnlx4vxoq2eh7pzgxxhludqitgxsp6fevpllmqynug2uiq@ocid1.tenancy.oc1..aaaaaaaa6veuezxddkzbxmxnjp5thywdjz42z5qfrd6mmosmqehvebrewola.hj.com"
|
||||||
SMTP_PASSWORD=
|
SMTP_PASSWORD="3)J2E9_Np:}#kozD2Wed"
|
||||||
SMTP_FROM=noreply@parking.local
|
SMTP_FROM="noreply@rocketscale.it"
|
||||||
SMTP_USE_TLS=true
|
SMTP_USE_TLS=true
|
||||||
|
|
||||||
# When SMTP is disabled, emails are logged to this file
|
# When SMTP is disabled, emails are logged to this file
|
||||||
|
|||||||
@@ -46,10 +46,12 @@ if SECRET_KEY == "change-me-in-production":
|
|||||||
|
|
||||||
ALGORITHM = "HS256"
|
ALGORITHM = "HS256"
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "1440")) # 24 hours
|
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "1440")) # 24 hours
|
||||||
|
COOKIE_SECURE = os.getenv("COOKIE_SECURE", "false").lower() == "true"
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
HOST = os.getenv("HOST", "0.0.0.0")
|
HOST = os.getenv("HOST", "0.0.0.0")
|
||||||
PORT = int(os.getenv("PORT", "8000"))
|
PORT = int(os.getenv("PORT", "8000"))
|
||||||
|
TIMEZONE = os.getenv("TIMEZONE", "UTC")
|
||||||
|
|
||||||
# CORS
|
# CORS
|
||||||
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "http://localhost:8000,http://127.0.0.1:8000,http://lvh.me").split(",")
|
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "http://localhost:8000,http://127.0.0.1:8000,http://lvh.me").split(",")
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ def login(request: Request, data: LoginRequest, response: Response, db: Session
|
|||||||
value=token,
|
value=token,
|
||||||
httponly=True,
|
httponly=True,
|
||||||
max_age=config.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
|
max_age=config.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
|
||||||
samesite="lax"
|
samesite="lax",
|
||||||
|
secure=config.COOKIE_SECURE
|
||||||
)
|
)
|
||||||
|
|
||||||
config.logger.info(f"User logged in: {data.email}")
|
config.logger.info(f"User logged in: {data.email}")
|
||||||
@@ -114,7 +115,12 @@ def login(request: Request, data: LoginRequest, response: Response, db: Session
|
|||||||
@router.post("/logout")
|
@router.post("/logout")
|
||||||
def logout(response: Response):
|
def logout(response: Response):
|
||||||
"""Logout and clear session"""
|
"""Logout and clear session"""
|
||||||
response.delete_cookie("session_token")
|
response.delete_cookie(
|
||||||
|
key="session_token",
|
||||||
|
httponly=True,
|
||||||
|
samesite="lax",
|
||||||
|
secure=config.COOKIE_SECURE
|
||||||
|
)
|
||||||
return {"message": "Logged out"}
|
return {"message": "Logged out"}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
import sqlite3
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
# Function to find the db file
|
|
||||||
def find_db():
|
|
||||||
candidates = [
|
|
||||||
'data/parking.db',
|
|
||||||
'/home/ssalemi/org-parking/data/parking.db',
|
|
||||||
'./data/parking.db'
|
|
||||||
]
|
|
||||||
for path in candidates:
|
|
||||||
if os.path.exists(path):
|
|
||||||
return path
|
|
||||||
return None
|
|
||||||
|
|
||||||
db_path = find_db()
|
|
||||||
|
|
||||||
if not db_path:
|
|
||||||
print("Error: Could not find data/parking.db. Make sure you are in the project root.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
print(f"Target Database: {db_path}")
|
|
||||||
print("Attempting to fix 'parking_exclusions' index...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Turn off foreign keys temporarily to avoid issues during schema modification if needed
|
|
||||||
cursor.execute("PRAGMA foreign_keys=OFF")
|
|
||||||
|
|
||||||
# 1. Drop the existing unique index
|
|
||||||
print("Dropping index idx_exclusion_office_user...")
|
|
||||||
try:
|
|
||||||
cursor.execute("DROP INDEX IF EXISTS idx_exclusion_office_user")
|
|
||||||
print("Index dropped successfully.")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Warning during drop: {e}")
|
|
||||||
|
|
||||||
# 2. Recreate it as non-unique
|
|
||||||
print("Creating non-unique index idx_exclusion_office_user...")
|
|
||||||
try:
|
|
||||||
cursor.execute("CREATE INDEX idx_exclusion_office_user ON parking_exclusions (office_id, user_id)")
|
|
||||||
print("Index created successfully.")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error creating index: {e}")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
print("\nSUCCESS: Database updated. You can now define multiple exclusions per user.")
|
|
||||||
|
|
||||||
except sqlite3.OperationalError as e:
|
|
||||||
if "locked" in str(e):
|
|
||||||
print("\nERROR: Database is LOCKED.")
|
|
||||||
print("Please STOP the running application (Docker) and try again.")
|
|
||||||
else:
|
|
||||||
print(f"\nError: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\nUnexpected Error: {e}")
|
|
||||||
@@ -291,11 +291,15 @@ def run_scheduled_notifications(db: "Session"):
|
|||||||
Schedule:
|
Schedule:
|
||||||
- Thursday at 12:00: Presence reminder for next week
|
- Thursday at 12:00: Presence reminder for next week
|
||||||
- Friday at 12:00: Weekly parking summary
|
- Friday at 12:00: Weekly parking summary
|
||||||
- Daily at user's preferred time: Daily parking reminder (Mon-Fri)
|
- Daily at user's preferred time: Daily parking reminder (Only on open days)
|
||||||
"""
|
"""
|
||||||
from database.models import User
|
from database.models import User, OfficeWeeklyClosingDay, OfficeClosingDay
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
# Use configured timezone
|
||||||
|
tz = ZoneInfo(config.TIMEZONE)
|
||||||
|
now = datetime.now(tz)
|
||||||
|
|
||||||
now = datetime.now()
|
|
||||||
current_hour = now.hour
|
current_hour = now.hour
|
||||||
current_minute = now.minute
|
current_minute = now.minute
|
||||||
current_weekday = now.weekday() # 0=Monday, 6=Sunday
|
current_weekday = now.weekday() # 0=Monday, 6=Sunday
|
||||||
@@ -309,9 +313,39 @@ def run_scheduled_notifications(db: "Session"):
|
|||||||
next_week = get_next_week_dates(today_date)
|
next_week = get_next_week_dates(today_date)
|
||||||
send_presence_reminder(user, next_week, db)
|
send_presence_reminder(user, next_week, db)
|
||||||
|
|
||||||
# Daily parking reminder at user's preferred time (working days only)
|
# Daily parking reminder at user's preferred time
|
||||||
if current_weekday < 5: # Monday to Friday
|
user_hour = user.notify_daily_parking_hour or 8
|
||||||
user_hour = user.notify_daily_parking_hour or 8
|
user_minute = user.notify_daily_parking_minute or 0
|
||||||
user_minute = user.notify_daily_parking_minute or 0
|
|
||||||
if current_hour == user_hour and abs(current_minute - user_minute) < 5:
|
# Check if it's the right time for this user
|
||||||
|
if current_hour == user_hour and abs(current_minute - user_minute) < 5:
|
||||||
|
# Check if Office is OPEN today
|
||||||
|
is_office_open = True
|
||||||
|
|
||||||
|
if user.office:
|
||||||
|
# Check weekly closing days (e.g. Sat/Sun)
|
||||||
|
# Note: WeekDay enum matches python weekday (0=Mon)
|
||||||
|
weekly_closed = db.query(OfficeWeeklyClosingDay).filter(
|
||||||
|
OfficeWeeklyClosingDay.office_id == user.office_id,
|
||||||
|
OfficeWeeklyClosingDay.weekday == current_weekday
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if weekly_closed:
|
||||||
|
is_office_open = False
|
||||||
|
|
||||||
|
# Check specific closing days (Holidays)
|
||||||
|
if is_office_open:
|
||||||
|
specific_closed = db.query(OfficeClosingDay).filter(
|
||||||
|
OfficeClosingDay.office_id == user.office_id,
|
||||||
|
OfficeClosingDay.date == today_date
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if specific_closed:
|
||||||
|
is_office_open = False
|
||||||
|
else:
|
||||||
|
# Fallback if no office assigned: default to Mon-Fri open
|
||||||
|
if current_weekday >= 5:
|
||||||
|
is_office_open = False
|
||||||
|
|
||||||
|
if is_office_open:
|
||||||
send_daily_parking_reminder(user, now, db)
|
send_daily_parking_reminder(user, now, db)
|
||||||
|
|||||||
Reference in New Issue
Block a user