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