feat(admin): Enhance Turnstile configuration UI and add GitHub commit links
- Move Turnstile status indicator above input fields for better visibility - Mask secret key input with placeholder dots when already configured - Update secret key helper text to indicate configuration status - Enhance version API endpoint to include GitHub commit URL - Add githubUrl field to version response for direct commit linking - Implement emoji sizing styles for post content and compose input - Update RightSidebar version state type to include githubUrl - Add clickable GitHub commit link in version display when available - Improve security by hiding sensitive Turnstile secret key values
This commit is contained in:
+15
-14
@@ -474,6 +474,19 @@ export default function AdminPage() {
|
|||||||
Cloudflare Dashboard
|
Cloudflare Dashboard
|
||||||
</a>.
|
</a>.
|
||||||
</p>
|
</p>
|
||||||
|
{nodeSettings.turnstileSiteKey && nodeSettings.turnstileSecretKey && (
|
||||||
|
<div style={{
|
||||||
|
padding: '8px 12px',
|
||||||
|
background: 'rgba(34, 197, 94, 0.1)',
|
||||||
|
border: '1px solid rgba(34, 197, 94, 0.3)',
|
||||||
|
borderRadius: '6px',
|
||||||
|
fontSize: '12px',
|
||||||
|
color: 'rgb(34, 197, 94)',
|
||||||
|
marginBottom: '12px',
|
||||||
|
}}>
|
||||||
|
✓ Turnstile is enabled and will be shown on login/registration
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'grid', gap: '12px' }}>
|
<div style={{ display: 'grid', gap: '12px' }}>
|
||||||
<div>
|
<div>
|
||||||
@@ -501,25 +514,13 @@ export default function AdminPage() {
|
|||||||
type="password"
|
type="password"
|
||||||
value={nodeSettings.turnstileSecretKey}
|
value={nodeSettings.turnstileSecretKey}
|
||||||
onChange={e => setNodeSettings({ ...nodeSettings, turnstileSecretKey: e.target.value })}
|
onChange={e => setNodeSettings({ ...nodeSettings, turnstileSecretKey: e.target.value })}
|
||||||
placeholder="0x4AAAAAAA..."
|
placeholder={nodeSettings.turnstileSecretKey ? '••••••••••••••••' : '0x4AAAAAAA...'}
|
||||||
style={{ fontFamily: 'monospace', fontSize: '13px' }}
|
style={{ fontFamily: 'monospace', fontSize: '13px' }}
|
||||||
/>
|
/>
|
||||||
<p style={{ fontSize: '11px', color: 'var(--foreground-tertiary)', marginTop: '4px' }}>
|
<p style={{ fontSize: '11px', color: 'var(--foreground-tertiary)', marginTop: '4px' }}>
|
||||||
Secret key for server-side verification
|
{nodeSettings.turnstileSecretKey ? 'Secret key is configured (hidden for security)' : 'Secret key for server-side verification'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{nodeSettings.turnstileSiteKey && nodeSettings.turnstileSecretKey && (
|
|
||||||
<div style={{
|
|
||||||
padding: '8px 12px',
|
|
||||||
background: 'rgba(34, 197, 94, 0.1)',
|
|
||||||
border: '1px solid rgba(34, 197, 94, 0.3)',
|
|
||||||
borderRadius: '6px',
|
|
||||||
fontSize: '12px',
|
|
||||||
color: 'rgb(34, 197, 94)',
|
|
||||||
}}>
|
|
||||||
✓ Turnstile is enabled and will be shown on login/registration
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,26 +9,51 @@ export async function GET() {
|
|||||||
stdio: ['pipe', 'pipe', 'ignore'] // Ignore stderr
|
stdio: ['pipe', 'pipe', 'ignore'] // Ignore stderr
|
||||||
}).trim();
|
}).trim();
|
||||||
|
|
||||||
// Also get the short hash for reference
|
// Get the short hash for reference
|
||||||
const commitHash = execSync('git rev-parse --short HEAD', {
|
const commitHash = execSync('git rev-parse --short HEAD', {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
stdio: ['pipe', 'pipe', 'ignore']
|
stdio: ['pipe', 'pipe', 'ignore']
|
||||||
}).trim();
|
}).trim();
|
||||||
|
|
||||||
|
const fullHash = execSync('git rev-parse HEAD', {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
stdio: ['pipe', 'pipe', 'ignore']
|
||||||
|
}).trim();
|
||||||
|
|
||||||
|
// Try to get the GitHub repo URL
|
||||||
|
let githubUrl = null;
|
||||||
|
try {
|
||||||
|
const remoteUrl = execSync('git config --get remote.origin.url', {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
stdio: ['pipe', 'pipe', 'ignore']
|
||||||
|
}).trim();
|
||||||
|
|
||||||
|
// Convert git URL to GitHub web URL
|
||||||
|
// Handle both SSH (git@github.com:user/repo.git) and HTTPS (https://github.com/user/repo.git)
|
||||||
|
if (remoteUrl.includes('github.com')) {
|
||||||
|
let cleanUrl = remoteUrl
|
||||||
|
.replace('git@github.com:', 'https://github.com/')
|
||||||
|
.replace(/\.git$/, '');
|
||||||
|
|
||||||
|
githubUrl = `${cleanUrl}/commit/${fullHash}`;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// If we can't get the remote URL, that's okay
|
||||||
|
}
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
count: parseInt(commitCount, 10),
|
count: parseInt(commitCount, 10),
|
||||||
hash: commitHash,
|
hash: commitHash,
|
||||||
fullHash: execSync('git rev-parse HEAD', {
|
fullHash: fullHash,
|
||||||
encoding: 'utf-8',
|
githubUrl: githubUrl
|
||||||
stdio: ['pipe', 'pipe', 'ignore']
|
|
||||||
}).trim()
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If git is not available or not a git repo, return unknown
|
// If git is not available or not a git repo, return unknown
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
count: null,
|
count: null,
|
||||||
hash: 'unknown',
|
hash: 'unknown',
|
||||||
fullHash: null
|
fullHash: null,
|
||||||
|
githubUrl: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-1
@@ -1797,4 +1797,15 @@ a.btn-primary:visited {
|
|||||||
.thread-line {
|
.thread-line {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
border-left: 1px solid var(--border);
|
border-left: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Big Emojis! */
|
||||||
|
.post-content img.emoji,
|
||||||
|
.compose-input img.emoji {
|
||||||
|
display: inline;
|
||||||
|
height: 2.5em;
|
||||||
|
width: 2.5em;
|
||||||
|
margin: 0 0.05em 0 0.1em;
|
||||||
|
vertical-align: -0.5em;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function RightSidebar() {
|
|||||||
bannerUrl: '',
|
bannerUrl: '',
|
||||||
admins: [] as Admin[],
|
admins: [] as Admin[],
|
||||||
});
|
});
|
||||||
const [version, setVersion] = useState<{ count: number | null; hash: string; fullHash: string | null } | null>(null);
|
const [version, setVersion] = useState<{ count: number | null; hash: string; fullHash: string | null; githubUrl: string | null } | null>(null);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ export function RightSidebar() {
|
|||||||
fetch('/api/version')
|
fetch('/api/version')
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => setVersion(data))
|
.then(data => setVersion(data))
|
||||||
.catch(() => setVersion({ count: null, hash: 'unknown', fullHash: null }));
|
.catch(() => setVersion({ count: null, hash: 'unknown', fullHash: null, githubUrl: null }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -119,9 +119,21 @@ export function RightSidebar() {
|
|||||||
{version && version.count !== null && (
|
{version && version.count !== null && (
|
||||||
<>
|
<>
|
||||||
{' • '}
|
{' • '}
|
||||||
<span title={`${version.hash} (${version.fullHash})`}>
|
{version.githubUrl ? (
|
||||||
Commit {version.count}
|
<a
|
||||||
</span>
|
href={version.githubUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
title={`${version.hash} (${version.fullHash})`}
|
||||||
|
style={{ color: 'var(--accent)' }}
|
||||||
|
>
|
||||||
|
Commit {version.count}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span title={`${version.hash} (${version.fullHash})`}>
|
||||||
|
Commit {version.count}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user