feat(docs): upgrade installation, dashboard, and certificate request guides with premium UI and assets
@@ -1,7 +1,8 @@
|
||||
import useSWR from 'swr'
|
||||
import axios from 'axios'
|
||||
import { Tab } from '@headlessui/react' // Nextra uses headlessui usually, or we can build simple tabs
|
||||
import { useState } from 'react'
|
||||
import { Download, Terminal, Smartphone, Shield, Monitor, FileCode, Check, Copy, AlertCircle } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
// Interface matching the API response
|
||||
interface CaCertificate {
|
||||
@@ -22,191 +23,318 @@ const fetcher = (url: string) => axios.get(url).then(res => res.data.data)
|
||||
export function DynamicInstallationGuide() {
|
||||
const { data: certificates, error, isLoading } = useSWR<CaCertificate[]>('https://api.trustlab.dyzulk.com/api/public/ca-certificates', fetcher)
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const [copiedId, setCopiedId] = useState<string | null>(null)
|
||||
|
||||
if (error) return <div className="p-4 border border-red-200 bg-red-50 text-red-600 rounded-lg">Failed to load live certificates. CLI: <code>curl -sL https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.sh | sudo bash</code></div>
|
||||
if (isLoading || !certificates) return <div className="p-4 text-gray-500 animate-pulse bg-gray-50 rounded-lg">Loading dynamic installer links...</div>
|
||||
const handleCopy = (text: string, id: string) => {
|
||||
navigator.clipboard.writeText(text)
|
||||
setCopiedId(id)
|
||||
setTimeout(() => setCopiedId(null), 2000)
|
||||
}
|
||||
|
||||
if (error) return (
|
||||
<div className="flex items-center gap-3 p-4 my-4 text-red-600 bg-red-50 border border-red-100 rounded-lg dark:bg-red-900/10 dark:text-red-400 dark:border-red-900/20">
|
||||
<AlertCircle className="w-5 h-5 flex-shrink-0" />
|
||||
<div className="text-sm">
|
||||
<span className="font-semibold">Unable to load live certificates.</span>
|
||||
<p className="mt-1 opacity-90">Please ensure you can access <code>api.trustlab.dyzulk.com</code>.</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
if (isLoading || !certificates) return (
|
||||
<div className="w-full h-64 my-6 bg-gray-50 dark:bg-neutral-900 rounded-lg animate-pulse flex items-center justify-center">
|
||||
<span className="text-gray-400 dark:text-gray-600 text-sm font-medium">Loading installer data...</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
const root = certificates.find(c => c.type === 'root')
|
||||
const intermediates = certificates.filter(c => c.type !== 'root')
|
||||
|
||||
// Helper to format the table rows
|
||||
const renderTable = (os: 'windows' | 'mac' | 'linux' | 'android') => {
|
||||
return (
|
||||
<div className="overflow-x-auto my-4 border rounded-lg">
|
||||
<table className="w-full text-sm text-left">
|
||||
<thead className="text-xs uppercase bg-gray-50 dark:bg-neutral-900 text-gray-700 dark:text-gray-300">
|
||||
<tr>
|
||||
<th className="px-6 py-3">Certificate</th>
|
||||
<th className="px-6 py-3">Raw File</th>
|
||||
<th className="px-6 py-3">
|
||||
{os === 'windows' ? 'Auto-Installer Script' :
|
||||
os === 'mac' ? 'Configuration Profile' :
|
||||
os === 'linux' ? 'One-Liner Installer' : 'Alternative Format'}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-800">
|
||||
{/* ROOT CA */}
|
||||
{root && (
|
||||
<tr className="bg-white dark:bg-neutral-950">
|
||||
<td className="px-6 py-4 font-medium text-gray-900 dark:text-gray-100 flex flex-col">
|
||||
<span>Root CA</span>
|
||||
<span className="text-xs text-gray-500 font-mono">{root.name}</span>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<a href={root.cdn_url || '#'} className="text-blue-600 hover:underline font-semibold" target="_blank" rel="noopener noreferrer">
|
||||
Download .crt
|
||||
</a>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
{os === 'windows' && root.bat_cdn_url && (
|
||||
<a href={root.bat_cdn_url} className="text-blue-600 hover:underline">Download .bat</a>
|
||||
)}
|
||||
{os === 'mac' && root.mac_cdn_url && (
|
||||
<a href={root.mac_cdn_url} className="text-blue-600 hover:underline">Download .mobileconfig</a>
|
||||
)}
|
||||
{os === 'android' && root.der_cdn_url && (
|
||||
<a href={root.der_cdn_url} className="text-blue-600 hover:underline">Download .der</a>
|
||||
)}
|
||||
{os === 'linux' && root.linux_cdn_url && (
|
||||
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs select-all">
|
||||
curl -sL {root.linux_cdn_url} | sudo bash
|
||||
</code>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{/* INTERMEDIATES */}
|
||||
{intermediates.map(cert => (
|
||||
<tr key={cert.serial} className="bg-white dark:bg-neutral-950">
|
||||
<td className="px-6 py-4 font-medium text-gray-900 dark:text-gray-100 flex flex-col">
|
||||
<span>Intermediate</span>
|
||||
<span className="text-xs text-gray-500 font-mono">{cert.name}</span>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<a href={cert.cdn_url || '#'} className="text-blue-600 hover:underline" target="_blank" rel="noopener noreferrer">
|
||||
Download .crt
|
||||
</a>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
{os === 'windows' && cert.bat_cdn_url && (
|
||||
<a href={cert.bat_cdn_url} className="text-blue-600 hover:underline">Download .bat</a>
|
||||
)}
|
||||
{os === 'mac' && cert.mac_cdn_url && (
|
||||
<a href={cert.mac_cdn_url} className="text-blue-600 hover:underline">Download .mobileconfig</a>
|
||||
)}
|
||||
{os === 'linux' && cert.linux_cdn_url && (
|
||||
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs select-all">
|
||||
curl -sL {cert.linux_cdn_url} | sudo bash
|
||||
</code>
|
||||
)}
|
||||
{os === 'android' && cert.der_cdn_url && (
|
||||
<a href={cert.der_cdn_url} className="text-blue-600 hover:underline">Download .der</a>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{ id: 'windows', label: 'Windows' },
|
||||
{ id: 'mac', label: 'macOS' },
|
||||
{ id: 'android', label: 'Android' },
|
||||
{ id: 'linux', label: 'Linux (CLI)' },
|
||||
{ id: 'windows', label: 'Windows', icon: Monitor },
|
||||
{ id: 'mac', label: 'macOS', icon: Monitor }, // Changed icon to Monitor for consistency or distinctiveness? User has 'Smartphone' for Mac in previous code, likely mistake. I will use Monitor or Laptop. Actually let's keeps consistent.
|
||||
{ id: 'ios', label: 'iOS', icon: Smartphone },
|
||||
{ id: 'android', label: 'Android', icon: Smartphone },
|
||||
{ id: 'linux', label: 'Linux (CLI)', icon: Terminal },
|
||||
]
|
||||
|
||||
const activeTab = tabs[selectedIndex].id as 'windows' | 'mac' | 'ios' | 'linux' | 'android'
|
||||
|
||||
return (
|
||||
<div className="mt-6">
|
||||
<div className="flex space-x-2 border-b border-gray-200 dark:border-gray-800 mb-4 overflow-x-auto">
|
||||
{tabs.map((tab, idx) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setSelectedIndex(idx)}
|
||||
className={`px-4 py-2 text-sm font-medium transition-colors border-b-2 whitespace-nowrap ${
|
||||
selectedIndex === idx
|
||||
? 'border-blue-500 text-blue-600 dark:text-blue-400'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300'
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
<div className="mt-8 mb-12">
|
||||
{/* Tabs Header - Nextra Style (Bottom Border) */}
|
||||
<div className="flex overflow-x-auto border-b border-gray-200 dark:border-neutral-800 scrollbar-hide">
|
||||
{tabs.map((tab, idx) => {
|
||||
const isActive = selectedIndex === idx
|
||||
const Icon = tab.icon
|
||||
return (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setSelectedIndex(idx)}
|
||||
className={clsx(
|
||||
"flex items-center gap-2 px-4 py-3 text-sm font-medium transition-all relative outline-none focus-visible:ring-2 focus-visible:ring-blue-500 whitespace-nowrap",
|
||||
isActive
|
||||
? "text-blue-600 dark:text-blue-400"
|
||||
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:bg-gray-50 dark:hover:bg-neutral-900/50"
|
||||
)}
|
||||
>
|
||||
<Icon className={clsx("w-4 h-4", isActive ? "stroke-[2.5px]" : "stroke-2")} />
|
||||
{tab.label}
|
||||
{isActive && (
|
||||
<div className="absolute bottom-0 left-0 w-full h-0.5 bg-blue-600 dark:bg-blue-400 rounded-t-full" />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="min-h-[300px]">
|
||||
{/* Windows Tab */}
|
||||
{selectedIndex === 0 && (
|
||||
<div className="animate-in fade-in slide-in-from-bottom-2 duration-300">
|
||||
{renderTable('windows')}
|
||||
<div className="space-y-4 text-gray-800 dark:text-gray-200 text-sm mt-4 p-4 bg-gray-50 dark:bg-white/5 rounded-lg border dark:border-white/10">
|
||||
<h4 className="font-bold">Installation Steps (Raw File)</h4>
|
||||
<ol className="list-decimal pl-5 space-y-1">
|
||||
<li>Double-click the downloaded <code>.crt</code> file.</li>
|
||||
<li>Click <strong>Install Certificate</strong>.</li>
|
||||
<li>Select <strong>Local Machine</strong> (requires Admin).</li>
|
||||
<li>Choose "Place all certificates in the following store".</li>
|
||||
<li>Select <strong>Trusted Root Certification Authorities</strong> (for Root) or <strong>Intermediate Certification Authorities</strong> (for Intermediates).</li>
|
||||
<li>FInish.</li>
|
||||
</ol>
|
||||
<p className="text-xs text-gray-500 italic mt-2">*Note: Using the Auto-Installer Script (.bat) handles all of this automatically.*</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Tab Content */}
|
||||
<div className="min-h-[300px] mt-6 animate-in fade-in slide-in-from-bottom-1 duration-200">
|
||||
<div className="overflow-x-auto border border-gray-200 dark:border-neutral-800 rounded-lg shadow-sm bg-white dark:bg-neutral-950">
|
||||
<table className="w-full text-sm text-left min-w-[600px]">
|
||||
<thead className="bg-gray-50/50 dark:bg-neutral-900/50 border-b border-gray-200 dark:border-neutral-800">
|
||||
<tr>
|
||||
<th className="px-6 py-4 font-semibold text-gray-900 dark:text-gray-100 w-1/3">Certificate</th>
|
||||
<th className="px-6 py-4 font-semibold text-gray-900 dark:text-gray-100">Raw Format</th>
|
||||
<th className="px-6 py-4 font-semibold text-gray-900 dark:text-gray-100">
|
||||
{activeTab === 'linux' ? 'One-Liner Installer' :
|
||||
activeTab === 'android' ? 'Alternative (.der)' :
|
||||
activeTab === 'ios' ? 'Config Profile' :
|
||||
activeTab === 'mac' ? 'Config Profile' :
|
||||
'Auto-Installer'}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100 dark:divide-neutral-800">
|
||||
{/* Root Row */}
|
||||
{root && (
|
||||
<tr className="group hover:bg-gray-50/50 dark:hover:bg-neutral-900/20 transition-colors">
|
||||
<td className="px-6 py-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="mt-1 p-1.5 bg-purple-100 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400 rounded-md">
|
||||
<Shield className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900 dark:text-gray-100">Root CA</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 font-mono mt-0.5">{root.name}</div>
|
||||
<div className="text-[10px] text-gray-400 mt-1">Exp: {new Date(root.expires_at).getFullYear()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<a href={root.cdn_url || '#'} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-2 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-neutral-900 border border-gray-200 dark:border-neutral-800 rounded-md hover:border-blue-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors shadow-sm">
|
||||
<Download className="w-3.5 h-3.5" />
|
||||
Download .crt
|
||||
</a>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<InstallerCell cert={root} os={activeTab} handleCopy={handleCopy} copiedId={copiedId} />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{/* macOS Tab */}
|
||||
{selectedIndex === 1 && (
|
||||
<div className="animate-in fade-in slide-in-from-bottom-2 duration-300">
|
||||
{renderTable('mac')}
|
||||
<div className="space-y-4 text-gray-800 dark:text-gray-200 text-sm mt-4 p-4 bg-gray-50 dark:bg-white/5 rounded-lg border dark:border-white/10">
|
||||
<h4 className="font-bold">Installation Steps</h4>
|
||||
<ul className="list-disc pl-5 space-y-1">
|
||||
<li><strong>Option 1 (Profile):</strong> Download <code>.mobileconfig</code>, go to <em>System Settings > Privacy & Security > Profiles</em> to install.</li>
|
||||
<li><strong>Option 2 (Raw):</strong> Download <code>.crt</code>, open in <strong>Keychain Access</strong>, then set Trust settings to <strong>Always Trust</strong>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Intermediates Rows */}
|
||||
{intermediates.map(cert => (
|
||||
<tr key={cert.serial} className="group hover:bg-gray-50/50 dark:hover:bg-neutral-900/20 transition-colors">
|
||||
<td className="px-6 py-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="mt-1 p-1.5 bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded-md">
|
||||
<FileCode className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-gray-900 dark:text-gray-100">Intermediate CA</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 font-mono mt-0.5">{cert.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<a href={cert.cdn_url || '#'} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-2 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-neutral-900 border border-gray-200 dark:border-neutral-800 rounded-md hover:border-blue-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors shadow-sm">
|
||||
<Download className="w-3.5 h-3.5" />
|
||||
Download .crt
|
||||
</a>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<InstallerCell cert={cert} os={activeTab} handleCopy={handleCopy} copiedId={copiedId} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Android Tab */}
|
||||
{selectedIndex === 2 && (
|
||||
<div className="animate-in fade-in slide-in-from-bottom-2 duration-300">
|
||||
{renderTable('android')}
|
||||
<div className="space-y-4 text-gray-800 dark:text-gray-200 text-sm mt-4 p-4 bg-gray-50 dark:bg-white/5 rounded-lg border dark:border-white/10">
|
||||
<h4 className="font-bold">Installation Steps</h4>
|
||||
<ol className="list-decimal pl-5 space-y-1">
|
||||
<li>Download the <code>.crt</code> (or <code>.der</code> if required by your device).</li>
|
||||
<li>Go to <strong>Settings > Security > Encryption & Credentials</strong>.</li>
|
||||
<li>Tap <strong>Install a certificate > CA Certificate</strong>.</li>
|
||||
<li>Select "Install anyway" and choose the file.</li>
|
||||
{/* Contextual Instructions Footer - Detailed Manual Steps */}
|
||||
<div className="mt-6 border-t border-gray-100 dark:border-neutral-800 pt-6">
|
||||
{activeTab === 'windows' && (
|
||||
<div className="space-y-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
|
||||
<Monitor className="w-4 h-4 text-blue-500" />
|
||||
Manual Installation (Raw .crt)
|
||||
</h4>
|
||||
<div className="grid md:grid-cols-2 gap-6 text-sm text-gray-600 dark:text-gray-400">
|
||||
<div className="space-y-2">
|
||||
<strong className="text-gray-800 dark:text-gray-200 block">For Root CA:</strong>
|
||||
<ol className="list-decimal pl-4 space-y-1 marker:text-gray-400">
|
||||
<li>Double-click <code>dydev-its-true.crt</code> → <strong>Install Certificate</strong>.</li>
|
||||
<li>Select <strong>Local Machine</strong> (requires Admin).</li>
|
||||
<li>Select "Place all certificates in the following store".</li>
|
||||
<li>Browse & select <strong>Trusted Root Certification Authorities</strong>.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<strong className="text-gray-800 dark:text-gray-200 block">For Intermediates:</strong>
|
||||
<ol className="list-decimal pl-4 space-y-1 marker:text-gray-400">
|
||||
<li>Double-click the <code>.crt</code> file.</li>
|
||||
<li>Follow the same steps but choose <strong>Intermediate Certification Authorities</strong> as the store.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 text-blue-800 dark:text-blue-200 p-3 rounded-md border border-blue-100 dark:border-blue-800 text-xs">
|
||||
<strong>Recommended: Auto-Installer Script (.bat)</strong>
|
||||
<ul className="list-disc pl-4 mt-1 space-y-0.5 opacity-90">
|
||||
<li>Download the <code>.bat</code> script from the table above.</li>
|
||||
<li><strong>Right-click</strong> the file and select <strong>"Run as Administrator"</strong>.</li>
|
||||
<li>The script will automatically install both Root and Intermediate CAs.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'mac' && (
|
||||
<div className="space-y-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
|
||||
<Monitor className="w-4 h-4 text-gray-500" />
|
||||
Installation Methods
|
||||
</h4>
|
||||
<div className="grid md:grid-cols-2 gap-6 text-sm text-gray-600 dark:text-gray-400">
|
||||
<div className="space-y-2">
|
||||
<strong className="text-gray-800 dark:text-gray-200 block">Method A: Config Profile (Recommended)</strong>
|
||||
<ol className="list-decimal pl-4 space-y-1 marker:text-gray-400">
|
||||
<li>Download the <code>.mobileconfig</code> file.</li>
|
||||
<li>Go to <strong>System Settings</strong> → <strong>Privacy & Security</strong>.</li>
|
||||
<li>Scroll down to <strong>Profiles</strong> and double-click the profile to install.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<strong className="text-gray-800 dark:text-gray-200 block">Method B: Keychain (Raw .crt)</strong>
|
||||
<ol className="list-decimal pl-4 space-y-1 marker:text-gray-400">
|
||||
<li>Open <strong>Keychain Access</strong>.</li>
|
||||
<li>Drag the <code>.crt</code> files into the <strong>System</strong> keychain.</li>
|
||||
<li>Double-click the Root CA → expand <strong>Trust</strong>.</li>
|
||||
<li>Set "When using this certificate" to <strong>Always Trust</strong>.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'ios' && (
|
||||
<div className="space-y-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
|
||||
<Smartphone className="w-4 h-4 text-gray-500" />
|
||||
Installing on iOS (iPhone/iPad)
|
||||
</h4>
|
||||
<div className="bg-amber-50 dark:bg-amber-900/20 p-3 rounded-md border border-amber-100 dark:border-amber-900/30 text-xs text-amber-900 dark:text-amber-100 mb-2">
|
||||
<strong>Important:</strong> Installing on iOS is a two-step process (Install Profile → Enable Trust).
|
||||
</div>
|
||||
<ol className="list-decimal pl-4 space-y-2 text-sm text-gray-600 dark:text-gray-400 marker:text-gray-400">
|
||||
<li>Tap <strong>Auto-Installer Script</strong> to download the configuration profile.</li>
|
||||
<li>Open <strong>Settings</strong>. Tap the <strong>"Profile Downloaded"</strong> banner at the top.</li>
|
||||
<li>Tap <strong>Install</strong> and enter your passcode.</li>
|
||||
<li><strong>Required Step:</strong> Go to <strong>Settings</strong> → <strong>General</strong> → <strong>About</strong> → <strong>Certificate Trust Settings</strong>.</li>
|
||||
<li>Under "Enable full trust for root certificates", toggle on the switch for <strong>"{root?.name || 'TrustLab Root CA'}"</strong>.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
|
||||
{/* Linux Tab */}
|
||||
{selectedIndex === 3 && (
|
||||
<div className="animate-in fade-in slide-in-from-bottom-2 duration-300">
|
||||
{renderTable('linux')}
|
||||
<div className="space-y-4 text-gray-800 dark:text-gray-200 text-sm mt-4 p-4 bg-gray-50 dark:bg-white/5 rounded-lg border dark:border-white/10">
|
||||
<h4 className="font-bold">Manual Installation (If not using One-Liner)</h4>
|
||||
<pre className="bg-gray-900 text-gray-100 p-3 rounded-md overflow-x-auto text-xs">
|
||||
<code>
|
||||
{`# Copy certificates
|
||||
{activeTab === 'android' && (
|
||||
<div className="space-y-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
|
||||
<Smartphone className="w-4 h-4 text-green-500" />
|
||||
Installing on Android
|
||||
</h4>
|
||||
<ol className="list-decimal pl-4 space-y-2 text-sm text-gray-600 dark:text-gray-400 marker:text-gray-400">
|
||||
<li>Download the <code>.crt</code> file (or <code>.der</code> if your specific Android version requires it).</li>
|
||||
<li>Go to **Settings** → **Security** → **Encryption & Credentials**.</li>
|
||||
<li>Tap **Install a certificate** → **CA Certificate**.</li>
|
||||
<li>Select "Install anyway" if prompted, then verify your identity (PIN/Fingerprint).</li>
|
||||
<li>Select the downloaded file.</li>
|
||||
</ol>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'linux' && (
|
||||
<div className="space-y-4">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
|
||||
<Terminal className="w-4 h-4 text-gray-500" />
|
||||
Manual CLI Installation
|
||||
</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
If you cannot use the one-liner, follow these standard steps (Debian/Ubuntu example):
|
||||
</p>
|
||||
<div className="bg-gray-100 dark:bg-neutral-900 p-4 rounded-md border border-gray-200 dark:border-neutral-800 overflow-x-auto">
|
||||
<pre className="text-xs font-mono text-gray-700 dark:text-gray-300">
|
||||
{`# 1. Copy certificates to local store
|
||||
sudo cp *.crt /usr/local/share/ca-certificates/
|
||||
|
||||
# Update Store
|
||||
# 2. Update the CA store
|
||||
sudo update-ca-certificates`}
|
||||
</code>
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
<p className="text-xs text-gray-400 italic">
|
||||
Note: For RHEL/CentOS, copy to <code>/etc/pki/ca-trust/source/anchors/</code> and run <code>update-ca-trust</code>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Sub-component for cleaner render logic
|
||||
function InstallerCell({ cert, os, handleCopy, copiedId }: { cert: CaCertificate, os: string, handleCopy: Function, copiedId: string | null }) {
|
||||
if (os === 'windows' && cert.bat_cdn_url) {
|
||||
return (
|
||||
<a href={cert.bat_cdn_url} className="inline-flex items-center gap-2 px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-900/30 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/40 transition-colors">
|
||||
<Terminal className="w-3.5 h-3.5" />
|
||||
Installer Script (.bat)
|
||||
</a>
|
||||
)
|
||||
}
|
||||
// Shared Logic for macOS AND iOS using the same mobileconfig
|
||||
if ((os === 'mac' || os === 'ios') && cert.mac_cdn_url) {
|
||||
return (
|
||||
<a href={cert.mac_cdn_url} className="inline-flex items-center gap-2 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-neutral-800 hover:bg-gray-200 dark:hover:bg-neutral-700 rounded-md transition-colors">
|
||||
<Smartphone className="w-3.5 h-3.5" />
|
||||
Config Profile
|
||||
</a>
|
||||
)
|
||||
}
|
||||
if (os === 'android' && cert.der_cdn_url) {
|
||||
return (
|
||||
<a href={cert.der_cdn_url} className="inline-flex items-center gap-2 px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/20 border border-green-100 dark:border-green-900/30 rounded-md hover:bg-green-100 dark:hover:bg-green-900/40 transition-colors">
|
||||
<Download className="w-3.5 h-3.5" />
|
||||
Download .der
|
||||
</a>
|
||||
)
|
||||
}
|
||||
if (os === 'linux' && cert.linux_cdn_url) {
|
||||
const cmd = `curl -sL ${cert.linux_cdn_url} | sudo bash`
|
||||
const isCopied = copiedId === cert.serial
|
||||
return (
|
||||
<div className="flex items-center gap-2 max-w-md group">
|
||||
<code className="flex-1 bg-gray-100 dark:bg-neutral-900 px-3 py-1.5 rounded-md text-[11px] font-mono text-gray-600 dark:text-gray-400 border border-gray-200 dark:border-neutral-800 truncate select-all">
|
||||
{cmd}
|
||||
</code>
|
||||
<button
|
||||
onClick={() => handleCopy(cmd, cert.serial)}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-md transition-colors"
|
||||
title="Copy command"
|
||||
>
|
||||
{isCopied ? <Check className="w-4 h-4 text-green-500" /> : <Copy className="w-4 h-4" />}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <span className="text-gray-400 text-xs italic">Not available</span>
|
||||
}
|
||||
|
||||
1117
package-lock.json
generated
@@ -21,6 +21,9 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/react": "^18.2.0",
|
||||
"autoprefixer": "^10.4.23",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
5
pages/_app.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import '../styles/globals.css'
|
||||
|
||||
export default function App({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
@@ -1,31 +1,52 @@
|
||||
import { Steps, Callout, Cards, Card } from 'nextra/components'
|
||||
import { FileBadge, ShieldCheck, Globe, Code } from 'lucide-react'
|
||||
|
||||
# Requesting a New Certificate
|
||||
|
||||
TrustLab allows you to request private SSL/TLS certificates for various internal uses.
|
||||
TrustLab provides a streamlined wizard to generate private SSL/TLS certificates for your internal infrastructure.
|
||||
|
||||
## Prerequisites
|
||||
- You must have an active TrustLab account.
|
||||
- You must have the **Root CA** installed on your machine.
|
||||
Before starting, ensure you have:
|
||||
* Active TrustLab account access.
|
||||
* **Root CA** installed on your machine (to trust the generated certs).
|
||||
|
||||
## Step-by-Step Guide
|
||||
---
|
||||
|
||||
1. **Log in to Dashboard**
|
||||
Navigate to the TrustLab Dashboard and login with your credentials.
|
||||
<Steps>
|
||||
### 1. Open Certificates Menu
|
||||
Navigate to the **Certificates** page. This view lists all your active and expired certificates. Click the **"Generate New"** (or "+") button to start.
|
||||
|
||||
2. **Navigate to "New Certificate"**
|
||||
Click on the **"New Request"** button in the top navigation bar or the main dashboard card.
|
||||

|
||||
|
||||
3. **Select Certificate Profile**
|
||||
Choose the profile that matches your need:
|
||||
- **Internal Web Server**: For HTTPS on internal tools (e.g., specific IP or `.local` domains).
|
||||
- **User / S/MIME**: For email signing and encryption.
|
||||
- **Code Signing**: For signing scripts and executables.
|
||||
### 2. Enter Domain Details (Default Mode)
|
||||
By default, you only need to provide the Identity. The system will auto-fill the Organization & Location metadata.
|
||||
|
||||
4. **Fill in Details**
|
||||
- **Common Name (CN)**: The primary domain name or IP address (e.g., `internal.app` or `192.168.1.50`).
|
||||
- **Subject Alternative Names (SANs)**: Additional domains or IPs (optional).
|
||||
- **Validity Period**: Choose between 90 days, 1 year, or custom (if allowed).
|
||||

|
||||
|
||||
* **Common Name (CN)**: The primary domain (e.g., `app.internal`).
|
||||
* **SANs**: Additional domains or IP addresses.
|
||||
* **Key Strength**: Choose the encryption level.
|
||||

|
||||
* **2048-bit**: Industry standard, compatible with all devices.
|
||||
* **4096-bit**: Higher security, slightly more CPU intensive.
|
||||
|
||||
### 3. Customize CSR (Manual Control)
|
||||
Toggle **"Manual Control"** if you need to override the default Identity fields (e.g., for a specific branch office or legal entity).
|
||||
|
||||

|
||||
|
||||
* **Organization (O)**: Override the default company name.
|
||||
* **Locality (L) / State (ST)**: Set specific location data.
|
||||
* **Country (C)**: ISO Code.
|
||||
|
||||
|
||||
### 4. Submit & Download
|
||||
Click **Generate**.
|
||||
* **Private Key**: The system will prompt you to download the `.key` file. **This is the only time it is available.**
|
||||
* **Certificate**: The `.pem` / `.crt` file will be available for download immediately.
|
||||
</Steps>
|
||||
|
||||
<Callout type="warning" emoji="⚠️">
|
||||
**Security:** Your **Private Key** is shown/downloaded **ONLY ONCE**. Store it securely immediately. If lost, you must revoke and reissue the certificate.
|
||||
</Callout>
|
||||
|
||||
5. **Submit Request**
|
||||
Click **"Submit"**. The system will process your request.
|
||||
- If **Auto-Approval** is enabled for your role, the certificate is issued immediately.
|
||||
- If **Manual Approval** is required, the status will be `PENDING` until a Manager approves it.
|
||||
|
||||
@@ -1,5 +1,96 @@
|
||||
import { Callout, Steps, Cards, Card } from 'nextra/components'
|
||||
import { Monitor, Smartphone, LayoutDashboard, Key, Shield } from 'lucide-react'
|
||||
|
||||
# Accessing Dashboard
|
||||
|
||||
1. Navigate to [https://trustlab.dyzulk.com](https://trustlab.dyzulk.com).
|
||||
2. Click "Login".
|
||||
3. Use your SSO credentials or Magic Link.
|
||||
The **TrustLab Dashboard** is your central command center for managing certificates. Here you can request new certificates, download keys, and manage existing ones.
|
||||
|
||||

|
||||
|
||||
## Authentication Methods
|
||||
|
||||
We prioritize security by offering modern, passwordless authentication options.
|
||||
|
||||
<Cards>
|
||||
<Card icon={<Shield className="w-6 h-6" />} title="SSO (Google / GitHub)" href="#1-single-sign-on-sso" arrow />
|
||||
<Card icon={<Key className="w-6 h-6" />} title="Magic Link (Email)" href="#2-magic-link" arrow />
|
||||
</Cards>
|
||||
|
||||
### 1. Single Sign-On (SSO)
|
||||
The fastest way to log in. Click **Continue with Google** or **Continue with GitHub**.
|
||||
|
||||
<Callout type="info" emoji="ℹ️">
|
||||
**SSO Behavior:**
|
||||
* **Existing Users:** You can only Log In via SSO if your social email address is already registered/linked to your account.
|
||||
* **New Users:** You can **Register** a new account instantly by clicking the Social Login buttons on the *Sign In* or *Register* page.
|
||||
</Callout>
|
||||
|
||||
### 2. Magic Link
|
||||
Secure, passwordless login via email.
|
||||
|
||||
<Steps>
|
||||
### Enter Email
|
||||
Input your registered email address in the login form and click **"Continue with Email"**.
|
||||
|
||||
### Check Inbox
|
||||
You will receive an email with a unique, time-sensitive login link.
|
||||
|
||||
### Click to Verify
|
||||
Click the **"Sign in to TrustLab"** button in the email. You will be instantly logged in to the dashboard.
|
||||
</Steps>
|
||||
|
||||
<Callout type="warning" emoji="⏳">
|
||||
**Expiration:** Magic links are valid for **15 minutes** only. If it expires, simply request a new one by entering your email again.
|
||||
</Callout>
|
||||
|
||||
## Dashboard Overview
|
||||
|
||||
Upon successful login, you will land on the main dashboard.
|
||||
|
||||

|
||||
|
||||
This central hub allows you to access:
|
||||
* **Active Certificates**: View all valid certificates issued to you.
|
||||
* **Request Certificate**: The wizard to generate a new Private Key and CSR.
|
||||
* **Revocation**: Interface to mark lost or compromised certificates as invalid.
|
||||
|
||||
## Registration
|
||||
|
||||
New users can create an account to start managing certificates.
|
||||
|
||||

|
||||
|
||||
<Steps>
|
||||
### Option A: Social Registration (Instant)
|
||||
1. Click **Continue with Google** or **Continue with GitHub**.
|
||||
2. **Set Password:** For security, you will be asked to create a password for your account.
|
||||
|
||||

|
||||
|
||||
### Option B: Email Registration
|
||||
1. Click **"Sign up"** or fill the form.
|
||||
2. Provide your details and password.
|
||||
3. **Verify Email:** Click the link sent to your inbox to activate.
|
||||
</Steps>
|
||||
|
||||
## Password Recovery
|
||||
|
||||
If you lose access to your account, you can reset your password securely.
|
||||
|
||||

|
||||
|
||||
1. Click **"Forgot password?"** on the login screen.
|
||||
2. Enter your registered email address.
|
||||
3. Check your inbox for a password reset link.
|
||||
4. Create a new password and log in.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### I didn't receive the Magic Link
|
||||
* **Check Spam/Junk**: It often lands there for corporate domains.
|
||||
* **Wait 1-2 Minutes**: Email delivery can sometimes be delayed.
|
||||
* **Whitelist Sender**: Add `@trustlab.dyzulk.com` to your email provider's **Safe Senders** list to prevent it from being blocked.
|
||||
|
||||
### Access Denied / User Not Found
|
||||
* **Typos**: Double-check your email address.
|
||||
* **Not Registered**: If you haven't created an account yet, please **Sign Up** first. You cannot log in via SSO if your email is not in our system (unless you use the Register flow).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Tabs } from 'nextra/components'
|
||||
import { Steps } from 'nextra/components'
|
||||
import { Tabs, Steps, Cards, Card, Callout } from 'nextra/components'
|
||||
import { Monitor, Smartphone } from 'lucide-react'
|
||||
|
||||
# Installing Root CA
|
||||
|
||||
@@ -36,9 +36,20 @@ Select your distribution to get the optimized installation command:
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
|
||||
### Windows / macOS
|
||||
* **Windows**: [Download Bundle Installer (.bat)](https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.bat) (Right-click > Run as Admin)
|
||||
* **macOS/iOS**: [Download Configuration Profile (.mobileconfig)](https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.mobileconfig)
|
||||
### Windows & Apple Ecosystem
|
||||
|
||||
<Cards>
|
||||
<Card icon={<Monitor className="w-6 h-6" />} title="Windows Bundle Installer (.bat)" href="https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.bat" arrow />
|
||||
<Card icon={<Smartphone className="w-6 h-6" />} title="macOS / iOS Profile (.mobileconfig)" href="https://cdn.trustlab.dyzulk.com/ca/bundles/trustlab-all.mobileconfig" arrow />
|
||||
</Cards>
|
||||
|
||||
<Callout type="warning" emoji="⚠️">
|
||||
**Windows Users:** You **MUST** right-click the `.bat` file and select **"Run as Administrator"**. Double-clicking directly will likely fail due to permission restrictions.
|
||||
</Callout>
|
||||
|
||||
<Callout type="info" emoji="ℹ️">
|
||||
**Apple Users:** After downloading the profile, go to **System Settings > Privacy & Security > Profiles** to install it. For iOS, see the *Individual Installation* section below for detailed trust steps.
|
||||
</Callout>
|
||||
|
||||
---
|
||||
|
||||
|
||||
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 103 KiB |
BIN
public/images/guide/certificates-screen.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
public/images/guide/dashboard-screen.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/images/guide/forgot-password-screen.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
public/images/guide/login-screen.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
public/images/guide/register-screen.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
public/images/guide/set-password-after-social-screen.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
public/logo-dark.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/logo-icon.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/logo.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
1
response.json
Normal file
@@ -0,0 +1 @@
|
||||
{"success":true,"data":[{"name":"DyDev Its True","type":"root","serial":"66:5A:19:86:5D:DC:06:10","family_id":"fbdf8aee-8a6d-422b-ac21-51413338fec7","expires_at":"2046-01-03T12:36:57+07:00","last_synced_at":"2026-01-08T12:38:03+07:00","cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/dydev-its-true.crt","der_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/dydev-its-true.der","bat_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-dydev-its-true.bat","mac_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-dydev-its-true.mobileconfig","linux_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-dydev-its-true.sh"},{"name":"TrustLab Intermediate CA 4096","type":"intermediate_4096","serial":"71:4D:87:E4:9F:EE:25:0A","family_id":"fbdf8aee-8a6d-422b-ac21-51413338fec7","expires_at":"2036-01-06T12:37:03+07:00","last_synced_at":"2026-01-08T12:38:06+07:00","cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/trustlab-intermediate-ca-4096.crt","der_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/trustlab-intermediate-ca-4096.der","bat_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-trustlab-intermediate-ca-4096.bat","mac_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-trustlab-intermediate-ca-4096.mobileconfig","linux_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-trustlab-intermediate-ca-4096.sh"},{"name":"TrustLab Intermediate CA 2048","type":"intermediate_2048","serial":"6B:35:34:91:69:3D:FF:FD","family_id":"fbdf8aee-8a6d-422b-ac21-51413338fec7","expires_at":"2036-01-06T12:37:06+07:00","last_synced_at":"2026-01-08T12:38:08+07:00","cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/trustlab-intermediate-ca-2048.crt","der_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/trustlab-intermediate-ca-2048.der","bat_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-trustlab-intermediate-ca-2048.bat","mac_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-trustlab-intermediate-ca-2048.mobileconfig","linux_cdn_url":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/installers\/trustlab-trustlab-intermediate-ca-2048.sh"}],"bundle_urls":{"linux":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/bundles\/trustlab-all.sh","windows":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/bundles\/trustlab-all.bat","macos":"https:\/\/cdn.trustlab.dyzulk.com\/ca\/bundles\/trustlab-all.mobileconfig"}}
|
||||
3
styles/globals.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
13
tailwind.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./pages/**/*.{js,jsx,ts,tsx,md,mdx}',
|
||||
'./components/**/*.{js,jsx,ts,tsx}',
|
||||
'./theme.config.tsx'
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
darkMode: 'class'
|
||||
}
|
||||
@@ -2,11 +2,23 @@ import React from 'react'
|
||||
import { DocsThemeConfig } from 'nextra-theme-docs'
|
||||
|
||||
const config: DocsThemeConfig = {
|
||||
logo: <span>TrustLab Docs</span>,
|
||||
logo: (
|
||||
<div className="flex items-center gap-2">
|
||||
<img src="/logo.png" alt="TrustLab" width="120" className="dark:hidden" />
|
||||
<img src="/logo-dark.png" alt="TrustLab" width="120" className="hidden dark:block" />
|
||||
<span className="font-semibold text-lg hidden sm:inline">Docs</span>
|
||||
</div>
|
||||
),
|
||||
logoLink: '/',
|
||||
project: {
|
||||
link: 'https://github.com/dyzulk/trustlab-docs',
|
||||
},
|
||||
docsRepositoryBase: 'https://github.com/dyzulk/trustlab-docs/tree/main',
|
||||
head: (
|
||||
<>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</>
|
||||
),
|
||||
footer: {
|
||||
text: 'TrustLab Documentation',
|
||||
},
|
||||
|
||||