feat: Set up SessionMiddleware and Jinja2 Template Configuration
- Add SECRET_KEY environment variable and .env file for session management - Configure SessionMiddleware with FastAPI for user session handling - Set up Jinja2 template engine with template directory configuration - Mount static files directory for CSS, JS, and image assets - Create comprehensive base.html template with Bootstrap 5 CDN - Add Bootstrap Icons and custom styling support - Include responsive navigation with user authentication state - Create placeholder CSS and JavaScript files for customization - Add aiofiles dependency for static file serving This establishes the web framework foundation with session management and templating system ready for frontend development.
This commit is contained in:
3
.env
Normal file
3
.env
Normal file
@@ -0,0 +1,3 @@
|
||||
# Delphi Database Environment Configuration
|
||||
SECRET_KEY=your-secret-key-here-change-this-in-production
|
||||
DATABASE_URL=sqlite:///./delphi.db
|
||||
33
app/main.py
33
app/main.py
@@ -5,20 +5,36 @@ This module initializes the FastAPI application, sets up database connections,
|
||||
and provides the main application instance.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, Depends
|
||||
from fastapi import FastAPI, Depends, Request
|
||||
from fastapi.middleware.sessions import SessionMiddleware
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from sqlalchemy.orm import Session
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from .database import create_tables, get_db, get_database_url
|
||||
from .models import User
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Get SECRET_KEY from environment variables
|
||||
SECRET_KEY = os.getenv("SECRET_KEY")
|
||||
if not SECRET_KEY:
|
||||
raise ValueError("SECRET_KEY environment variable must be set")
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configure Jinja2 templates
|
||||
templates = Jinja2Templates(directory="app/templates")
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
@@ -54,6 +70,21 @@ app = FastAPI(
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# Add CORS middleware for cross-origin requests
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # In production, specify allowed origins
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Add SessionMiddleware for session management
|
||||
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)
|
||||
|
||||
# Mount static files directory
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
|
||||
@@ -1 +1,87 @@
|
||||
<!-- Base template with Bootstrap 5 and navigation -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Delphi Database{% endblock %}</title>
|
||||
|
||||
<!-- Bootstrap 5 CSS CDN -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Bootstrap Icons -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link href="{{ url_for('static', path='/css/custom.css') }}" rel="stylesheet">
|
||||
|
||||
{% block extra_head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="{{ url_for('static', path='/logo/delphi-logo.webp') }}" alt="Delphi Logo" width="30" height="30" class="d-inline-block align-text-top me-2">
|
||||
Delphi Database
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.url.path == '/' %}active{% endif %}" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if 'dashboard' in request.url.path %}active{% endif %}" href="/dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if 'admin' in request.url.path %}active{% endif %}" href="/admin">Admin</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav">
|
||||
{% if 'user' in session %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-person-circle me-1"></i>{{ session.user.username }}
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="userDropdown">
|
||||
<li><a class="dropdown-item" href="/profile">Profile</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="/logout">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if 'login' in request.url.path %}active{% endif %}" href="/login">Login</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="container-fluid mt-4">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-light text-center text-muted mt-5 py-3">
|
||||
<div class="container">
|
||||
<small>© 2025 Delphi Database. All rights reserved.</small>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap 5 JS Bundle CDN -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Custom JS -->
|
||||
<script src="{{ url_for('static', path='/js/custom.js') }}"></script>
|
||||
|
||||
{% block extra_scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,3 +7,4 @@ passlib[bcrypt]==1.7.4
|
||||
python-dotenv==1.0.0
|
||||
uvicorn[standard]==0.24.0
|
||||
jinja2==3.1.2
|
||||
aiofiles==23.2.1
|
||||
|
||||
51
static/css/custom.css
Normal file
51
static/css/custom.css
Normal file
@@ -0,0 +1,51 @@
|
||||
/* Custom CSS for Delphi Database */
|
||||
|
||||
/* Additional styles can be added here */
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Custom navbar styles */
|
||||
.navbar-brand {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Custom card styles */
|
||||
.card {
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
/* Custom button styles */
|
||||
.btn-primary {
|
||||
background-color: #0d6efd;
|
||||
border-color: #0d6efd;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #0b5ed7;
|
||||
border-color: #0a58ca;
|
||||
}
|
||||
|
||||
/* Custom form styles */
|
||||
.form-control:focus {
|
||||
border-color: #86b7fe;
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
/* Custom table styles */
|
||||
.table th {
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
}
|
||||
|
||||
/* Custom alert styles */
|
||||
.alert {
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
/* Custom footer styles */
|
||||
footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
82
static/js/custom.js
Normal file
82
static/js/custom.js
Normal file
@@ -0,0 +1,82 @@
|
||||
// Custom JavaScript for Delphi Database
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize tooltips if any
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
|
||||
// Auto-hide alerts after 5 seconds
|
||||
var alerts = document.querySelectorAll('.alert:not(.alert-permanent)');
|
||||
alerts.forEach(function(alert) {
|
||||
setTimeout(function() {
|
||||
var bsAlert = new bootstrap.Alert(alert);
|
||||
bsAlert.close();
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
// Confirm delete actions
|
||||
var deleteButtons = document.querySelectorAll('[data-confirm-delete]');
|
||||
deleteButtons.forEach(function(button) {
|
||||
button.addEventListener('click', function(e) {
|
||||
var message = this.getAttribute('data-confirm-delete') || 'Are you sure you want to delete this item?';
|
||||
if (!confirm(message)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Form validation enhancement
|
||||
var forms = document.querySelectorAll('.needs-validation');
|
||||
Array.prototype.slice.call(forms).forEach(function(form) {
|
||||
form.addEventListener('submit', function(event) {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
}, false);
|
||||
});
|
||||
|
||||
// Dynamic form field enabling/disabling
|
||||
var toggleFields = document.querySelectorAll('[data-toggle-field]');
|
||||
toggleFields.forEach(function(element) {
|
||||
element.addEventListener('change', function() {
|
||||
var targetSelector = this.getAttribute('data-toggle-field');
|
||||
var targetField = document.querySelector(targetSelector);
|
||||
if (targetField) {
|
||||
targetField.disabled = !this.checked;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Utility functions
|
||||
function formatDate(dateString) {
|
||||
if (!dateString) return '';
|
||||
var date = new Date(dateString);
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
function formatCurrency(amount) {
|
||||
if (!amount) return '$0.00';
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD'
|
||||
}).format(amount);
|
||||
}
|
||||
|
||||
function showLoading(button) {
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Loading...';
|
||||
}
|
||||
}
|
||||
|
||||
function hideLoading(button, originalText) {
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user