mirror of
https://github.com/khairul169/home-lab.git
synced 2025-04-28 16:49:36 +07:00
feat: add terminal & wake pc
This commit is contained in:
parent
1e8252d9da
commit
5e8a3c88a2
@ -1,5 +1,9 @@
|
|||||||
#
|
# Config
|
||||||
JWT_SECRET=
|
JWT_SECRET=
|
||||||
|
|
||||||
AUTH_USERNAME=
|
AUTH_USERNAME=
|
||||||
AUTH_PASSWORD=
|
AUTH_PASSWORD=
|
||||||
|
|
||||||
|
# Apps
|
||||||
|
PC_MAC_ADDR=
|
||||||
|
TERMINAL_SHELL=
|
||||||
|
Binary file not shown.
1
backend/global.d.ts
vendored
Normal file
1
backend/global.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
declare module "wol";
|
@ -1,8 +1,11 @@
|
|||||||
|
import "dotenv/config";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { cors } from "hono/cors";
|
import { cors } from "hono/cors";
|
||||||
import { serveStatic } from "hono/bun";
|
|
||||||
import { HTTPException } from "hono/http-exception";
|
import { HTTPException } from "hono/http-exception";
|
||||||
|
import { serveStatic } from "@hono/node-server/serve-static";
|
||||||
|
import { serve } from "@hono/node-server";
|
||||||
import routes from "./routes/_routes";
|
import routes from "./routes/_routes";
|
||||||
|
import createWsServer from "./websocket";
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
.use(cors())
|
.use(cors())
|
||||||
@ -15,4 +18,8 @@ const app = new Hono()
|
|||||||
return c.json({ message: err.message }, 500);
|
return c.json({ message: err.message }, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default app;
|
const server = serve(app, (info) => {
|
||||||
|
console.log(`App listening on http://${info.address}:${info.port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
createWsServer(server);
|
||||||
|
46
backend/lib/terminal.ts
Normal file
46
backend/lib/terminal.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import Pty from "node-pty";
|
||||||
|
import type { WebSocket } from "ws";
|
||||||
|
|
||||||
|
type TerminalClient = WebSocket & { tty?: Pty.IPty | null };
|
||||||
|
|
||||||
|
export const createTerminalSession = (client: TerminalClient) => {
|
||||||
|
// Each client will have own terminal
|
||||||
|
const tty = Pty.spawn(process.env.TERMINAL_SHELL || "bash", [], {
|
||||||
|
name: "xterm-color",
|
||||||
|
cols: 80,
|
||||||
|
rows: 24,
|
||||||
|
cwd: process.env.PWD,
|
||||||
|
env: process.env,
|
||||||
|
});
|
||||||
|
client.tty = tty;
|
||||||
|
|
||||||
|
tty.onExit(() => {
|
||||||
|
client.tty = null;
|
||||||
|
client.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
tty.onData(function (data) {
|
||||||
|
client.send(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("close", function () {
|
||||||
|
if (client.tty) {
|
||||||
|
client.tty.kill("SIGINT");
|
||||||
|
client.tty = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("message", function (data) {
|
||||||
|
const msg = data.toString("utf-8");
|
||||||
|
|
||||||
|
if (msg.startsWith("resize:")) {
|
||||||
|
const [cols, rows] = msg.split(":")[1].split(",");
|
||||||
|
if (client.tty) {
|
||||||
|
client.tty.resize(parseInt(cols), parseInt(rows));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.tty && client.tty.write(msg);
|
||||||
|
});
|
||||||
|
};
|
165
backend/package-lock.json
generated
Normal file
165
backend/package-lock.json
generated
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
{
|
||||||
|
"name": "backend",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "backend",
|
||||||
|
"dependencies": {
|
||||||
|
"@hono/zod-validator": "^0.2.0",
|
||||||
|
"hono": "^4.1.0",
|
||||||
|
"nanoid": "^5.0.6",
|
||||||
|
"systeminformation": "^5.22.2",
|
||||||
|
"wol": "^1.0.7",
|
||||||
|
"zod": "^3.22.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"@types/jsonwebtoken": "^9.0.6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@hono/zod-validator": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hono/zod-validator/-/zod-validator-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-PC7akbA/DCFY406BL3+ogjYb+7Fgfs/6XPvyURBYMczo0M7kYsTUMnF8hA9mez1RORNCWPqXuFGfKrkoUVPvrQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"hono": ">=3.9.0",
|
||||||
|
"zod": "^3.19.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/bun": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-E6UWZuN4ymAxzUBWVIGDHJ3Zey7I8cMzDZ+cB1BqhZsmd1uPb9iAQzpWMruY1mKzsuD3R+dZPoBkZz8QL1KhSA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"bun-types": "1.0.29"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/jsonwebtoken": {
|
||||||
|
"version": "9.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz",
|
||||||
|
"integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.11.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
|
||||||
|
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/ws": {
|
||||||
|
"version": "8.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
|
||||||
|
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bun-types": {
|
||||||
|
"version": "1.0.29",
|
||||||
|
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.0.29.tgz",
|
||||||
|
"integrity": "sha512-Z+U1ORr/2UCwxelIZxE83pyPLclviYL9UewQCNEUmGeLObY8ao+3WF3D8N1+NMv2+S+hUWsdBJam+4GoPEz35g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "~20.11.3",
|
||||||
|
"@types/ws": "~8.5.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hono": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hono/-/hono-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-9no6DCHb4ijB1tWdFXU6JnrnFgzwVZ1cnIcS1BjAFnMcjbtBTOMsQrDrPH3GXbkNEEEkj8kWqcYBy8Qc0bBkJQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "5.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz",
|
||||||
|
"integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || >=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/systeminformation": {
|
||||||
|
"version": "5.22.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.3.tgz",
|
||||||
|
"integrity": "sha512-pTU7/kMQaYfUs929Uhl+C2heTNhKIgPuoiV5s2TMO3SLf10Zr7Rl/ZvVaiYWFbZVdFsZ+9tSXsybGdBQcr+xww==",
|
||||||
|
"os": [
|
||||||
|
"darwin",
|
||||||
|
"linux",
|
||||||
|
"win32",
|
||||||
|
"freebsd",
|
||||||
|
"openbsd",
|
||||||
|
"netbsd",
|
||||||
|
"sunos",
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"systeminformation": "lib/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "Buy me a coffee",
|
||||||
|
"url": "https://www.buymeacoffee.com/systeminfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
|
||||||
|
"integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/wol": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/wol/-/wol-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-kg7ETY8g3V5+3GVhUfWCVjeXuCmfrX6xfw4cw4c88+MtoxkbFmcs9Y5yhT1wwOL8inogFUQZ8JMzH9OekaaawQ==",
|
||||||
|
"bin": {
|
||||||
|
"wake": "bin/wake"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "3.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
|
||||||
|
"integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,21 +3,28 @@
|
|||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --watch index.ts",
|
"dev": "tsx --watch index.ts",
|
||||||
"start": "bun run index.ts"
|
"start": "tsx index.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
"@types/jsonwebtoken": "^9.0.6"
|
"@types/node": "^20.11.28",
|
||||||
|
"@types/ws": "^8.5.10",
|
||||||
|
"tsx": "^4.7.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hono/node-server": "^1.8.2",
|
||||||
"@hono/zod-validator": "^0.2.0",
|
"@hono/zod-validator": "^0.2.0",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
"hono": "^4.1.0",
|
"hono": "^4.1.0",
|
||||||
"nanoid": "^5.0.6",
|
"nanoid": "^5.0.6",
|
||||||
|
"node-pty": "^1.0.0",
|
||||||
"systeminformation": "^5.22.2",
|
"systeminformation": "^5.22.2",
|
||||||
|
"wol": "^1.0.7",
|
||||||
|
"ws": "^8.16.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ import { Hono } from "hono";
|
|||||||
import auth from "./auth";
|
import auth from "./auth";
|
||||||
import system from "./system";
|
import system from "./system";
|
||||||
import _process from "./process";
|
import _process from "./process";
|
||||||
|
import apps from "./apps";
|
||||||
import { authMiddleware } from "../lib/jwt";
|
import { authMiddleware } from "../lib/jwt";
|
||||||
|
|
||||||
const routes = new Hono()
|
const routes = new Hono()
|
||||||
.route("/auth", auth)
|
.route("/auth", auth)
|
||||||
.use(authMiddleware)
|
.use(authMiddleware)
|
||||||
.route("/system", system)
|
.route("/system", system)
|
||||||
.route("/process", _process);
|
.route("/process", _process)
|
||||||
|
.route("/apps", apps);
|
||||||
|
|
||||||
export type AppType = typeof routes;
|
export type AppType = typeof routes;
|
||||||
|
|
||||||
|
22
backend/routes/apps.ts
Normal file
22
backend/routes/apps.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Hono } from "hono";
|
||||||
|
import wol from "wol";
|
||||||
|
import { HTTPException } from "hono/http-exception";
|
||||||
|
|
||||||
|
const route = new Hono().post("/wakepc", async (c) => {
|
||||||
|
const { PC_MAC_ADDR } = process.env;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
wol.wake(PC_MAC_ADDR || "", (err: any, res: any) =>
|
||||||
|
err ? reject(err) : resolve(res)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw new HTTPException(400, { message: "Cannot wake pc up!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({ message: "waking up..." });
|
||||||
|
});
|
||||||
|
|
||||||
|
export default route;
|
28
backend/websocket.ts
Normal file
28
backend/websocket.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { WebSocketServer } from "ws";
|
||||||
|
import { verifyToken } from "./lib/jwt";
|
||||||
|
import { createTerminalSession } from "./lib/terminal";
|
||||||
|
|
||||||
|
const createWsServer = (server: any) => {
|
||||||
|
const wss = new WebSocketServer({ server: server as never });
|
||||||
|
|
||||||
|
wss.on("connection", async (ws, req) => {
|
||||||
|
const url = new URL(req.url || "", `http://${req.headers.host}`);
|
||||||
|
const token = url.searchParams.get("token");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await verifyToken(token || "");
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
ws.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.pathname === "/terminal") {
|
||||||
|
createTerminalSession(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.on("error", console.error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createWsServer;
|
257
backend/yarn.lock
Normal file
257
backend/yarn.lock
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f"
|
||||||
|
integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==
|
||||||
|
|
||||||
|
"@esbuild/android-arm64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4"
|
||||||
|
integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==
|
||||||
|
|
||||||
|
"@esbuild/android-arm@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824"
|
||||||
|
integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==
|
||||||
|
|
||||||
|
"@esbuild/android-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d"
|
||||||
|
integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e"
|
||||||
|
integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd"
|
||||||
|
integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487"
|
||||||
|
integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c"
|
||||||
|
integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b"
|
||||||
|
integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef"
|
||||||
|
integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601"
|
||||||
|
integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299"
|
||||||
|
integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec"
|
||||||
|
integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8"
|
||||||
|
integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf"
|
||||||
|
integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8"
|
||||||
|
integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==
|
||||||
|
|
||||||
|
"@esbuild/linux-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78"
|
||||||
|
integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b"
|
||||||
|
integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0"
|
||||||
|
integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30"
|
||||||
|
integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae"
|
||||||
|
integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67"
|
||||||
|
integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==
|
||||||
|
|
||||||
|
"@esbuild/win32-x64@0.19.12":
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae"
|
||||||
|
integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==
|
||||||
|
|
||||||
|
"@hono/node-server@^1.8.2":
|
||||||
|
version "1.8.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.8.2.tgz#940b3a0dbd7adbc510b79b626f3603258493354b"
|
||||||
|
integrity sha512-h8l2TBLCPHZBUrrkosZ6L5CpBLj6zdESyF4B+zngiCDF7aZFQJ0alVbLx7jn8PCVi9EyoFf8a4hOZFi1tD95EA==
|
||||||
|
|
||||||
|
"@hono/zod-validator@^0.2.0":
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@hono/zod-validator/-/zod-validator-0.2.0.tgz#77d8f1f167cba85b008a52f594ed823c841b2300"
|
||||||
|
integrity sha512-PC7akbA/DCFY406BL3+ogjYb+7Fgfs/6XPvyURBYMczo0M7kYsTUMnF8hA9mez1RORNCWPqXuFGfKrkoUVPvrQ==
|
||||||
|
|
||||||
|
"@types/jsonwebtoken@^9.0.6":
|
||||||
|
version "9.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz#d1af3544d99ad992fb6681bbe60676e06b032bd3"
|
||||||
|
integrity sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/node@*", "@types/node@^20.11.28":
|
||||||
|
version "20.11.28"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.28.tgz#4fd5b2daff2e580c12316e457473d68f15ee6f66"
|
||||||
|
integrity sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==
|
||||||
|
dependencies:
|
||||||
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
|
"@types/ws@^8.5.10":
|
||||||
|
version "8.5.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"
|
||||||
|
integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
|
dotenv@^16.4.5:
|
||||||
|
version "16.4.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
|
||||||
|
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
|
||||||
|
|
||||||
|
esbuild@~0.19.10:
|
||||||
|
version "0.19.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04"
|
||||||
|
integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==
|
||||||
|
optionalDependencies:
|
||||||
|
"@esbuild/aix-ppc64" "0.19.12"
|
||||||
|
"@esbuild/android-arm" "0.19.12"
|
||||||
|
"@esbuild/android-arm64" "0.19.12"
|
||||||
|
"@esbuild/android-x64" "0.19.12"
|
||||||
|
"@esbuild/darwin-arm64" "0.19.12"
|
||||||
|
"@esbuild/darwin-x64" "0.19.12"
|
||||||
|
"@esbuild/freebsd-arm64" "0.19.12"
|
||||||
|
"@esbuild/freebsd-x64" "0.19.12"
|
||||||
|
"@esbuild/linux-arm" "0.19.12"
|
||||||
|
"@esbuild/linux-arm64" "0.19.12"
|
||||||
|
"@esbuild/linux-ia32" "0.19.12"
|
||||||
|
"@esbuild/linux-loong64" "0.19.12"
|
||||||
|
"@esbuild/linux-mips64el" "0.19.12"
|
||||||
|
"@esbuild/linux-ppc64" "0.19.12"
|
||||||
|
"@esbuild/linux-riscv64" "0.19.12"
|
||||||
|
"@esbuild/linux-s390x" "0.19.12"
|
||||||
|
"@esbuild/linux-x64" "0.19.12"
|
||||||
|
"@esbuild/netbsd-x64" "0.19.12"
|
||||||
|
"@esbuild/openbsd-x64" "0.19.12"
|
||||||
|
"@esbuild/sunos-x64" "0.19.12"
|
||||||
|
"@esbuild/win32-arm64" "0.19.12"
|
||||||
|
"@esbuild/win32-ia32" "0.19.12"
|
||||||
|
"@esbuild/win32-x64" "0.19.12"
|
||||||
|
|
||||||
|
fsevents@~2.3.3:
|
||||||
|
version "2.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
|
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||||
|
|
||||||
|
get-tsconfig@^4.7.2:
|
||||||
|
version "4.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.3.tgz#0498163d98f7b58484dd4906999c0c9d5f103f83"
|
||||||
|
integrity sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==
|
||||||
|
dependencies:
|
||||||
|
resolve-pkg-maps "^1.0.0"
|
||||||
|
|
||||||
|
hono@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/hono/-/hono-4.1.0.tgz#62cef81df0dbf731643155e1e5c1b9dffb230dc4"
|
||||||
|
integrity sha512-9no6DCHb4ijB1tWdFXU6JnrnFgzwVZ1cnIcS1BjAFnMcjbtBTOMsQrDrPH3GXbkNEEEkj8kWqcYBy8Qc0bBkJQ==
|
||||||
|
|
||||||
|
nan@^2.17.0:
|
||||||
|
version "2.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0"
|
||||||
|
integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==
|
||||||
|
|
||||||
|
nanoid@^5.0.6:
|
||||||
|
version "5.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.6.tgz#7f99a033aa843e4dcf9778bdaec5eb02f4dc44d5"
|
||||||
|
integrity sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==
|
||||||
|
|
||||||
|
node-pty@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd"
|
||||||
|
integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==
|
||||||
|
dependencies:
|
||||||
|
nan "^2.17.0"
|
||||||
|
|
||||||
|
resolve-pkg-maps@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f"
|
||||||
|
integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
|
||||||
|
|
||||||
|
systeminformation@^5.22.2:
|
||||||
|
version "5.22.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.22.3.tgz#33ef8bd045125d672f64e7000015fefae07a77cb"
|
||||||
|
integrity sha512-pTU7/kMQaYfUs929Uhl+C2heTNhKIgPuoiV5s2TMO3SLf10Zr7Rl/ZvVaiYWFbZVdFsZ+9tSXsybGdBQcr+xww==
|
||||||
|
|
||||||
|
tsx@^4.7.1:
|
||||||
|
version "4.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.7.1.tgz#27af6cbf4e1cdfcb9b5425b1c61bb7e668eb5e84"
|
||||||
|
integrity sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==
|
||||||
|
dependencies:
|
||||||
|
esbuild "~0.19.10"
|
||||||
|
get-tsconfig "^4.7.2"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.3"
|
||||||
|
|
||||||
|
undici-types@~5.26.4:
|
||||||
|
version "5.26.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||||
|
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||||
|
|
||||||
|
wol@^1.0.7:
|
||||||
|
version "1.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/wol/-/wol-1.0.7.tgz#a2e70efca2a28324a744a5d12331d359da470ff7"
|
||||||
|
integrity sha512-kg7ETY8g3V5+3GVhUfWCVjeXuCmfrX6xfw4cw4c88+MtoxkbFmcs9Y5yhT1wwOL8inogFUQZ8JMzH9OekaaawQ==
|
||||||
|
|
||||||
|
ws@^8.16.0:
|
||||||
|
version "8.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
|
||||||
|
integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
|
||||||
|
|
||||||
|
zod@^3.22.4:
|
||||||
|
version "3.22.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
|
||||||
|
integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==
|
@ -31,10 +31,14 @@
|
|||||||
"react-native-safe-area-context": "4.8.2",
|
"react-native-safe-area-context": "4.8.2",
|
||||||
"react-native-screens": "~3.29.0",
|
"react-native-screens": "~3.29.0",
|
||||||
"react-native-svg": "14.1.0",
|
"react-native-svg": "14.1.0",
|
||||||
|
"react-native-toast-notifications": "^3.4.0",
|
||||||
"react-native-web": "~0.19.6",
|
"react-native-web": "~0.19.6",
|
||||||
"react-query": "^3.39.3",
|
"react-query": "^3.39.3",
|
||||||
"twrnc": "^4.1.0",
|
"twrnc": "^4.1.0",
|
||||||
"typescript": "^5.3.0",
|
"typescript": "^5.3.0",
|
||||||
|
"xterm": "^5.3.0",
|
||||||
|
"xterm-addon-attach": "^0.9.0",
|
||||||
|
"xterm-addon-fit": "^0.8.0",
|
||||||
"zod": "^3.22.4",
|
"zod": "^3.22.4",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
|
@ -7,8 +7,10 @@ import { cn, tw } from "@/lib/utils";
|
|||||||
import { useDeviceContext } from "twrnc";
|
import { useDeviceContext } from "twrnc";
|
||||||
import { StatusBar } from "expo-status-bar";
|
import { StatusBar } from "expo-status-bar";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
import Toast from "react-native-toast-notifications";
|
||||||
import { useStore } from "zustand";
|
import { useStore } from "zustand";
|
||||||
import authStore from "@/stores/authStore";
|
import authStore from "@/stores/authStore";
|
||||||
|
import { toastStore } from "@/stores/toastStore";
|
||||||
|
|
||||||
const RootLayout = () => {
|
const RootLayout = () => {
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
@ -39,6 +41,11 @@ const RootLayout = () => {
|
|||||||
<View style={cn("flex-1 bg-[#f2f7fb]", { paddingTop: insets.top })}>
|
<View style={cn("flex-1 bg-[#f2f7fb]", { paddingTop: insets.top })}>
|
||||||
<Slot />
|
<Slot />
|
||||||
</View>
|
</View>
|
||||||
|
<Toast
|
||||||
|
ref={(ref) => {
|
||||||
|
toastStore.setState(ref);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
13
src/app/apps/lib.ts
Normal file
13
src/app/apps/lib.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// src/app/apps/lib.ts
|
||||||
|
|
||||||
|
import api from "@/lib/api";
|
||||||
|
import { showToast } from "@/stores/toastStore";
|
||||||
|
|
||||||
|
export const wakePcUp = async () => {
|
||||||
|
try {
|
||||||
|
await api.apps.wakepc.$post();
|
||||||
|
showToast("Waking up PC...");
|
||||||
|
} catch (err) {
|
||||||
|
showToast("Cannot wake up the PC!", { type: "danger" });
|
||||||
|
}
|
||||||
|
};
|
93
src/app/apps/terminal.tsx
Normal file
93
src/app/apps/terminal.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { Platform } from "react-native";
|
||||||
|
import { Terminal } from "xterm";
|
||||||
|
import { FitAddon } from "xterm-addon-fit";
|
||||||
|
import { AttachAddon } from "xterm-addon-attach";
|
||||||
|
|
||||||
|
import Box from "@ui/Box";
|
||||||
|
import Text from "@ui/Text";
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import "xterm/css/xterm.css";
|
||||||
|
import { API_BASEURL } from "@/lib/constants";
|
||||||
|
import authStore, { useAuth } from "@/stores/authStore";
|
||||||
|
|
||||||
|
const isWeb = Platform.OS === "web";
|
||||||
|
|
||||||
|
const TerminalPage = () => {
|
||||||
|
const { token } = useAuth();
|
||||||
|
const terminalRef = useRef<any>();
|
||||||
|
const fitRef = useRef<FitAddon | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isWeb || !token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const term = new Terminal({ theme: { background: "#1d1e2b" } });
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
|
term.loadAddon(fitAddon);
|
||||||
|
|
||||||
|
const baseUrl = API_BASEURL.replace("https://", "wss://").replace(
|
||||||
|
"http://",
|
||||||
|
"ws://"
|
||||||
|
);
|
||||||
|
const socket = new WebSocket(baseUrl + "/terminal?token=" + token);
|
||||||
|
const attachAddon = new AttachAddon(socket);
|
||||||
|
|
||||||
|
// Attach the socket to term
|
||||||
|
term.loadAddon(attachAddon);
|
||||||
|
term.open(terminalRef.current);
|
||||||
|
|
||||||
|
fitAddon.fit();
|
||||||
|
fitRef.current = fitAddon;
|
||||||
|
|
||||||
|
const sendResizeSignal = (data: { cols: number; rows: number }) => {
|
||||||
|
socket.send("resize:" + [data.cols, data.rows].join(","));
|
||||||
|
};
|
||||||
|
|
||||||
|
term.onResize(sendResizeSignal);
|
||||||
|
setTimeout(() => {
|
||||||
|
sendResizeSignal({ cols: term.cols, rows: term.rows });
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
attachAddon.dispose();
|
||||||
|
fitAddon.dispose();
|
||||||
|
term.dispose();
|
||||||
|
fitRef.current = null;
|
||||||
|
};
|
||||||
|
}, [token]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isWeb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onResize = () => {
|
||||||
|
if (fitRef.current) {
|
||||||
|
fitRef.current.fit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("resize", onResize);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", onResize);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!isWeb) {
|
||||||
|
return (
|
||||||
|
<Box className="p-8">
|
||||||
|
<Text className="text-center">Only web platform suppported.</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={terminalRef}
|
||||||
|
style={{ height: "100vh", background: "#1d1e2b", padding: 16 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TerminalPage;
|
57
src/app/index/_sections/Apps.tsx
Normal file
57
src/app/index/_sections/Apps.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import React, { ComponentProps } from "react";
|
||||||
|
import Text from "@ui/Text";
|
||||||
|
import Box from "@ui/Box";
|
||||||
|
import { Ionicons } from "@ui/Icons";
|
||||||
|
import { HStack } from "@ui/Stack";
|
||||||
|
import Button from "@ui/Button";
|
||||||
|
import { useNavigation } from "expo-router";
|
||||||
|
import { wakePcUp } from "@/app/apps/lib";
|
||||||
|
|
||||||
|
type Props = ComponentProps<typeof Box>;
|
||||||
|
|
||||||
|
const Apps = (props: Props) => {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const appList = [
|
||||||
|
{
|
||||||
|
name: "Terminal",
|
||||||
|
icon: <Ionicons name="terminal" />,
|
||||||
|
path: "terminal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PC Control",
|
||||||
|
icon: <Ionicons name="desktop" />,
|
||||||
|
action: wakePcUp,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box {...props}>
|
||||||
|
<Text className="text-lg md:text-2xl font-medium">Apps</Text>
|
||||||
|
<HStack className="mt-4 flex-wrap gap-4 md:gap-8">
|
||||||
|
{appList.map((app, idx) => (
|
||||||
|
<Button
|
||||||
|
key={idx}
|
||||||
|
className="flex-col flex-1 basis-1/3 md:max-w-[160px] h-32 bg-white"
|
||||||
|
labelClasses="text-sm text-center text-gray-900"
|
||||||
|
iconClassName="text-2xl mb-1 text-primary"
|
||||||
|
variant="outline"
|
||||||
|
icon={app.icon}
|
||||||
|
label={app.name}
|
||||||
|
onPress={() => {
|
||||||
|
if (app.path) {
|
||||||
|
navigation.navigate(("apps/" + app.path) as never);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.action) {
|
||||||
|
app.action();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</HStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(Apps);
|
@ -11,7 +11,7 @@ type Props = {
|
|||||||
|
|
||||||
const Summary = ({ data }: Props) => {
|
const Summary = ({ data }: Props) => {
|
||||||
return (
|
return (
|
||||||
<Box className="px-4 py-6 mt-4 bg-white border border-gray-100 rounded-lg">
|
<Box className="px-4 mt-4 py-6 bg-white border border-gray-100 rounded-lg">
|
||||||
<Text className="text-5xl">{dayjs(data?.date).format("HH:mm")}</Text>
|
<Text className="text-5xl">{dayjs(data?.date).format("HH:mm")}</Text>
|
||||||
<Text className="mt-2">
|
<Text className="mt-2">
|
||||||
{dayjs(data?.date).format("dddd, DD MMM YYYY")}
|
{dayjs(data?.date).format("dddd, DD MMM YYYY")}
|
||||||
|
@ -7,6 +7,9 @@ import Summary from "./_sections/Summary";
|
|||||||
import Storage from "./_sections/Storage";
|
import Storage from "./_sections/Storage";
|
||||||
import Container from "@ui/Container";
|
import Container from "@ui/Container";
|
||||||
import { useAuth } from "@/stores/authStore";
|
import { useAuth } from "@/stores/authStore";
|
||||||
|
import { HStack } from "@ui/Stack";
|
||||||
|
import Box from "@ui/Box";
|
||||||
|
import Apps from "./_sections/Apps";
|
||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
const { isLoggedIn } = useAuth();
|
const { isLoggedIn } = useAuth();
|
||||||
@ -22,12 +25,17 @@ const HomePage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container scrollable className="px-4 py-8 md:py-16">
|
<Container scrollable className="px-4 md:px-8 max-w-none py-8">
|
||||||
<Text className="text-2xl font-medium">Home Lab</Text>
|
<HStack className="items-start gap-8">
|
||||||
|
<Box className="flex-1 md:max-w-lg">
|
||||||
<Summary data={system} />
|
<Text className="text-2xl font-medium">Home Lab</Text>
|
||||||
<Performance data={system} />
|
<Summary data={system} />
|
||||||
<Storage data={system} />
|
<Apps className="md:hidden mt-6" />
|
||||||
|
<Performance data={system} />
|
||||||
|
<Storage data={system} />
|
||||||
|
</Box>
|
||||||
|
<Apps className="hidden md:flex md:flex-col md:flex-1" />
|
||||||
|
</HStack>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -60,6 +60,7 @@ interface ButtonProps
|
|||||||
labelClasses?: string;
|
labelClasses?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
|
iconClassName?: string;
|
||||||
children?: string;
|
children?: string;
|
||||||
}
|
}
|
||||||
function Button({
|
function Button({
|
||||||
@ -69,12 +70,15 @@ function Button({
|
|||||||
variant,
|
variant,
|
||||||
size,
|
size,
|
||||||
icon,
|
icon,
|
||||||
|
iconClassName,
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: ButtonProps) {
|
}: ButtonProps) {
|
||||||
const textStyles = cn(
|
const textStyles = buttonTextVariants({
|
||||||
buttonTextVariants({ variant, size, className: labelClasses })
|
variant,
|
||||||
);
|
size,
|
||||||
|
className: labelClasses,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
@ -86,10 +90,12 @@ function Button({
|
|||||||
}
|
}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{icon ? <Slot.View style={textStyles}>{icon}</Slot.View> : null}
|
{icon ? (
|
||||||
|
<Slot.View style={cn(textStyles, iconClassName)}>{icon}</Slot.View>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{label || children ? (
|
{label || children ? (
|
||||||
<Text style={textStyles}>{label || children}</Text>
|
<Text style={cn(textStyles)}>{label || children}</Text>
|
||||||
) : null}
|
) : null}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
);
|
);
|
||||||
|
14
src/stores/toastStore.ts
Normal file
14
src/stores/toastStore.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { createStore } from "zustand";
|
||||||
|
import { Toast, ToastOptions } from "react-native-toast-notifications";
|
||||||
|
|
||||||
|
export const toastStore = createStore<typeof Toast | null>(() => null);
|
||||||
|
|
||||||
|
export const showToast = (
|
||||||
|
message: string | JSX.Element,
|
||||||
|
toastOptions?: ToastOptions
|
||||||
|
) => {
|
||||||
|
const toast = toastStore.getState();
|
||||||
|
if (toast) {
|
||||||
|
toast.show(message, toastOptions);
|
||||||
|
}
|
||||||
|
};
|
20
yarn.lock
20
yarn.lock
@ -6406,6 +6406,11 @@ react-native-svg@14.1.0:
|
|||||||
css-select "^5.1.0"
|
css-select "^5.1.0"
|
||||||
css-tree "^1.1.3"
|
css-tree "^1.1.3"
|
||||||
|
|
||||||
|
react-native-toast-notifications@^3.4.0:
|
||||||
|
version "3.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-toast-notifications/-/react-native-toast-notifications-3.4.0.tgz#9f2c79ec80fba4af6b788ecddf704f94cf21dbc2"
|
||||||
|
integrity sha512-ZvB//jLhRiBRemtcH7vGP1maiKCikqtW4aDqo+QYvEIOcX0y3GrjxRayVAqI4oh0qJgd/26DkbM8COobj+0MEQ==
|
||||||
|
|
||||||
react-native-web@~0.19.6:
|
react-native-web@~0.19.6:
|
||||||
version "0.19.10"
|
version "0.19.10"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.19.10.tgz#5f7205f8909c0889bc89c9fde7c6e287defa7c63"
|
resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.19.10.tgz#5f7205f8909c0889bc89c9fde7c6e287defa7c63"
|
||||||
@ -7827,6 +7832,21 @@ xtend@~4.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
|
xterm-addon-attach@^0.9.0:
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xterm-addon-attach/-/xterm-addon-attach-0.9.0.tgz#dd18057f147a402de1df852c1de4c8f3b63b37be"
|
||||||
|
integrity sha512-NykWWOsobVZPPK3P9eFkItrnBK9Lw0f94uey5zhqIVB1bhswdVBfl+uziEzSOhe2h0rT9wD0wOeAYsdSXeavPw==
|
||||||
|
|
||||||
|
xterm-addon-fit@^0.8.0:
|
||||||
|
version "0.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz#48ca99015385141918f955ca7819e85f3691d35f"
|
||||||
|
integrity sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==
|
||||||
|
|
||||||
|
xterm@^5.3.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46"
|
||||||
|
integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==
|
||||||
|
|
||||||
y18n@^4.0.0:
|
y18n@^4.0.0:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user