feat: only show access code field when password is configured (#161)

Co-authored-by: dayuan.jiang <jiangdy@amazon.co.jp>
This commit is contained in:
Dayuan Jiang
2025-12-08 11:00:14 +09:00
committed by GitHub
parent ecea8a6005
commit c4aaa7c915

View File

@@ -6,7 +6,6 @@ import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@/components/ui/dialog" } from "@/components/ui/dialog"
@@ -22,6 +21,14 @@ interface SettingsDialogProps {
export const STORAGE_ACCESS_CODE_KEY = "next-ai-draw-io-access-code" export const STORAGE_ACCESS_CODE_KEY = "next-ai-draw-io-access-code"
export const STORAGE_CLOSE_PROTECTION_KEY = "next-ai-draw-io-close-protection" export const STORAGE_CLOSE_PROTECTION_KEY = "next-ai-draw-io-close-protection"
const STORAGE_ACCESS_CODE_REQUIRED_KEY = "next-ai-draw-io-access-code-required"
function getStoredAccessCodeRequired(): boolean | null {
if (typeof window === "undefined") return null
const stored = localStorage.getItem(STORAGE_ACCESS_CODE_REQUIRED_KEY)
if (stored === null) return null
return stored === "true"
}
export function SettingsDialog({ export function SettingsDialog({
open, open,
@@ -32,6 +39,32 @@ export function SettingsDialog({
const [closeProtection, setCloseProtection] = useState(true) const [closeProtection, setCloseProtection] = useState(true)
const [isVerifying, setIsVerifying] = useState(false) const [isVerifying, setIsVerifying] = useState(false)
const [error, setError] = useState("") const [error, setError] = useState("")
const [accessCodeRequired, setAccessCodeRequired] = useState(
() => getStoredAccessCodeRequired() ?? false,
)
useEffect(() => {
// Only fetch if not cached in localStorage
if (getStoredAccessCodeRequired() !== null) return
fetch("/api/config")
.then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json()
})
.then((data) => {
const required = data?.accessCodeRequired === true
localStorage.setItem(
STORAGE_ACCESS_CODE_REQUIRED_KEY,
String(required),
)
setAccessCodeRequired(required)
})
.catch(() => {
// Don't cache on error - allow retry on next mount
setAccessCodeRequired(false)
})
}, [])
useEffect(() => { useEffect(() => {
if (open) { if (open) {
@@ -49,11 +82,12 @@ export function SettingsDialog({
}, [open]) }, [open])
const handleSave = async () => { const handleSave = async () => {
if (!accessCodeRequired) return
setError("") setError("")
setIsVerifying(true) setIsVerifying(true)
try { try {
// Verify access code with server
const response = await fetch("/api/verify-access-code", { const response = await fetch("/api/verify-access-code", {
method: "POST", method: "POST",
headers: { headers: {
@@ -65,17 +99,10 @@ export function SettingsDialog({
if (!data.valid) { if (!data.valid) {
setError(data.message || "Invalid access code") setError(data.message || "Invalid access code")
setIsVerifying(false)
return return
} }
// Save settings only if verification passes
localStorage.setItem(STORAGE_ACCESS_CODE_KEY, accessCode.trim()) localStorage.setItem(STORAGE_ACCESS_CODE_KEY, accessCode.trim())
localStorage.setItem(
STORAGE_CLOSE_PROTECTION_KEY,
closeProtection.toString(),
)
onCloseProtectionChange?.(closeProtection)
onOpenChange(false) onOpenChange(false)
} catch { } catch {
setError("Failed to verify access code") setError("Failed to verify access code")
@@ -97,31 +124,42 @@ export function SettingsDialog({
<DialogHeader> <DialogHeader>
<DialogTitle>Settings</DialogTitle> <DialogTitle>Settings</DialogTitle>
<DialogDescription> <DialogDescription>
Configure your access settings. Configure your application settings.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-2"> <div className="space-y-4 py-2">
<div className="space-y-2"> {accessCodeRequired && (
<label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"> <div className="space-y-2">
Access Code <Label htmlFor="access-code">Access Code</Label>
</label> <div className="flex gap-2">
<Input <Input
type="password" id="access-code"
value={accessCode} type="password"
onChange={(e) => setAccessCode(e.target.value)} value={accessCode}
onKeyDown={handleKeyDown} onChange={(e) =>
placeholder="Enter access code" setAccessCode(e.target.value)
autoComplete="off" }
/> onKeyDown={handleKeyDown}
<p className="text-[0.8rem] text-muted-foreground"> placeholder="Enter access code"
Required if the server has enabled access control. autoComplete="off"
</p> />
{error && ( <Button
<p className="text-[0.8rem] text-destructive"> onClick={handleSave}
{error} disabled={isVerifying || !accessCode.trim()}
>
{isVerifying ? "..." : "Save"}
</Button>
</div>
<p className="text-[0.8rem] text-muted-foreground">
Required to use this application.
</p> </p>
)} {error && (
</div> <p className="text-[0.8rem] text-destructive">
{error}
</p>
)}
</div>
)}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="space-y-0.5"> <div className="space-y-0.5">
<Label htmlFor="close-protection"> <Label htmlFor="close-protection">
@@ -134,21 +172,17 @@ export function SettingsDialog({
<Switch <Switch
id="close-protection" id="close-protection"
checked={closeProtection} checked={closeProtection}
onCheckedChange={setCloseProtection} onCheckedChange={(checked) => {
setCloseProtection(checked)
localStorage.setItem(
STORAGE_CLOSE_PROTECTION_KEY,
checked.toString(),
)
onCloseProtectionChange?.(checked)
}}
/> />
</div> </div>
</div> </div>
<DialogFooter>
<Button
variant="outline"
onClick={() => onOpenChange(false)}
>
Cancel
</Button>
<Button onClick={handleSave} disabled={isVerifying}>
{isVerifying ? "Verifying..." : "Save"}
</Button>
</DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
) )