Files
i-tools/app/bcrypt/page.tsx
yfan 3d175d75af
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Sync to CNB / sync (push) Has been cancelled
Delete old workflow runs / del_runs (push) Has been cancelled
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled
first commit
2026-01-30 16:57:44 +08:00

173 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useState } from "react";
import bcrypt from "bcryptjs";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Lock, CheckCircle, XCircle, Copy, Hash } from "lucide-react";
import { toast } from "sonner";
import { Slider } from "@/components/ui/slider";
export default function BcryptPage() {
const [plainText, setPlainText] = useState("");
const [saltRounds, setSaltRounds] = useState(10);
const [hashResult, setHashResult] = useState("");
const [verifyText, setVerifyText] = useState("");
const [verifyHash, setVerifyHash] = useState("");
const [isMatch, setIsMatch] = useState<boolean | null>(null);
const handleGenerate = async () => {
if (!plainText) return;
try {
const hash = await bcrypt.hash(plainText, saltRounds);
setHashResult(hash);
toast.success("哈希生成成功");
} catch {
toast.error("生成失败");
}
};
const handleVerify = async () => {
if (!verifyText || !verifyHash) return;
try {
const match = await bcrypt.compare(verifyText, verifyHash);
setIsMatch(match);
if (match) {
toast.success("验证成功:匹配");
} else {
toast.error("验证失败:不匹配");
}
} catch {
setIsMatch(false);
toast.error("验证过程出错,请检查哈希格式");
}
};
const copyToClipboard = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
toast.success("已复制到剪贴板");
} catch {
toast.error("复制失败");
}
};
return (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="flex items-center space-x-4 border-b pb-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-linear-to-br from-slate-600 to-zinc-700 shadow-lg">
<Lock className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-2xl font-bold tracking-tight">Bcrypt </h1>
<p className="text-muted-foreground"> Bcrypt </p>
</div>
</div>
<div className="grid gap-6 lg:grid-cols-2">
{/* Generate Card */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Hash className="h-5 w-5 text-primary" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label></Label>
<Input
value={plainText}
onChange={(e) => setPlainText(e.target.value)}
placeholder="请输入要加密的文本"
/>
</div>
<div className="space-y-4">
<div className="flex justify-between">
<Label>Salt Rounds (): {saltRounds}</Label>
<span className="text-xs text-muted-foreground"></span>
</div>
<Slider
value={[saltRounds]}
onValueChange={(v) => setSaltRounds(v[0])}
min={4}
max={16}
step={1}
/>
</div>
<Button onClick={handleGenerate} disabled={!plainText} className="w-full">
</Button>
{hashResult && (
<div className="mt-4 p-3 bg-muted rounded-md break-all font-mono text-sm relative group">
{hashResult}
<Button
variant="ghost"
size="icon"
className="absolute top-1 right-1 h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity"
onClick={() => copyToClipboard(hashResult)}
>
<Copy className="h-3 w-3" />
</Button>
</div>
)}
</CardContent>
</Card>
{/* Verify Card */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-emerald-500" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label></Label>
<Input
value={verifyText}
onChange={(e) => setVerifyText(e.target.value)}
placeholder="请输入明文"
/>
</div>
<div className="space-y-2">
<Label>Bcrypt </Label>
<Input
value={verifyHash}
onChange={(e) => setVerifyHash(e.target.value)}
placeholder="$2a$10$..."
/>
</div>
<Button onClick={handleVerify} disabled={!verifyText || !verifyHash} variant="secondary" className="w-full">
</Button>
{isMatch !== null && (
<div className={`mt-4 p-4 rounded-md flex items-center justify-center gap-2 font-bold ${isMatch ? 'bg-emerald-500/10 text-emerald-600' : 'bg-destructive/10 text-destructive'}`}>
{isMatch ? (
<>
<CheckCircle className="h-5 w-5" />
</>
) : (
<>
<XCircle className="h-5 w-5" />
</>
)}
</div>
)}
</CardContent>
</Card>
</div>
</div>
);
}