diff --git a/.pwd.example b/.pwd.example index 5454631..4e8ec24 100644 --- a/.pwd.example +++ b/.pwd.example @@ -1,3 +1 @@ -# Default password: 123456 -# Remember to change this password after setting up the app -PASSWORD=14e1b600b1fd579f47433b88e8d85291 +PASSWORD=14e1b600b1fd579f47433b88e8d85291 \ No newline at end of file diff --git a/README.md b/README.md index f929a22..ddb3bad 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,13 @@ First, you need to make sure that your server (or computer) has installed Nodejs git clone https://github.com/nocpiun/ferrum.git cd ferrum npm i +npm run patch npm run build ``` 2. Prepare the `.pwd` file -Rename the `.pwd.example` to `.pwd` in the project root folder. And delete the comments in it. This file stores your access key to Ferrum. The default password is `123456`, and you can change your password in the settings. +Rename the `.pwd.example` to `.pwd` in the project root folder. This file stores your access key to Ferrum. The default password is `123456`, and you can change your password in the settings. ```txt PASSWORD=.... @@ -102,6 +103,7 @@ An explanation of the `package.json` scripts. - **`start`** Launch the app in production mode - **`dev`** Launch the app in development mode +- **`patch`** Install `next-ws` plugin - **`build`** Create a production build - **`build:ci`** Create a production build for CI environment - **`lint`** Run ESLint diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index da7b0a6..d4f6a78 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -16,7 +16,7 @@ import { useRouter } from "next/navigation"; import ThemeSwitch from "@/components/theme-switch"; import PasswordInput from "@/components/password-input"; import { BaseResponseData } from "@/types"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; const schema = z.object({ password: z.string().min(6, { message: "请输入访问密码" }), @@ -85,7 +85,7 @@ export default function Page() { src="/icon.png" className="w-[70px] mb-4" style={{ imageRendering: "pixelated" }}/> -

登录 Ferrum

+

登录 Ferrum{isDemo ? " (Demo)" : ""}

Explore throughout your server.

