Compare commits
No commits in common. "b7d144bb66d86bb1cbbabe1660fbb82407a202a3" and "f047a9ec1c100b12476930ef7a16475abc03fc54" have entirely different histories.
b7d144bb66
...
f047a9ec1c
@ -1,3 +0,0 @@
|
|||||||
NEXT_PUBLIC_CHINT_TOKEN=lIywwAMdrOdsRxuWvRoekdxrPtmIPkxA
|
|
||||||
DATABASE_URL="postgresql://postgres:root@localhost:5432/rooftop?schema=public"
|
|
||||||
JWT_SECRET="secret_key"
|
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -34,5 +34,3 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
.env
|
|
||||||
|
@ -64,6 +64,34 @@ const InverterViewPage = (props: Props) => {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab as={Fragment}>
|
||||||
|
{({ selected }) => (
|
||||||
|
<button className={`${selected ? 'border-b !border-primary text-primary !outline-none' : ''} -mb-[1px] flex items-center border-transparent p-5 py-3 before:inline-block hover:border-b hover:!border-primary hover:text-primary`} >
|
||||||
|
Event
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
|
<Tab as={Fragment}>
|
||||||
|
{({ selected }) => (
|
||||||
|
<button className={`${selected ? 'border-b !border-primary text-primary !outline-none' : ''} -mb-[1px] flex items-center border-transparent p-5 py-3 before:inline-block hover:border-b hover:!border-primary hover:text-primary`} >
|
||||||
|
Settings
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
|
<Tab as={Fragment}>
|
||||||
|
{({ selected }) => (
|
||||||
|
<button className={`${selected ? 'border-b !border-primary text-primary !outline-none' : ''} -mb-[1px] flex items-center border-transparent p-5 py-3 before:inline-block hover:border-b hover:!border-primary hover:text-primary`} >
|
||||||
|
Firmware
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
|
<Tab as={Fragment}>
|
||||||
|
{({ selected }) => (
|
||||||
|
<button className={`${selected ? 'border-b !border-primary text-primary !outline-none' : ''} -mb-[1px] flex items-center border-transparent p-5 py-3 before:inline-block hover:border-b hover:!border-primary hover:text-primary`} >
|
||||||
|
Data
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
<Tab.Panels>
|
<Tab.Panels>
|
||||||
<Tab.Panel>
|
<Tab.Panel>
|
||||||
@ -90,8 +118,9 @@ const InverterViewPage = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
<Tab.Panel>Chart</Tab.Panel>
|
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
)}
|
)}
|
||||||
|
@ -100,30 +100,12 @@ const Sidebar = () => {
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li className="menu nav-item">
|
<li className="menu nav-item">
|
||||||
<button type="button" className={`${currentMenu === 'sungrow' ? 'active' : ''} nav-link group w-full`} onClick={() => toggleMenu('sungrow')}>
|
<Link href="#" className="nav-link group">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<IconMenuComponents className="shrink-0 group-hover:!text-primary" />
|
<IconMenuComponents className="shrink-0 group-hover:!text-primary" />
|
||||||
<span className="text-black ltr:pl-3 rtl:pr-3 dark:text-[#506690] dark:group-hover:text-white-dark">Sungrow</span>
|
<span className="text-black ltr:pl-3 rtl:pr-3 dark:text-[#506690] dark:group-hover:text-white-dark">Sungrow</span>
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
<div className={currentMenu !== 'component' ? '-rotate-90 rtl:rotate-90' : ''}>
|
|
||||||
<IconCaretDown />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<AnimateHeight duration={300} height={currentMenu === 'sungrow' ? 'auto' : 0}>
|
|
||||||
<ul className="sub-menu text-gray-500">
|
|
||||||
<li>
|
|
||||||
<Link href="/sungrow/plant">Plant</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link href="/sungrow/device">Device</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link href="/sungrow/maintenance">Maintenance</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</AnimateHeight>
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li className="menu nav-item">
|
<li className="menu nav-item">
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
GET http://localhost:3005/api/auth/me
|
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2YmM2MGIzMC1hNzcyLTRiM2MtOTYwOC1jMjgyNmNhMjA2NjEiLCJlbWFpbCI6ImFzZEBhc2QuY29tIiwiaWF0IjoxNzQwNTUyMjE4LCJleHAiOjE3NDA1NTU4MTh9.n3rFlFbqKY-kg8YF6cuRkT_Kc6hsCqQQ87TJbkS8DAg
|
|
||||||
|
|
||||||
###
|
|
||||||
POST http://localhost:3005/api/register
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"email": "asd@asd.com",
|
|
||||||
"password":"123456"
|
|
||||||
}
|
|
||||||
|
|
||||||
###
|
|
||||||
POST http://localhost:3005/api/login
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"email": "asd@asd.com",
|
|
||||||
"password":"123456"
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
const SECRET_KEY = process.env.JWT_SECRET as string;
|
|
||||||
|
|
||||||
export function middleware(req: NextRequest) {
|
|
||||||
const token = req.cookies.get("token")?.value;
|
|
||||||
|
|
||||||
if (!token) return NextResponse.redirect(new URL("/login", req.url));
|
|
||||||
|
|
||||||
try {
|
|
||||||
jwt.verify(token, SECRET_KEY);
|
|
||||||
return NextResponse.next();
|
|
||||||
} catch (error) {
|
|
||||||
return NextResponse.redirect(new URL("/login", req.url));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const config = { matcher: ["/dashboard", "/profile"] };
|
|
893
package-lock.json
generated
893
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@headlessui/react": "^1.7.8",
|
"@headlessui/react": "^1.7.8",
|
||||||
"@prisma/client": "^6.4.1",
|
|
||||||
"@reduxjs/toolkit": "^1.9.1",
|
"@reduxjs/toolkit": "^1.9.1",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
"@types/node": "18.11.18",
|
"@types/node": "18.11.18",
|
||||||
@ -19,12 +18,9 @@
|
|||||||
"@types/react-dom": "18.0.10",
|
"@types/react-dom": "18.0.10",
|
||||||
"apexcharts": "^4.5.0",
|
"apexcharts": "^4.5.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bcrypt": "^5.1.1",
|
|
||||||
"cookie": "^1.0.2",
|
|
||||||
"eslint": "8.32.0",
|
"eslint": "8.32.0",
|
||||||
"eslint-config-next": "13.1.2",
|
"eslint-config-next": "13.1.2",
|
||||||
"i18next": "^22.4.10",
|
"i18next": "^22.4.10",
|
||||||
"jsonwebtoken": "^9.0.2",
|
|
||||||
"next": "14.0.3",
|
"next": "14.0.3",
|
||||||
"ni18n": "^1.0.5",
|
"ni18n": "^1.0.5",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
@ -35,6 +31,7 @@
|
|||||||
"react-perfect-scrollbar": "^1.5.8",
|
"react-perfect-scrollbar": "^1.5.8",
|
||||||
"react-popper": "^2.3.0",
|
"react-popper": "^2.3.0",
|
||||||
"react-redux": "^8.1.3",
|
"react-redux": "^8.1.3",
|
||||||
|
"typescript": "4.9.4",
|
||||||
"universal-cookie": "^6.1.1",
|
"universal-cookie": "^6.1.1",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
@ -47,7 +44,6 @@
|
|||||||
"postcss": "^8.4.35",
|
"postcss": "^8.4.35",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"prettier-plugin-tailwindcss": "^0.2.0",
|
"prettier-plugin-tailwindcss": "^0.2.0",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1"
|
||||||
"typescript": "^5.7.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from "next";
|
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
import { PrismaClient } from "@prisma/client";
|
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
const SECRET_KEY = process.env.JWT_SECRET as string;
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
const authHeader = req.headers.authorization;
|
|
||||||
|
|
||||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
||||||
return res.status(401).json({ message: "Unauthorized" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = authHeader.split(" ")[1]; // Extract token
|
|
||||||
|
|
||||||
try {
|
|
||||||
const decoded: any = jwt.verify(token, SECRET_KEY);
|
|
||||||
const user = await prisma.user.findUnique({ where: { id: decoded.userId } });
|
|
||||||
|
|
||||||
if (!user) return res.status(401).json({ message: "User not found" });
|
|
||||||
|
|
||||||
res.json({ user });
|
|
||||||
} catch (error) {
|
|
||||||
res.status(401).json({ message: "Invalid token" });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from "next";
|
|
||||||
import { PrismaClient } from "@prisma/client";
|
|
||||||
import bcrypt from "bcrypt";
|
|
||||||
import jwt from "jsonwebtoken";
|
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
const SECRET_KEY = process.env.JWT_SECRET as string;
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
if (req.method !== "POST") return res.status(405).json({ message: "Method not allowed" });
|
|
||||||
|
|
||||||
const { email, password } = req.body;
|
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({ where: { email } });
|
|
||||||
if (!user) return res.status(401).json({ message: "Invalid credentials" });
|
|
||||||
|
|
||||||
const isMatch = await bcrypt.compare(password, user.password);
|
|
||||||
if (!isMatch) return res.status(401).json({ message: "Invalid credentials" });
|
|
||||||
|
|
||||||
const token = jwt.sign({ userId: user.id, email: user.email }, SECRET_KEY, { expiresIn: "1h" });
|
|
||||||
|
|
||||||
res.setHeader("Set-Cookie", `token=${token}; HttpOnly; Path=/; Secure`);
|
|
||||||
res.json({ token });
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from "next";
|
|
||||||
import { PrismaClient } from "@prisma/client";
|
|
||||||
import bcrypt from "bcrypt";
|
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
if (req.method !== "POST") return res.status(405).json({ message: "Method not allowed" });
|
|
||||||
|
|
||||||
const { email, password } = req.body;
|
|
||||||
|
|
||||||
const existingUser = await prisma.user.findUnique({ where: { email } });
|
|
||||||
if (existingUser) return res.status(400).json({ message: "User already exists" });
|
|
||||||
|
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
|
||||||
const user = await prisma.user.create({
|
|
||||||
data: { email, password: hashedPassword },
|
|
||||||
});
|
|
||||||
|
|
||||||
res.status(201).json({ message: "User registered", user });
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "User" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"email" TEXT NOT NULL,
|
|
||||||
"password" TEXT NOT NULL,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
|
@ -1,3 +0,0 @@
|
|||||||
# Please do not edit this file manually
|
|
||||||
# It should be added in your version-control system (e.g., Git)
|
|
||||||
provider = "postgresql"
|
|
@ -1,22 +0,0 @@
|
|||||||
// This is your Prisma schema file,
|
|
||||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
||||||
|
|
||||||
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
||||||
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
||||||
|
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
}
|
|
||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "postgresql"
|
|
||||||
url = env("DATABASE_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
model User {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
email String @unique
|
|
||||||
password String
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user