new changes

This commit is contained in:
Niranjan
2026-04-07 05:05:28 +05:30
parent 7c070224bd
commit a18bba15f2
29975 changed files with 3247495 additions and 2761 deletions

View File

@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import Modal from 'react-bootstrap/Modal'
import { apiRequest } from '../api/client'
import { Shield, Loader2 } from 'lucide-react'
import { PageHeader, AdminButton, AdminAlert } from '../components/admin'
interface Domain {
id: number
@@ -66,150 +67,136 @@ export function DomainsPage() {
const hasCert = (domain: string) =>
certificates.some((c) => c.name === domain || c.name.startsWith(domain + ' '))
if (loading) return <div className="text-gray-500">Loading...</div>
if (error) return <div className="p-4 rounded bg-red-100 text-red-700">{error}</div>
if (loading) {
return (
<>
<PageHeader title="Domains & SSL" />
<p className="text-secondary">Loading</p>
</>
)
}
return (
<div>
<h1 className="text-2xl font-bold mb-4 text-gray-800 dark:text-white">Domains & SSL</h1>
<>
<PageHeader title="Domains & SSL" />
<div className="mb-6 p-4 bg-amber-50 dark:bg-amber-900/20 rounded-lg text-sm text-amber-800 dark:text-amber-200">
<p>Request Let&apos;s Encrypt certificates for your site domains. Requires certbot and nginx configured for the domain.</p>
{error ? <AdminAlert className="mb-3">{error}</AdminAlert> : null}
<div className="alert alert-warning small mb-4">
Request Let&apos;s Encrypt certificates for your site domains. Requires certbot and nginx configured for the domain.
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
<h2 className="px-4 py-2 border-b dark:border-gray-700 font-medium text-gray-800 dark:text-white">
Domains (from sites)
</h2>
<div className="divide-y divide-gray-200 dark:divide-gray-700 max-h-80 overflow-y-auto">
{domains.length === 0 ? (
<div className="px-4 py-8 text-center text-gray-500">No domains. Add a site first.</div>
) : (
domains.map((d) => (
<div
key={d.id}
className="flex items-center justify-between gap-2 px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/30"
>
<div>
<span className="font-mono text-gray-900 dark:text-white">{d.name}</span>
{d.port !== '80' && (
<span className="ml-2 text-gray-500 text-sm">:{d.port}</span>
)}
<span className="ml-2 text-gray-500 text-sm">({d.site_name})</span>
<div className="row g-4">
<div className="col-lg-6">
<div className="card h-100">
<div className="card-header">Domains (from sites)</div>
<div className="list-group list-group-flush overflow-auto" style={{ maxHeight: '20rem' }}>
{domains.length === 0 ? (
<div className="list-group-item text-secondary text-center py-4">No domains. Add a site first.</div>
) : (
domains.map((d) => (
<div
key={d.id}
className="list-group-item d-flex align-items-center justify-content-between gap-2 flex-wrap"
>
<div className="small">
<span className="font-monospace">{d.name}</span>
{d.port !== '80' ? <span className="text-secondary ms-1">:{d.port}</span> : null}
<span className="text-secondary ms-2">({d.site_name})</span>
</div>
<div>
{hasCert(d.name) ? (
<span className="text-success small">
<i className="ti ti-shield-check me-1" aria-hidden />
Cert
</span>
) : (
<AdminButton
variant="outline-primary"
size="sm"
disabled={!!requesting}
onClick={() => {
setRequestDomain(d)
setRequestEmail('')
}}
>
{requesting === d.name ? (
<span className="spinner-border spinner-border-sm" role="status" />
) : (
'Request SSL'
)}
</AdminButton>
)}
</div>
</div>
<div className="flex items-center gap-2">
{hasCert(d.name) ? (
<span className="text-green-600 dark:text-green-400 text-sm flex items-center gap-1">
<Shield className="w-4 h-4" /> Cert
</span>
) : (
<button
onClick={() => {
setRequestDomain(d)
setRequestEmail('')
}}
disabled={!!requesting}
className="text-sm px-2 py-1 rounded bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 hover:bg-blue-200 dark:hover:bg-blue-900/50 disabled:opacity-50"
>
{requesting === d.name ? (
<Loader2 className="w-4 h-4 animate-spin inline" />
) : (
'Request SSL'
)}
</button>
)}
</div>
</div>
))
)}
))
)}
</div>
</div>
</div>
<div className="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
<h2 className="px-4 py-2 border-b dark:border-gray-700 font-medium text-gray-800 dark:text-white">
Certificates
</h2>
<div className="divide-y divide-gray-200 dark:divide-gray-700 max-h-80 overflow-y-auto">
{certificates.length === 0 ? (
<div className="px-4 py-8 text-center text-gray-500">No certificates yet</div>
) : (
certificates.map((c) => (
<div
key={c.name}
className="flex items-center gap-2 px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/30"
>
<Shield className="w-4 h-4 text-green-600 flex-shrink-0" />
<span className="font-mono text-gray-900 dark:text-white">{c.name}</span>
</div>
))
)}
<div className="col-lg-6">
<div className="card h-100">
<div className="card-header">Certificates</div>
<div className="list-group list-group-flush overflow-auto" style={{ maxHeight: '20rem' }}>
{certificates.length === 0 ? (
<div className="list-group-item text-secondary text-center py-4">No certificates yet</div>
) : (
certificates.map((c) => (
<div key={c.name} className="list-group-item d-flex align-items-center gap-2">
<i className="ti ti-shield-check text-success flex-shrink-0" aria-hidden />
<span className="font-monospace small text-break">{c.name}</span>
</div>
))
)}
</div>
</div>
</div>
</div>
{requestDomain && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-full max-w-md">
<h2 className="text-xl font-bold mb-4 text-gray-800 dark:text-white">
Request SSL for {requestDomain.name}
</h2>
<form onSubmit={handleRequestCert} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Domain
</label>
<input
type="text"
value={requestDomain.name}
readOnly
className="w-full px-4 py-2 border rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
/>
<Modal show={!!requestDomain} onHide={() => setRequestDomain(null)} centered>
<Modal.Header closeButton>
<Modal.Title>Request SSL for {requestDomain?.name}</Modal.Title>
</Modal.Header>
{requestDomain ? (
<form onSubmit={handleRequestCert}>
<Modal.Body>
<div className="mb-3">
<label className="form-label">Domain</label>
<input type="text" value={requestDomain.name} readOnly className="form-control-plaintext border rounded px-3 py-2 bg-body-secondary" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Webroot (site path)
</label>
<div className="mb-3">
<label className="form-label">Webroot (site path)</label>
<input
type="text"
value={requestDomain.site_path}
readOnly
className="w-full px-4 py-2 border rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
className="form-control-plaintext border rounded px-3 py-2 bg-body-secondary"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Email (for Let&apos;s Encrypt)
</label>
<div className="mb-0">
<label className="form-label">Email (for Let&apos;s Encrypt)</label>
<input
type="email"
value={requestEmail}
onChange={(e) => setRequestEmail(e.target.value)}
placeholder="admin@example.com"
className="w-full px-4 py-2 border rounded-lg bg-white dark:bg-gray-700"
className="form-control"
required
/>
</div>
<div className="flex gap-2 justify-end pt-2">
<button
type="button"
onClick={() => setRequestDomain(null)}
className="px-4 py-2 text-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg"
>
Cancel
</button>
<button
type="submit"
disabled={!!requesting}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg disabled:opacity-50"
>
{requesting ? 'Requesting...' : 'Request'}
</button>
</div>
</form>
</div>
</div>
)}
</div>
</Modal.Body>
<Modal.Footer>
<AdminButton type="button" variant="secondary" onClick={() => setRequestDomain(null)}>
Cancel
</AdminButton>
<AdminButton type="submit" variant="primary" disabled={!!requesting}>
{requesting ? 'Requesting…' : 'Request'}
</AdminButton>
</Modal.Footer>
</form>
) : null}
</Modal>
</>
)
}