diff --git a/app/api/fs/disks/route.ts b/app/api/fs/disks/route.ts index 5455ac0..6e026dc 100644 --- a/app/api/fs/disks/route.ts +++ b/app/api/fs/disks/route.ts @@ -3,11 +3,25 @@ import os from "node:os"; import { NextRequest } from "next/server"; import si from "systeminformation"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; import { validateToken } from "@/lib/token"; import { packet, error } from "@/lib/packet"; +// Demo +import demoOS from "@/lib/demo/os.json"; export async function GET(req: NextRequest) { + if(isDemo) { + return packet({ + system: demoOS.os.platform, + disks: demoOS.disk.map((item) => ({ + used: item.used, + size: item.size, + capacity: (item.used / item.size) * 100, + mount: item.mount + })) + }); + } + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); @@ -17,7 +31,6 @@ export async function GET(req: NextRequest) { const disk = await si.fsSize(); return packet({ - status: 200, system: os.platform(), disks: disk.map((item) => ({ used: item.used, diff --git a/app/api/fs/download/route.ts b/app/api/fs/download/route.ts index 2ea0e21..7630b86 100644 --- a/app/api/fs/download/route.ts +++ b/app/api/fs/download/route.ts @@ -4,12 +4,25 @@ import path from "node:path"; import { NextRequest, NextResponse } from "next/server"; import mime from "mime"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; import { validateToken } from "@/lib/token"; import { error } from "@/lib/packet"; import { streamFile } from "@/lib/stream"; +// Demo +import demoFile from "@/lib/demo/file.json"; export async function GET(req: NextRequest) { + if(isDemo) { + return new NextResponse(demoFile.content, { + status: 200, + headers: { + "Content-Disposition": `attachment; filename=hello.txt`, + "Content-Type": "text/plain", + "Content-Length": demoFile.size.toString() + } + }); + } + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); diff --git a/app/api/fs/file/route.ts b/app/api/fs/file/route.ts index 75f8b16..7050ef1 100644 --- a/app/api/fs/file/route.ts +++ b/app/api/fs/file/route.ts @@ -4,13 +4,19 @@ import path from "node:path"; import { NextRequest, NextResponse } from "next/server"; import mime from "mime"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; import { validateToken } from "@/lib/token"; import { packet, error } from "@/lib/packet"; import { streamFile } from "@/lib/stream"; import { getFileType } from "@/lib/utils"; +// Demo +import demoFile from "@/lib/demo/file.json"; export async function GET(req: NextRequest) { + if(isDemo) { + return packet(demoFile); + } + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); @@ -59,6 +65,8 @@ interface FilePostRequestData { } export async function POST(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); @@ -95,6 +103,8 @@ interface FilePatchRequestData { } export async function PATCH(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); @@ -125,6 +135,8 @@ export async function PATCH(req: NextRequest) { } export async function DELETE(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); diff --git a/app/api/fs/folder/route.ts b/app/api/fs/folder/route.ts index 7d0cb6e..ffc4d03 100644 --- a/app/api/fs/folder/route.ts +++ b/app/api/fs/folder/route.ts @@ -5,11 +5,19 @@ import path from "node:path"; import { NextRequest } from "next/server"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; import { validateToken } from "@/lib/token"; import { packet, error } from "@/lib/packet"; +// Demo +import demoFiles from "@/lib/demo/files.json"; export async function GET(req: NextRequest) { + if(isDemo) { + return packet({ + items: demoFiles + }); + } + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); @@ -62,6 +70,8 @@ interface FolderPostRequestData { } export async function POST(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); @@ -95,6 +105,8 @@ export async function POST(req: NextRequest) { } export async function DELETE(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); @@ -128,6 +140,8 @@ interface FolderPutRequestData { } export async function PUT(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); diff --git a/app/api/fs/thumbnail/route.ts b/app/api/fs/thumbnail/route.ts index bf9e673..fd8d82c 100644 --- a/app/api/fs/thumbnail/route.ts +++ b/app/api/fs/thumbnail/route.ts @@ -3,13 +3,15 @@ import fs from "node:fs"; import { NextRequest, NextResponse } from "next/server"; import mime from "mime"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; import { validateToken } from "@/lib/token"; -import { error } from "@/lib/packet"; +import { error, packet } from "@/lib/packet"; import { getExtname, getFileType } from "@/lib/utils"; import { streamFile } from "@/lib/stream"; export async function GET(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); diff --git a/app/api/fs/upload/route.ts b/app/api/fs/upload/route.ts index d66a643..e009149 100644 --- a/app/api/fs/upload/route.ts +++ b/app/api/fs/upload/route.ts @@ -3,11 +3,13 @@ import path from "node:path"; import { NextRequest } from "next/server"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; import { validateToken } from "@/lib/token"; import { packet, error } from "@/lib/packet"; export async function POST(req: NextRequest) { + if(isDemo) return packet({}); + const token = req.cookies.get(tokenStorageKey)?.value; if(!token) return error(401); diff --git a/app/api/os/route.ts b/app/api/os/route.ts index 97075e7..46e82c8 100644 --- a/app/api/os/route.ts +++ b/app/api/os/route.ts @@ -10,8 +10,10 @@ import { cpuModel, usagePercent as cpuPercent } from "node-system-stats"; import cookie from "cookie"; import { error } from "@/lib/packet"; -import { tokenStorageKey } from "@/lib/global"; +import { isDemo, tokenStorageKey } from "@/lib/global"; import { validateToken } from "@/lib/token"; +// Demo +import demoOS from "@/lib/demo/os.json"; export function GET() { return error(400); @@ -38,6 +40,12 @@ export function SOCKET( console.log("[Server: /api/os] Socket client connected."); const handleRequest = async () => { + if(isDemo) { + client.send(JSON.stringify(demoOS)); + + return; + } + const cpu = await si.cpu(); const cpuTemp = await si.cpuTemperature(); const mem = await si.mem(); diff --git a/components/dashboard/disk-widget.tsx b/components/dashboard/disk-widget.tsx index 38f46a5..d73e64e 100644 --- a/components/dashboard/disk-widget.tsx +++ b/components/dashboard/disk-widget.tsx @@ -56,7 +56,7 @@ const DiskWidget: React.FC = (props) => {
- + {disk.mount} diff --git a/components/explorer/disk-item.tsx b/components/explorer/disk-item.tsx index 6e28e87..a56926f 100644 --- a/components/explorer/disk-item.tsx +++ b/components/explorer/disk-item.tsx @@ -12,7 +12,7 @@ const DiskItem: React.FC = (props) => { return (
- {props.mount} + {props.mount} {formatSize(props.used, 1)} / {formatSize(props.size, 1)} diff --git a/lib/auth.ts b/lib/auth.ts index 3a42a30..d2b763b 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,14 +1,21 @@ import fs from "fs"; import path from "path"; +import { isDemo } from "./global"; + const envPath = path.join(process.cwd(), ".pwd"); +const defaultPwd = "14e1b600b1fd579f47433b88e8d85291"; // 123456 export function getPasswordFromEnv(): string { + if(isDemo) return defaultPwd; + const env = fs.readFileSync(envPath, "utf-8"); return env.replace("PASSWORD=", ""); } export function setPasswordToEnv(password: string) { + if(isDemo) return; + fs.writeFileSync(envPath, `PASSWORD=${password}`, "utf-8"); } diff --git a/lib/demo/file.json b/lib/demo/file.json new file mode 100644 index 0000000..80d18b5 --- /dev/null +++ b/lib/demo/file.json @@ -0,0 +1,5 @@ +{ + "path": "hello.txt", + "size": 53, + "content": "Welcome to Ferrum Explorer. This is a DEMO text file." +} diff --git a/lib/demo/files.json b/lib/demo/files.json new file mode 100644 index 0000000..501b233 --- /dev/null +++ b/lib/demo/files.json @@ -0,0 +1,20 @@ +[ + { + "name": "hello.txt", + "type": "file", + "size": 53, + "access": true + }, + { + "name": "test.txt", + "type": "file", + "size": 1024, + "access": true + }, + { + "name": "demo-folder", + "type": "folder", + "size": 0, + "access": true + } +] diff --git a/lib/demo/os.json b/lib/demo/os.json new file mode 100644 index 0000000..aab59a7 --- /dev/null +++ b/lib/demo/os.json @@ -0,0 +1,94 @@ +{ + "cpu": { + "model": "Intel xeon(R) Platinum 8280", + "totalCores": 28, + "usage": 34, + "temperature": 36 + }, + "memory": { + "total": 34359738368, + "usage": 82, + "amount": 4, + "mems": [ + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + }, + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + }, + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + }, + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + }, + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + }, + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + }, + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + }, + { + "size": 34359738368, + "type": "DDR4", + "formFactor": "DIMM" + } + ] + }, + "gpu": [ + { + "model": "A100 Tensor Core GPU", + "vendor": "Nvidia", + "memoryTotal": 81920, + "memoryUsage": 58 + } + ], + "battery": { + "hasBattery": true, + "isCharging": true, + "percent": 90 + }, + "disk": [ + { + "mount": "/", + "type": "ext4", + "size": 107374182400, + "used": 71940702208 + }, + { + "mount": "/media/user/disk-1", + "type": "exfat", + "size": 1099511627776, + "used": 678604832768 + }, + { + "mount": "/media/user/disk-2", + "type": "exfat", + "size": 1099511627776, + "used": 1055488212992 + } + ], + "os": { + "arch": "arm64", + "platform": "linux", + "release": "1.0.0", + "version": "1.0.0" + } +} diff --git a/lib/global.ts b/lib/global.ts index 7556723..b95a7df 100644 --- a/lib/global.ts +++ b/lib/global.ts @@ -1,4 +1,6 @@ export const version = "2.0.0a1"; +export const isDemo = process.env["NEXT_PUBLIC_IS_DEMO"] === "1"; + export const tokenStorageKey = "ferrum-token"; export const diskStorageKey = "ferrum-disk"; export const starListStorageKey = "ferrum-starred"; diff --git a/package.json b/package.json index e888d06..637b550 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "private": true, "scripts": { "dev": "next dev -p 3300", + "patch": "npx next-ws-cli@latest patch", "build": "next build", - "build:ci": "npx next-ws-cli@latest patch && npm run build", + "build:ci": "npm run patch && npm run build", "start": "next start -p 3300", "lint": "eslint . --ext .ts,.tsx -c .eslintrc.json --fix" },