mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-06 18:31:00 +00:00
Recovery option, fix profile card menu placement
This commit is contained in:
parent
cace835c84
commit
417159aad9
@ -48,7 +48,7 @@ const UserProfileCard = ({ user }) => {
|
|||||||
const MobileProfileCard = () => (
|
const MobileProfileCard = () => (
|
||||||
<div className="w-full bg-gray-800 rounded-lg p-2 py-1 border border-gray-700 shadow-md h-[420px] flex flex-col justify-center items-start">
|
<div className="w-full bg-gray-800 rounded-lg p-2 py-1 border border-gray-700 shadow-md h-[420px] flex flex-col justify-center items-start">
|
||||||
<div className="flex flex-col gap-2 pt-4 w-full relative">
|
<div className="flex flex-col gap-2 pt-4 w-full relative">
|
||||||
<div className="absolute top-8 right-[10px]">
|
<div className="absolute top-8 right-[14px]">
|
||||||
<i
|
<i
|
||||||
className="pi pi-ellipsis-h text-2xl cursor-pointer"
|
className="pi pi-ellipsis-h text-2xl cursor-pointer"
|
||||||
onClick={(e) => menu.current.toggle(e)}
|
onClick={(e) => menu.current.toggle(e)}
|
||||||
@ -145,7 +145,7 @@ const UserProfileCard = ({ user }) => {
|
|||||||
className="rounded-full my-4"
|
className="rounded-full my-4"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-2 pt-4 w-fit relative">
|
<div className="flex flex-col gap-2 pt-4 w-fit relative">
|
||||||
<div className="absolute top-[-4px] right-[-30px]">
|
<div className="absolute top-[-1px] right-[-24px]">
|
||||||
<i
|
<i
|
||||||
className="pi pi-ellipsis-h text-2xl cursor-pointer"
|
className="pi pi-ellipsis-h text-2xl cursor-pointer"
|
||||||
onClick={(e) => menu.current.toggle(e)}
|
onClick={(e) => menu.current.toggle(e)}
|
||||||
|
@ -12,6 +12,7 @@ import { updateUser, getUserByPubkey, createUser, getUserById, getUserByEmail }
|
|||||||
import { createRole } from "@/db/models/roleModels";
|
import { createRole } from "@/db/models/roleModels";
|
||||||
import appConfig from "@/config/appConfig";
|
import appConfig from "@/config/appConfig";
|
||||||
import NDK from "@nostr-dev-kit/ndk";
|
import NDK from "@nostr-dev-kit/ndk";
|
||||||
|
import { nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
// Initialize NDK for Nostr interactions
|
// Initialize NDK for Nostr interactions
|
||||||
const ndk = new NDK({
|
const ndk = new NDK({
|
||||||
@ -162,6 +163,55 @@ export const authOptions = {
|
|||||||
avatar: profile.avatar_url
|
avatar: profile.avatar_url
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
// Recovery provider
|
||||||
|
CredentialsProvider({
|
||||||
|
id: "recovery",
|
||||||
|
name: "Recovery",
|
||||||
|
credentials: {
|
||||||
|
nsec: { label: "Recovery Key (nsec or hex)", type: "text" }
|
||||||
|
},
|
||||||
|
authorize: async (credentials) => {
|
||||||
|
if (!credentials?.nsec) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Convert nsec to hex if needed
|
||||||
|
let privkeyHex = credentials.nsec;
|
||||||
|
if (credentials.nsec.startsWith('nsec')) {
|
||||||
|
try {
|
||||||
|
const { data: decodedPrivkey } = nip19.decode(credentials.nsec);
|
||||||
|
privkeyHex = Buffer.from(decodedPrivkey).toString('hex');
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Invalid nsec format:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find user with matching privkey
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: { privkey: privkeyHex },
|
||||||
|
include: {
|
||||||
|
role: true,
|
||||||
|
purchased: true,
|
||||||
|
userCourses: true,
|
||||||
|
userLessons: true,
|
||||||
|
nip05: true,
|
||||||
|
lightningAddress: true,
|
||||||
|
userBadges: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
console.error("No user found with provided recovery key");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Recovery authorization error:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
callbacks: {
|
callbacks: {
|
||||||
|
@ -7,7 +7,9 @@ import { InputText } from 'primereact/inputtext';
|
|||||||
|
|
||||||
export default function SignIn() {
|
export default function SignIn() {
|
||||||
const [email, setEmail] = useState("")
|
const [email, setEmail] = useState("")
|
||||||
|
const [nsec, setNsec] = useState("")
|
||||||
const [showEmailInput, setShowEmailInput] = useState(false)
|
const [showEmailInput, setShowEmailInput] = useState(false)
|
||||||
|
const [showRecoveryInput, setShowRecoveryInput] = useState(false)
|
||||||
const {ndk, addSigner} = useNDKContext();
|
const {ndk, addSigner} = useNDKContext();
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -68,6 +70,25 @@ export default function SignIn() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRecoverySignIn = async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
try {
|
||||||
|
const result = await signIn("recovery", {
|
||||||
|
nsec,
|
||||||
|
redirect: false,
|
||||||
|
callbackUrl: '/'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result?.ok) {
|
||||||
|
router.push('/');
|
||||||
|
} else {
|
||||||
|
console.error("Recovery login failed:", result?.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Recovery sign in error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Redirect if already signed in
|
// Redirect if already signed in
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
@ -124,6 +145,45 @@ export default function SignIn() {
|
|||||||
rounded
|
rounded
|
||||||
onClick={handleAnonymousSignIn}
|
onClick={handleAnonymousSignIn}
|
||||||
/>
|
/>
|
||||||
|
<GenericButton
|
||||||
|
label={"recover account"}
|
||||||
|
icon="pi pi-key"
|
||||||
|
className="text-[#f8f8ff] w-[250px] my-4 mx-auto"
|
||||||
|
rounded
|
||||||
|
onClick={() => setShowRecoveryInput(!showRecoveryInput)}
|
||||||
|
/>
|
||||||
|
{showRecoveryInput && (
|
||||||
|
<form onSubmit={handleRecoverySignIn} className="flex flex-col items-center bg-gray-700 w-fit mx-auto p-4 rounded-lg">
|
||||||
|
<div className="text-center mb-4 max-w-[350px]">
|
||||||
|
<p className="text-yellow-400 mb-2">⚠️ Recovery Notice</p>
|
||||||
|
<p className="text-gray-200 mb-2">
|
||||||
|
🔑 This recovery option is only for accounts created through:
|
||||||
|
</p>
|
||||||
|
<ul className="text-gray-300 mb-2 text-left list-none">
|
||||||
|
<li>📧 Email Login</li>
|
||||||
|
<li>👤 Anonymous Login</li>
|
||||||
|
<li>🐙 GitHub Login</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-red-400 text-sm">
|
||||||
|
⛔ Do NOT enter your personal Nostr nsec here! Only use the recovery key provided by PlebDevs (available on your profile page).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<InputText
|
||||||
|
type="password"
|
||||||
|
value={nsec}
|
||||||
|
onChange={(e) => setNsec(e.target.value)}
|
||||||
|
placeholder="Enter recovery key (nsec or hex)"
|
||||||
|
className="w-[250px] my-4"
|
||||||
|
/>
|
||||||
|
<GenericButton
|
||||||
|
type="submit"
|
||||||
|
label={"Recover Account"}
|
||||||
|
icon="pi pi-lock-open"
|
||||||
|
className="text-[#f8f8ff] w-fit my-4"
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user