提交 5e4dfbf5 作者: 翁进城

登录模块开发

上级 ea160631
...@@ -3,7 +3,8 @@ import { Avatar, Button, Space, Tabs } from "antd"; ...@@ -3,7 +3,8 @@ import { Avatar, Button, Space, Tabs } from "antd";
import type { TabsProps } from "antd"; import type { TabsProps } from "antd";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { PlusOutlined } from "@ant-design/icons"; import LoginModal from "~/components/loginModal";
import { useUser } from "~/lib/hooks";
const items: TabsProps["items"] = [ const items: TabsProps["items"] = [
{ {
...@@ -39,6 +40,7 @@ const items: TabsProps["items"] = [ ...@@ -39,6 +40,7 @@ const items: TabsProps["items"] = [
export default function NavHeader() { export default function NavHeader() {
const router = useRouter(); const router = useRouter();
const currentPath = router.asPath; const currentPath = router.asPath;
const user = useUser();
console.log("currentHash", currentPath); console.log("currentHash", currentPath);
const onChange = (key: string) => { const onChange = (key: string) => {
...@@ -46,6 +48,16 @@ export default function NavHeader() { ...@@ -46,6 +48,16 @@ export default function NavHeader() {
router.push(key); router.push(key);
}; };
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const handleCancel = () => {
setIsModalOpen(false);
};
return ( return (
<div className={styles.navHeader}> <div className={styles.navHeader}>
<div className={styles.nav}> <div className={styles.nav}>
...@@ -62,10 +74,21 @@ export default function NavHeader() { ...@@ -62,10 +74,21 @@ export default function NavHeader() {
</Button> </Button>
<Button className={styles.btn2}>入驻加盟</Button> <Button className={styles.btn2}>入驻加盟</Button>
</Space> </Space>
<div className={styles.haedImg}> {user ? (
<Avatar size={48} style={{ background: "#fff" }}></Avatar> <div className={styles.haedImg}>
</div> <Avatar size={48} style={{ background: "#fff" }}></Avatar>
</div>
) : (
<Button
type="text"
onClick={showModal}
style={{ fontWeight: "bold", fontSize: 16 }}
>
登录
</Button>
)}
</div> </div>
<LoginModal open={isModalOpen} onCancel={handleCancel}></LoginModal>
</div> </div>
); );
} }
import React, { useState } from "react";
import { Modal } from "antd";
import Image from "next/image";
type Props = {
open: boolean;
onCancel: () => void;
};
export default function loginModal(props: Props) {
return (
<>
<Modal
open={props.open}
onCancel={props.onCancel}
width={400}
footer={null}
>
<div
style={{
fontSize: 20,
fontFamily: "MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI",
fontWeight: "bold",
color: "#000",
marginTop: 17,
marginBottom: 48,
textAlign: "center",
}}
>
欢迎来到云享飞
</div>
<Image
alt=""
src=""
width={160}
height={160}
style={{ margin: "auto", display: "block" }}
></Image>
<div
style={{
marginTop: 39,
marginBottom: 52,
fontSize: 14,
fontFamily: "MicrosoftYaHei",
color: "#3E454D",
textAlign: "center",
}}
>
打开微信扫一扫
</div>
</Modal>
</>
);
}
import { serialize, parse } from "cookie";
const TOKEN_NAME = "token";
export const MAX_AGE = 60 * 60 * 8; // 8 hours
export function setTokenCookie(res, token) {
const cookie = serialize(TOKEN_NAME, token, {
maxAge: MAX_AGE,
expires: new Date(Date.now() + MAX_AGE * 1000),
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
sameSite: "lax",
});
res.setHeader("Set-Cookie", cookie);
}
export function removeTokenCookie(res) {
const cookie = serialize(TOKEN_NAME, "", {
maxAge: -1,
path: "/",
});
res.setHeader("Set-Cookie", cookie);
}
export function parseCookies(req) {
// For API Routes we don't need to parse the cookies.
if (req.cookies) return req.cookies;
// For pages we do need to parse the cookies.
const cookie = req.headers?.cookie;
return parse(cookie || "");
}
export function getTokenCookie(req) {
const cookies = parseCookies(req);
return cookies[TOKEN_NAME];
}
import Iron from "@hapi/iron";
import { MAX_AGE, setTokenCookie, getTokenCookie } from "./auth-cookies";
const TOKEN_SECRET = process.env.TOKEN_SECRET;
export async function setLoginSession(res, session) {
const createdAt = Date.now();
// Create a session object with a max age that we can validate later
const obj = { ...session, createdAt, maxAge: MAX_AGE };
const token = await Iron.seal(obj, TOKEN_SECRET, Iron.defaults);
setTokenCookie(res, token);
}
export async function getLoginSession(req) {
const token = getTokenCookie(req);
if (!token) return;
const session = await Iron.unseal(token, TOKEN_SECRET, Iron.defaults);
const expiresAt = session.createdAt + session.maxAge * 1000;
// Validate the expiration date of the session
if (Date.now() > expiresAt) {
throw new Error("Session expired");
}
return session;
}
import { useEffect } from "react";
import Router from "next/router";
import request from '~/api/request';
/*
const fetcher = (url) =>
fetch(url)
.then((r) => r.json())
.then((data) => {
return { user: data?.user || null };
}); */
export function useUser({ redirectTo, redirectIfFound } = {}) {
const { data, error } = request("/api/user");
const user = data?.user;
const finished = Boolean(data);
const hasUser = Boolean(user);
useEffect(() => {
if (!redirectTo || !finished) return;
if (
// If redirectTo is set, redirect if the user was not found.
(redirectTo && !redirectIfFound && !hasUser) ||
// If redirectIfFound is also set, redirect if the user was found
(redirectIfFound && hasUser)
) {
Router.push(redirectTo);
}
}, [redirectTo, redirectIfFound, finished, hasUser]);
return error ? null : user;
}
import Local from "passport-local";
import { findUser, validatePassword } from "./user";
export const localStrategy = new Local.Strategy(function (
username,
password,
done
) {
findUser({ username })
.then((user) => {
if (user && validatePassword(user, password)) {
done(null, user);
} else {
done(new Error("Invalid username and password combination"));
}
})
.catch((error) => {
done(error);
});
});
import crypto from "crypto";
import { v4 as uuidv4 } from "uuid";
/**
* User methods. The example doesn't contain a DB, but for real applications you must use a
* db here, such as MongoDB, Fauna, SQL, etc.
*/
const users = [];
export async function createUser({ username, password }) {
// Here you should create the user and save the salt and hashed password (some dbs may have
// authentication methods that will do it for you so you don't have to worry about it):
const salt = crypto.randomBytes(16).toString("hex");
const hash = crypto
.pbkdf2Sync(password, salt, 1000, 64, "sha512")
.toString("hex");
const user = {
id: uuidv4(),
createdAt: Date.now(),
username,
hash,
salt,
};
// This is an in memory store for users, there is no data persistence without a proper DB
users.push(user);
return { username, createdAt: Date.now() };
}
// Here you should lookup for the user in your DB
export async function findUser({ username }) {
// This is an in memory store for users, there is no data persistence without a proper DB
return users.find((user) => user.username === username);
}
// Compare the password of an already fetched user (using `findUser`) and compare the
// password for a potential match
export function validatePassword(user, inputPassword) {
const inputHash = crypto
.pbkdf2Sync(inputPassword, user.salt, 1000, 64, "sha512")
.toString("hex");
const passwordsMatch = user.hash === inputHash;
return passwordsMatch;
}
...@@ -30,7 +30,14 @@ ...@@ -30,7 +30,14 @@
}, },
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.0.1", "@ant-design/icons": "^5.0.1",
"@hapi/iron": "^7.0.1",
"babel-plugin-styled-components": "^2.1.1", "babel-plugin-styled-components": "^2.1.1",
"styled-components": "^6.0.0-rc.1" "cookie": "^0.5.0",
"next-connect": "^1.0.0",
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"styled-components": "^6.0.0-rc.1",
"swr": "^2.1.5",
"uuid": "^9.0.0"
} }
} }
...@@ -109,6 +109,7 @@ export default function publishMessage(props: Props) { ...@@ -109,6 +109,7 @@ export default function publishMessage(props: Props) {
okText="发布" okText="发布"
okButtonProps={{ style: { height: 37, padding: "0 32px", fontSize: 16 } }} okButtonProps={{ style: { height: 37, padding: "0 32px", fontSize: 16 } }}
cancelButtonProps={{ style: { display: "none" } }} cancelButtonProps={{ style: { display: "none" } }}
maskClosable={false}
> >
<Form style={{ paddingTop: 32 }}> <Form style={{ paddingTop: 32 }}>
<Form.Item> <Form.Item>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论