mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 14:22:28 +08:00
feat: verify access code with server before saving (#128)
This commit is contained in:
32
app/api/verify-access-code/route.ts
Normal file
32
app/api/verify-access-code/route.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
export async function POST(req: Request) {
|
||||||
|
const accessCodes =
|
||||||
|
process.env.ACCESS_CODE_LIST?.split(",")
|
||||||
|
.map((code) => code.trim())
|
||||||
|
.filter(Boolean) || []
|
||||||
|
|
||||||
|
// If no access codes configured, verification always passes
|
||||||
|
if (accessCodes.length === 0) {
|
||||||
|
return Response.json({
|
||||||
|
valid: true,
|
||||||
|
message: "No access code required",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessCodeHeader = req.headers.get("x-access-code")
|
||||||
|
|
||||||
|
if (!accessCodeHeader) {
|
||||||
|
return Response.json(
|
||||||
|
{ valid: false, message: "Access code is required" },
|
||||||
|
{ status: 401 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accessCodes.includes(accessCodeHeader)) {
|
||||||
|
return Response.json(
|
||||||
|
{ valid: false, message: "Invalid access code" },
|
||||||
|
{ status: 401 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({ valid: true, message: "Access code is valid" })
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ export function SettingsDialog({
|
|||||||
}: SettingsDialogProps) {
|
}: SettingsDialogProps) {
|
||||||
const [accessCode, setAccessCode] = useState("")
|
const [accessCode, setAccessCode] = useState("")
|
||||||
const [closeProtection, setCloseProtection] = useState(true)
|
const [closeProtection, setCloseProtection] = useState(true)
|
||||||
|
const [isVerifying, setIsVerifying] = useState(false)
|
||||||
|
const [error, setError] = useState("")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
@@ -42,17 +44,44 @@ export function SettingsDialog({
|
|||||||
)
|
)
|
||||||
// Default to true if not set
|
// Default to true if not set
|
||||||
setCloseProtection(storedCloseProtection !== "false")
|
setCloseProtection(storedCloseProtection !== "false")
|
||||||
|
setError("")
|
||||||
}
|
}
|
||||||
}, [open])
|
}, [open])
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = async () => {
|
||||||
localStorage.setItem(STORAGE_ACCESS_CODE_KEY, accessCode.trim())
|
setError("")
|
||||||
localStorage.setItem(
|
setIsVerifying(true)
|
||||||
STORAGE_CLOSE_PROTECTION_KEY,
|
|
||||||
closeProtection.toString(),
|
try {
|
||||||
)
|
// Verify access code with server
|
||||||
onCloseProtectionChange?.(closeProtection)
|
const response = await fetch("/api/verify-access-code", {
|
||||||
onOpenChange(false)
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"x-access-code": accessCode.trim(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
if (!data.valid) {
|
||||||
|
setError(data.message || "Invalid access code")
|
||||||
|
setIsVerifying(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save settings only if verification passes
|
||||||
|
localStorage.setItem(STORAGE_ACCESS_CODE_KEY, accessCode.trim())
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_CLOSE_PROTECTION_KEY,
|
||||||
|
closeProtection.toString(),
|
||||||
|
)
|
||||||
|
onCloseProtectionChange?.(closeProtection)
|
||||||
|
onOpenChange(false)
|
||||||
|
} catch {
|
||||||
|
setError("Failed to verify access code")
|
||||||
|
} finally {
|
||||||
|
setIsVerifying(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
@@ -87,6 +116,11 @@ export function SettingsDialog({
|
|||||||
<p className="text-[0.8rem] text-muted-foreground">
|
<p className="text-[0.8rem] text-muted-foreground">
|
||||||
Required if the server has enabled access control.
|
Required if the server has enabled access control.
|
||||||
</p>
|
</p>
|
||||||
|
{error && (
|
||||||
|
<p className="text-[0.8rem] text-destructive">
|
||||||
|
{error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</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">
|
||||||
@@ -111,7 +145,9 @@ export function SettingsDialog({
|
|||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSave}>Save</Button>
|
<Button onClick={handleSave} disabled={isVerifying}>
|
||||||
|
{isVerifying ? "Verifying..." : "Save"}
|
||||||
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
Reference in New Issue
Block a user