Access Denied implementation

This commit is contained in:
knuthtimo-lab 2025-12-31 17:33:23 +01:00
parent 0347ee1342
commit b22539bf68
1 changed files with 32 additions and 129 deletions

View File

@ -15,7 +15,10 @@ function App() {
const [allRules, setAllRules] = useState([]);
const [showAllRules, setShowAllRules] = useState(false);
const [toast, setToast] = useState(null);
const [isAuthenticating, setIsAuthenticating] = useState(false);
const [isAuthenticating, setIsAuthenticating] = useState(() => {
const params = new URLSearchParams(window.location.search);
return !!(params.get('email') && params.get('expires') && params.get('signature'));
});
const [singleEmailMode, setSingleEmailMode] = useState(false);
const showToast = (message, type = 'success') => {
@ -184,106 +187,34 @@ function App() {
<Header />
<main className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Single Email Mode Header */}
{singleEmailMode && email && (
<div className="card mb-8 bg-gradient-to-r from-primary-50 to-primary-100 border-primary-200">
<div className="flex items-center gap-4">
<div className="bg-primary-600 text-white rounded-full w-12 h-12 flex items-center justify-center">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
<div className="flex-1">
<h2 className="text-xl font-bold text-gray-900">Your Email Configuration</h2>
<p className="text-sm text-primary-700 font-medium mt-1">{email}</p>
</div>
</div>
</div>
)}
{/* Email Search Section - Only show if NOT in single email mode */}
{!singleEmailMode && (
<>
<div className="card mb-8">
<div className="flex items-start justify-between mb-4">
<div>
<h2 className="text-2xl font-bold text-gray-900">Manage Email Rules</h2>
<p className="text-sm text-gray-600 mt-1">
Search for an email address to configure auto-replies and forwarding
{/* Authenticating State */}
{isAuthenticating && (
<div className="text-center py-16">
<FiRefreshCw className="w-16 h-16 text-primary-600 mx-auto mb-4 animate-spin" />
<h3 className="text-lg font-semibold text-gray-900 mb-2">
Authenticating from Roundcube...
</h3>
<p className="text-gray-600 max-w-md mx-auto">
Please wait while we verify your session
</p>
</div>
<button
onClick={() => setShowAllRules(!showAllRules)}
className="btn-secondary flex items-center gap-2"
>
<FiList className="w-4 h-4" />
All Rules ({allRules.length})
</button>
</div>
)}
<div className="flex gap-2">
<div className="flex-1">
<input
type="email"
value={email}
onChange={(e) => {
setEmail(e.target.value);
setEmailError('');
}}
onKeyPress={handleKeyPress}
placeholder="Enter email address (e.g., user@example.com)"
className={`input-field ${emailError ? 'border-red-500 focus:ring-red-500' : ''}`}
/>
{emailError && (
<p className="mt-1 text-sm text-red-600">{emailError}</p>
)}
{/* Access Denied / Invalid State */}
{!isAuthenticating && !currentRule && (
<div className="text-center py-16">
<div className="bg-red-100 rounded-full w-20 h-20 flex items-center justify-center mx-auto mb-6">
<svg className="w-10 h-10 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</div>
<button
onClick={handleSearch}
disabled={isLoading}
className="btn-primary flex items-center gap-2 whitespace-nowrap"
>
{isLoading ? (
<>
<FiRefreshCw className="w-4 h-4 animate-spin" />
Loading...
</>
) : (
<>
<FiSearch className="w-4 h-4" />
Search
</>
)}
</button>
<h3 className="text-2xl font-bold text-gray-900 mb-2">
Access Denied
</h3>
<p className="text-gray-600 max-w-md mx-auto mb-8">
This application can only be accessed via the secure link provided in your Roundcube configuration.
</p>
</div>
</div>
{/* All Rules List */}
{showAllRules && allRules.length > 0 && (
<div className="card mb-8">
<h3 className="text-lg font-semibold text-gray-900 mb-4">All Configured Rules</h3>
<div className="space-y-2 max-h-96 overflow-y-auto">
{allRules.map((rule, index) => (
<div
key={index}
onClick={() => handleSelectRule(rule.email_address)}
className="flex items-center justify-between p-4 bg-gray-50 hover:bg-gray-100 border border-gray-200 rounded-lg cursor-pointer transition-colors"
>
<div>
<p className="font-medium text-gray-900">{rule.email_address}</p>
<div className="flex gap-3 mt-1 text-xs text-gray-600">
<span className={rule.ooo_active ? 'text-green-600 font-semibold' : ''}>
OOO: {rule.ooo_active ? 'Active' : 'Inactive'}
</span>
<span>Forwards: {rule.forwards?.length || 0}</span>
</div>
</div>
</div>
))}
</div>
</div>
)}
</>
)}
{/* Configuration Tabs */}
@ -314,16 +245,14 @@ function App() {
<nav className="flex gap-8">
<button
onClick={() => setActiveTab('ooo')}
className={`pb-3 px-1 transition-colors ${
activeTab === 'ooo' ? 'tab-active' : 'tab-inactive'
className={`pb-3 px-1 transition-colors ${activeTab === 'ooo' ? 'tab-active' : 'tab-inactive'
}`}
>
Out of Office
</button>
<button
onClick={() => setActiveTab('forwarding')}
className={`pb-3 px-1 transition-colors ${
activeTab === 'forwarding' ? 'tab-active' : 'tab-inactive'
className={`pb-3 px-1 transition-colors ${activeTab === 'forwarding' ? 'tab-active' : 'tab-inactive'
}`}
>
Email Forwarding
@ -342,32 +271,6 @@ function App() {
</div>
</div>
)}
{/* Empty State */}
{!currentRule && !showAllRules && !isAuthenticating && (
<div className="text-center py-16">
<FiSearch className="w-16 h-16 text-gray-300 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-900 mb-2">
Search for an email address
</h3>
<p className="text-gray-600 max-w-md mx-auto">
Enter an email address above to view and configure its out-of-office auto-replies and forwarding rules
</p>
</div>
)}
{/* Authenticating State */}
{isAuthenticating && (
<div className="text-center py-16">
<FiRefreshCw className="w-16 h-16 text-primary-600 mx-auto mb-4 animate-spin" />
<h3 className="text-lg font-semibold text-gray-900 mb-2">
Authenticating from Roundcube...
</h3>
<p className="text-gray-600 max-w-md mx-auto">
Please wait while we verify your session
</p>
</div>
)}
</main>
{/* Toast Notifications */}