From 5e8a3c88a200e0d043d3edd864834c50473bc5c5 Mon Sep 17 00:00:00 2001 From: Khairul Hidayat Date: Sat, 16 Mar 2024 10:14:17 +0700 Subject: [PATCH] feat: add terminal & wake pc --- backend/.env.example | 6 +- backend/bun.lockb | Bin 5383 -> 0 bytes backend/global.d.ts | 1 + backend/index.ts | 11 +- backend/lib/terminal.ts | 46 +++++ backend/package-lock.json | 165 ++++++++++++++++++ backend/package.json | 17 +- backend/routes/_routes.ts | 4 +- backend/routes/apps.ts | 22 +++ backend/websocket.ts | 28 +++ backend/yarn.lock | 257 ++++++++++++++++++++++++++++ package.json | 4 + src/app/_layout.tsx | 7 + src/app/apps/lib.ts | 13 ++ src/app/apps/terminal.tsx | 93 ++++++++++ src/app/index/_sections/Apps.tsx | 57 ++++++ src/app/index/_sections/Summary.tsx | 2 +- src/app/index/index.tsx | 20 ++- src/components/ui/Button.tsx | 16 +- src/stores/toastStore.ts | 14 ++ yarn.lock | 20 +++ 21 files changed, 782 insertions(+), 21 deletions(-) delete mode 100755 backend/bun.lockb create mode 100644 backend/global.d.ts create mode 100644 backend/lib/terminal.ts create mode 100644 backend/package-lock.json create mode 100644 backend/routes/apps.ts create mode 100644 backend/websocket.ts create mode 100644 backend/yarn.lock create mode 100644 src/app/apps/lib.ts create mode 100644 src/app/apps/terminal.tsx create mode 100644 src/app/index/_sections/Apps.tsx create mode 100644 src/stores/toastStore.ts diff --git a/backend/.env.example b/backend/.env.example index 0438ec4..495ee33 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,5 +1,9 @@ -# +# Config JWT_SECRET= AUTH_USERNAME= AUTH_PASSWORD= + +# Apps +PC_MAC_ADDR= +TERMINAL_SHELL= diff --git a/backend/bun.lockb b/backend/bun.lockb deleted file mode 100755 index 05773cdc61f9287e3a5451841ef3945b2852af98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5383 zcmeHLeNFs1Qjipps?|dL+SZyE5+D!^3PO);@wh}0aS=5@h>0N&lgBsJAn3Bk zDpjkpT`Z!NT3Ppi71!@pEh-g?v{s8&i=tJFQr&H}1{QYasqleTC!*6ao$|j)w_-vhhGh7l7{Xd>6yK^G%&*h76G~17|-cP+`{$ekp;X2b>b3@ zF^QK=C$V!u@cS%}XECacv<`+A(?%L6eh1G`7)J%j8~8llGuVf1xd3;Dad<47pg$P+ zMUKNTJ8q>%tnIjI-6xi}v`!CSek{i7ye#FKlWyT3yvB_=etvaE%J>gn+Wl(uQV)6Y z>K6vD`BEP7_gxh^`RcFC-Ootgb&E$zJHEQhP2hz*miZ-raz8ixLr?RlHHSiHv`m^-*{Xc)Wc&eFVd%J`TF>`R^3QxUsr@Dw@TW01-mhW64f4;qH(Xs~X{PusUgt5}PTW3GHp77B*+0xts zx8$5)m15|$SnKBfs?v!EUR2HgcDEg_eLfO&6>FK!cxT08-M!6kf3jXHa(~x!xnkc~ zuiSeLgM1S*?~8&5R45eT*NdaIHMMo8544?*is)DsdB$LUCTyW3%no-DF#>Qw#rP4q zwa4>k6*Rov+E!9iE!y33!5aR^ZQ#oRF>BpYsMa#RV`)lRVeXBNgZ>qT`wOJrg1_WF z_pjM<*Ui&IFX4NG*nz|$!V4xuy*~M788gj!mul&TZC)`JkC?^2cWWlsO}+YkI#Us_ zvr6=J)W(_h<0MB4isxkwm@g^J-Lqcw@ahMV!;-(re#Z`%sneeC`!NbuM;xjis&yaVL>snL&fJ4-rv*waQe-8Q!Gi^sY1!D zvIsj|`+OzP-tbq9>}m?BthpVbeO7s;q)1##|9Qo~Rd<9=jUL6?)rx&eebGN+<4@eX zyv*;+gGZ7w)soSIP37%*JC!pYer<<~d4`bSXp+0?)WuEfCpLQN+pgcbIOopotA*uX zrS3CbmUTtOtRL&-)_LH8YP(`i+1wqX!m3koTb86>d6NC!7oBiC7ZR+rN2b} zH<{lo&~1UO_|F4qFUOsgW>X@iGnyH-!9bDvii$RAG*i4~KHg?swuVlg`TG+ zo{)i)`chH_CFPOYm(->X{E6HSNHt8VRR{hu*nu4Mkh+-Etqy!R3?)?Fq=fL^ECoKhm z{&oD!A)P|_n1Dp1697T1fNx2E%C&e*R;tFFXws!JA#76uUqiT--b@=aGzkn1A1p%9 zKP2$cLYxlanxtlElR3-GXi{`W+ZPiZZCuPSspcSw#H7*c%#10EGNz{J%@l3YqM$^~ zird_PVzk)?U4kUhpriEWUId>|0x(aEiEUnoplB|e!}gAATLWOW1|UFuAV4pBVND>G zAQQ8((Z~9y*o-qmw++Q?^!0^fyBPcH037?Sm)iQOtR0W22VLO>RP zAZf-VU7}9Rs_g~Eei^{nckxE#5IVgZw8!H>9KXV0hz6n%cKTA^y@nHJ*Rv2Zg`Fuf vdUG#s2N`C-$S{j-(avsw4Le(?^ { + console.log(`App listening on http://${info.address}:${info.port}`); +}); + +createWsServer(server); diff --git a/backend/lib/terminal.ts b/backend/lib/terminal.ts new file mode 100644 index 0000000..d936bf2 --- /dev/null +++ b/backend/lib/terminal.ts @@ -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); + }); +}; diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..b665b1d --- /dev/null +++ b/backend/package-lock.json @@ -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" + } + } + } +} diff --git a/backend/package.json b/backend/package.json index d426a1f..117baf7 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,21 +3,28 @@ "module": "index.ts", "type": "module", "scripts": { - "dev": "bun run --watch index.ts", - "start": "bun run index.ts" + "dev": "tsx --watch index.ts", + "start": "tsx index.ts" }, "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": { "typescript": "^5.0.0" }, "dependencies": { + "@hono/node-server": "^1.8.2", "@hono/zod-validator": "^0.2.0", + "dotenv": "^16.4.5", "hono": "^4.1.0", "nanoid": "^5.0.6", + "node-pty": "^1.0.0", "systeminformation": "^5.22.2", + "wol": "^1.0.7", + "ws": "^8.16.0", "zod": "^3.22.4" } -} \ No newline at end of file +} diff --git a/backend/routes/_routes.ts b/backend/routes/_routes.ts index ef7e252..09c4916 100644 --- a/backend/routes/_routes.ts +++ b/backend/routes/_routes.ts @@ -2,13 +2,15 @@ import { Hono } from "hono"; import auth from "./auth"; import system from "./system"; import _process from "./process"; +import apps from "./apps"; import { authMiddleware } from "../lib/jwt"; const routes = new Hono() .route("/auth", auth) .use(authMiddleware) .route("/system", system) - .route("/process", _process); + .route("/process", _process) + .route("/apps", apps); export type AppType = typeof routes; diff --git a/backend/routes/apps.ts b/backend/routes/apps.ts new file mode 100644 index 0000000..d75f5d0 --- /dev/null +++ b/backend/routes/apps.ts @@ -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; diff --git a/backend/websocket.ts b/backend/websocket.ts new file mode 100644 index 0000000..41935ea --- /dev/null +++ b/backend/websocket.ts @@ -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; diff --git a/backend/yarn.lock b/backend/yarn.lock new file mode 100644 index 0000000..de3ed25 --- /dev/null +++ b/backend/yarn.lock @@ -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== diff --git a/package.json b/package.json index 62f2192..f418ca6 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,14 @@ "react-native-safe-area-context": "4.8.2", "react-native-screens": "~3.29.0", "react-native-svg": "14.1.0", + "react-native-toast-notifications": "^3.4.0", "react-native-web": "~0.19.6", "react-query": "^3.39.3", "twrnc": "^4.1.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", "zustand": "^4.5.2" }, diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index ec84d23..9d7b989 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -7,8 +7,10 @@ import { cn, tw } from "@/lib/utils"; import { useDeviceContext } from "twrnc"; import { StatusBar } from "expo-status-bar"; import { useSafeAreaInsets } from "react-native-safe-area-context"; +import Toast from "react-native-toast-notifications"; import { useStore } from "zustand"; import authStore from "@/stores/authStore"; +import { toastStore } from "@/stores/toastStore"; const RootLayout = () => { const insets = useSafeAreaInsets(); @@ -39,6 +41,11 @@ const RootLayout = () => { + { + toastStore.setState(ref); + }} + /> ); }; diff --git a/src/app/apps/lib.ts b/src/app/apps/lib.ts new file mode 100644 index 0000000..9b652e2 --- /dev/null +++ b/src/app/apps/lib.ts @@ -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" }); + } +}; diff --git a/src/app/apps/terminal.tsx b/src/app/apps/terminal.tsx new file mode 100644 index 0000000..dd0419f --- /dev/null +++ b/src/app/apps/terminal.tsx @@ -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(); + const fitRef = useRef(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 ( + + Only web platform suppported. + + ); + } + + return ( +
+ ); +}; + +export default TerminalPage; diff --git a/src/app/index/_sections/Apps.tsx b/src/app/index/_sections/Apps.tsx new file mode 100644 index 0000000..15fbddb --- /dev/null +++ b/src/app/index/_sections/Apps.tsx @@ -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; + +const Apps = (props: Props) => { + const navigation = useNavigation(); + + const appList = [ + { + name: "Terminal", + icon: , + path: "terminal", + }, + { + name: "PC Control", + icon: , + action: wakePcUp, + }, + ]; + + return ( + + Apps + + {appList.map((app, idx) => ( +