diff --git a/almeira-live b/almeira-live index 97a1992..07ecfa5 160000 --- a/almeira-live +++ b/almeira-live @@ -1 +1 @@ -Subproject commit 97a1992210872fe8eff0b3bce0a0a97db67c6eb4 +Subproject commit 07ecfa5268f8b17294a25d08ed82340479f4aaae diff --git a/almeira-web-deploy.zip b/almeira-web-deploy.zip index 6a4e792..03b9927 100644 Binary files a/almeira-web-deploy.zip and b/almeira-web-deploy.zip differ diff --git a/package-lock.json b/package-lock.json index 0722017..743b0f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "almeira-web", "version": "0.1.0", "dependencies": { + "@livekit/components-react": "^2.9.21", "@tiptap/extension-color": "^3.21.0", "@tiptap/extension-font-family": "^3.21.0", "@tiptap/extension-highlight": "^3.21.0", @@ -21,16 +22,18 @@ "@tiptap/starter-kit": "^3.21.0", "@types/dompurify": "^3.0.5", "@types/video.js": "^7.3.58", - "appwrite": "^22.4.1", + "appwrite": "^21.0.0", "axios": "^1.18.0", "cheerio": "^1.2.0", "clsx": "^2.1.1", "dompurify": "^3.3.3", "dotenv": "^17.4.2", "isomorphic-dompurify": "^2.20.0", + "livekit-client": "^2.20.0", + "livekit-server-sdk": "^2.16.0", "lucide-react": "^0.577.0", "next": "^16.2.6", - "node-appwrite": "^22.1.3", + "node-appwrite": "^21.0.0", "react": "19.2.3", "react-dom": "19.2.3", "string-similarity": "^4.0.4", @@ -385,6 +388,12 @@ "specificity": "bin/cli.js" } }, + "node_modules/@bufbuild/protobuf": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.1.tgz", + "integrity": "sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", @@ -718,7 +727,6 @@ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", "license": "MIT", - "optional": true, "dependencies": { "@floating-ui/utils": "^0.2.11" } @@ -738,8 +746,7 @@ "version": "0.2.11", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/@humanfs/core": { "version": "0.19.1", @@ -1309,6 +1316,77 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@livekit/components-core": { + "version": "0.12.13", + "resolved": "https://registry.npmjs.org/@livekit/components-core/-/components-core-0.12.13.tgz", + "integrity": "sha512-DQmi84afHoHjZ62wm8y+XPNIDHTwFHAltjd3lmyXj8UZHOY7wcza4vFt1xnghJOD5wLRY58L1dkAgAw59MgWvw==", + "license": "Apache-2.0", + "dependencies": { + "@floating-ui/dom": "1.7.4", + "loglevel": "1.9.1", + "rxjs": "7.8.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "livekit-client": "^2.17.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@livekit/components-core/node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@livekit/components-react": { + "version": "2.9.21", + "resolved": "https://registry.npmjs.org/@livekit/components-react/-/components-react-2.9.21.tgz", + "integrity": "sha512-6hU9VucJJL+gAhilNGe4MBCDCZVk64qyjP9Ck86krvOIdVU76WeWksddg1MYUP10AlUwwrfD7davz41pJTcMJw==", + "license": "Apache-2.0", + "dependencies": { + "@livekit/components-core": "0.12.13", + "clsx": "2.1.1", + "events": "^3.3.0", + "jose": "^6.0.12", + "usehooks-ts": "3.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@livekit/krisp-noise-filter": "^0.2.12 || ^0.3.0", + "livekit-client": "^2.18.2", + "react": ">=18", + "react-dom": ">=18", + "tslib": "^2.6.2" + }, + "peerDependenciesMeta": { + "@livekit/krisp-noise-filter": { + "optional": true + } + } + }, + "node_modules/@livekit/mutex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@livekit/mutex/-/mutex-1.1.1.tgz", + "integrity": "sha512-EsshAucklmpuUAfkABPxJNhzj9v2sG7JuzFDL4ML1oJQSV14sqrpTYnsaOudMAw9yOaW53NU3QQTlUQoRs4czw==", + "license": "Apache-2.0" + }, + "node_modules/@livekit/protocol": { + "version": "1.46.6", + "resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.46.6.tgz", + "integrity": "sha512-upzlHP1vi/kZ/QqALZTFskQ0ifqc2f15RKucHYOsIHJsaXvEYanG75mAb7o+Yomfs4XhQ4BaRsdY+TFHXpaqrg==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "^1.10.0" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -2343,6 +2421,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/dom-mediacapture-record": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.22.tgz", + "integrity": "sha512-mUMZLK3NvwRLcAAT9qmcK+9p7tpU2FHdDsntR3YI4+GY88XrgG4XiE7u1Q2LAN2/FZOz/tdMDC3GQCR4T8nFuw==", + "license": "MIT", + "peer": true + }, "node_modules/@types/dompurify": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", @@ -3140,13 +3225,10 @@ } }, "node_modules/appwrite": { - "version": "22.4.1", - "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-22.4.1.tgz", - "integrity": "sha512-NlEgUvSo7A1m+TEHZ8zlWUIuCDtNg3VN6Mbxd6r+1618CiWokig630FpeKQ9/WD5Io92QxuTV/dVzBLPlQghqg==", - "license": "BSD-3-Clause", - "dependencies": { - "json-bigint": "1.0.0" - } + "version": "21.5.0", + "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-21.5.0.tgz", + "integrity": "sha512-643bMRZVYXMluXvSXbdaLAi9qqTJLWbVGguKH4vH6IdKHur6gGIirhCOqAEt33pV4TOFJ55VBu8c/+Ft1ke2SA==", + "license": "BSD-3-Clause" }, "node_modules/argparse": { "version": "2.0.1", @@ -3485,15 +3567,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -3617,6 +3690,36 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-9.1.3.tgz", + "integrity": "sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==", + "license": "MIT", + "dependencies": { + "camelcase": "^8.0.0", + "map-obj": "5.0.0", + "quick-lru": "^6.1.1", + "type-fest": "^4.3.2" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001778", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", @@ -4828,6 +4931,15 @@ "node": ">=0.10.0" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5951,6 +6063,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6034,15 +6155,6 @@ "node": ">=6" } }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -6423,6 +6535,72 @@ "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==", "license": "MIT" }, + "node_modules/livekit-client": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.20.0.tgz", + "integrity": "sha512-RIJcpvBmOmwz3jTj3rmdY6Dzr55HrhcaJjMgY+HSmoEM+yIRyA40m7r8UKv0hnZWM3z/AYhP1q8C8ciz5UWFKQ==", + "license": "Apache-2.0", + "dependencies": { + "@livekit/mutex": "1.1.1", + "@livekit/protocol": "1.46.6", + "events": "^3.3.0", + "jose": "^6.1.0", + "loglevel": "^1.9.2", + "sdp-transform": "^2.15.0", + "tslib": "2.8.1", + "typed-emitter": "^2.1.0", + "webrtc-adapter": "9.0.5" + }, + "peerDependencies": { + "@types/dom-mediacapture-record": "^1" + } + }, + "node_modules/livekit-client/node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/livekit-server-sdk": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/livekit-server-sdk/-/livekit-server-sdk-2.16.0.tgz", + "integrity": "sha512-Myr90TWbqUujvPpo0N8JS+gtY6jVKgbIRx688cVSL3mp18IVAwhDWyRzNloQBcimHg24zz1JjERTiwOw0wuFIg==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "^1.10.1", + "@livekit/protocol": "^1.48.0", + "camelcase-keys": "^9.0.0", + "jose": "^5.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/livekit-server-sdk/node_modules/@livekit/protocol": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.49.0.tgz", + "integrity": "sha512-TCmihwjHBWAUdNrqvmrN/K59r7vDnPIy3OEaE3p0g4FvdcQTiLynmFC3igR25Q2fnifahhAJjnnfSCtWUK7kYg==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "^1.10.0" + } + }, + "node_modules/livekit-server-sdk/node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6439,6 +6617,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6446,6 +6630,19 @@ "dev": true, "license": "MIT" }, + "node_modules/loglevel": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -6499,6 +6696,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/map-obj": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.0.tgz", + "integrity": "sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/markdown-it": { "version": "14.3.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", @@ -6757,12 +6966,11 @@ } }, "node_modules/node-appwrite": { - "version": "22.1.3", - "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-22.1.3.tgz", - "integrity": "sha512-pXxvojqgYY3HAiJAj+P6xb/CXVNsMUd+JXVOLrCCDzqZmpKeMF6OEt4mAoDEyKTnRaixUDHlPrdh7y1G/w3sbw==", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-21.1.0.tgz", + "integrity": "sha512-HRK5BzN19vgvaH/EeNsigK24t4ngJ1AoiltK5JtahxP6uyMRztzkD8cXP+z9jj/xOjz7ySfQ9YypNyhNr6zVkA==", "license": "BSD-3-Clause", "dependencies": { - "json-bigint": "1.0.0", "node-fetch-native-with-agent": "1.7.2" } }, @@ -7494,6 +7702,18 @@ ], "license": "MIT" }, + "node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", @@ -7657,6 +7877,15 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -7736,6 +7965,21 @@ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, + "node_modules/sdp": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.2.tgz", + "integrity": "sha512-xZocWwfyp4hkbN4hLWxMjmv2Q8aNa9MhmOZ7L9aCZPT+dZsgRr6wZRrSYE3HTdyk/2pZKPSgqI7ns7Een1xMSA==", + "license": "MIT" + }, + "node_modules/sdp-transform": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.15.0.tgz", + "integrity": "sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==", + "license": "MIT", + "bin": { + "sdp-verify": "checker.js" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -8372,6 +8616,18 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -8450,6 +8706,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "license": "MIT", + "optionalDependencies": { + "rxjs": "*" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -8614,6 +8879,21 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/usehooks-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.1.tgz", + "integrity": "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==", + "license": "MIT", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/video.js": { "version": "8.23.7", "resolved": "https://registry.npmjs.org/video.js/-/video.js-8.23.7.tgz", @@ -8692,6 +8972,19 @@ "node": ">=20" } }, + "node_modules/webrtc-adapter": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-9.0.5.tgz", + "integrity": "sha512-U9vjByy/sK2OMXu5mmfuZFKTMIUQe34c0JXRO+oDrxJTsntdYT2iIFwYMOV7HhMTuktcZLGf2W1N/OcSf9ssWg==", + "license": "BSD-3-Clause", + "dependencies": { + "sdp": "^3.2.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", diff --git a/package.json b/package.json index f8b7191..fb64689 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "check-config": "node --env-file=.env.local check_config.js" }, "dependencies": { + "@livekit/components-react": "^2.9.21", "@tiptap/extension-color": "^3.21.0", "@tiptap/extension-font-family": "^3.21.0", "@tiptap/extension-highlight": "^3.21.0", @@ -23,16 +24,18 @@ "@tiptap/starter-kit": "^3.21.0", "@types/dompurify": "^3.0.5", "@types/video.js": "^7.3.58", - "appwrite": "^22.4.1", + "appwrite": "^21.0.0", "axios": "^1.18.0", "cheerio": "^1.2.0", "clsx": "^2.1.1", "dompurify": "^3.3.3", "dotenv": "^17.4.2", "isomorphic-dompurify": "^2.20.0", + "livekit-client": "^2.20.0", + "livekit-server-sdk": "^2.16.0", "lucide-react": "^0.577.0", "next": "^16.2.6", - "node-appwrite": "^22.1.3", + "node-appwrite": "^21.0.0", "react": "19.2.3", "react-dom": "19.2.3", "string-similarity": "^4.0.4", diff --git a/src/app/actions/restreamer.ts b/src/app/actions/restreamer.ts index fc09638..b798cf3 100644 --- a/src/app/actions/restreamer.ts +++ b/src/app/actions/restreamer.ts @@ -59,28 +59,34 @@ export type RestreamServerInfo = { viewer_count: number; }; -// ── Información del servidor ─────────────────────────────────────────────────── export async function getRestreamServerInfo(): Promise<{ success: boolean; data?: RestreamServerInfo; error?: string }> { try { - const data = await restreamFetch('/api/v3/metrics/process/restreamer-ui:ingest:*'); + const processes: any[] = await restreamFetch('/api/v3/process'); + const ingest = processes.find((p: any) => p.id?.includes('ingest') || p.metadata?.['restreamer-ui']?.meta?.type === 'ingest' || p.metadata?.['restreamer-ui']?.type === 'ingest'); + + let active = false; + let viewers = 0; + let uptime = 0; + + if (ingest) { + const metrics = await restreamFetch(`/api/v3/metrics/process/${ingest.id}`).catch(() => null); + active = ingest.state?.exec === 'running'; + viewers = metrics?.viewers ?? 0; + uptime = metrics?.uptime ?? ingest.state?.uptime ?? 0; + } + return { success: true, data: { - version: data?.version ?? 'desconocida', - uptime_seconds: data?.uptime ?? 0, + version: '16+', + uptime_seconds: uptime, is_connected: true, - active_ingest: data?.state === 'running', - viewer_count: data?.viewers ?? 0, + active_ingest: active, + viewer_count: viewers, } }; } catch (error: any) { - // Si falla el endpoint de metrics, devolvemos status básico - try { - await restreamFetch('/api/v3/config/active'); - return { success: true, data: { version: '-', uptime_seconds: 0, is_connected: true, active_ingest: false, viewer_count: 0 } }; - } catch { - return { success: false, error: error.message }; - } + return { success: false, error: error.message }; } } @@ -115,6 +121,10 @@ export async function createRestreamOutput(params: { platform: string; }): Promise<{ success: boolean; id?: string; error?: string }> { try { + const processes: any[] = await restreamFetch('/api/v3/process'); + const ingest = processes.find((p: any) => p.id?.includes('ingest') || p.metadata?.['restreamer-ui']?.meta?.type === 'ingest' || p.metadata?.['restreamer-ui']?.type === 'ingest'); + const reference = ingest?.id || 'restreamer-ui:ingest:*'; + const address = `${params.rtmpUrl.replace(/\/$/, '')}/${params.streamKey}`; const id = `restreamer-ui:egress:rtmp:${Date.now()}`; @@ -122,7 +132,7 @@ export async function createRestreamOutput(params: { method: 'POST', body: JSON.stringify({ id, - reference: 'restreamer-ui:ingest:*', + reference: reference, config: { id, reconnect: true, @@ -185,7 +195,7 @@ export async function deleteRestreamOutput(id: string): Promise<{ success: boole export async function getIngestStats(): Promise<{ success: boolean; data?: { bitrate: number; fps: number; state: string }; error?: string }> { try { const processes: any[] = await restreamFetch('/api/v3/process'); - const ingest = processes.find((p: any) => p.id?.includes('ingest')); + const ingest = processes.find((p: any) => p.id?.includes('ingest') || p.metadata?.['restreamer-ui']?.meta?.type === 'ingest' || p.metadata?.['restreamer-ui']?.type === 'ingest'); return { success: true, data: { diff --git a/src/app/api/livekit/token/route.ts b/src/app/api/livekit/token/route.ts new file mode 100644 index 0000000..41f3ff8 --- /dev/null +++ b/src/app/api/livekit/token/route.ts @@ -0,0 +1,24 @@ +import { AccessToken } from 'livekit-server-sdk'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(req: NextRequest) { + const room = req.nextUrl.searchParams.get('room'); + const identity = req.nextUrl.searchParams.get('identity') || `guest-${Math.floor(Math.random() * 1000)}`; + + if (!room) { + return NextResponse.json({ error: 'Missing "room" query parameter' }, { status: 400 }); + } + + // Las variables de entorno de LiveKit + // Si no existen, podemos usar unas por defecto para el desarrollo local, pero en prod deben estar configuradas + const apiKey = process.env.LIVEKIT_API_KEY || 'devkey'; + const apiSecret = process.env.LIVEKIT_API_SECRET || 'secret'; + + const at = new AccessToken(apiKey, apiSecret, { + identity: identity, + }); + + at.addGrant({ roomJoin: true, room: room }); + + return NextResponse.json({ token: await at.toJwt() }); +} diff --git a/test-pagination.js b/test-pagination.js new file mode 100644 index 0000000..9c40763 --- /dev/null +++ b/test-pagination.js @@ -0,0 +1,42 @@ +import { Client, Databases, Query } from "node-appwrite"; + +const client = new Client() + .setEndpoint('https://appwrite.almeira.com.co/v1') + .setProject('69b84df5003bd8957c5c') + .setKey('standard_ee81a4ca6784c0fa6a23abab0a983dbba9cd0c2d8e211a00c759d9801f5d8e27b43a605bfa373e13106da87acfc7673608f0ffb0eb04e10cae3dae93d2404bfda8676f96e3fcfd6bd642f6e12d69367916b796c54e98e2e4c4bc0b7a27aa42ba5048582fcbd45b1365b51c5014cb939c1853923c418048ff0e39c2237065da3c'); + +const databases = new Databases(client); + +async function run() { + try { + const typeRes = await databases.listDocuments('almeira', 'publication_types', [ + Query.equal('codigo', 'NOTICIA') + ]); + const typeId = typeRes.documents[0].$id; + + const queriesAdmin = [ + Query.equal('tipo_publicacion_id', typeId), + Query.orderDesc('fecha_publicacion'), + Query.limit(12), + Query.offset(0), + Query.equal('estado', ['Publicado', 'borrador']) + ]; + + const queriesUser = [ + Query.equal('tipo_publicacion_id', typeId), + Query.orderDesc('fecha_publicacion'), + Query.limit(12), + Query.offset(0), + Query.equal('estado', 'Publicado') + ]; + + const resAdmin = await databases.listDocuments('almeira', 'posts', queriesAdmin); + console.log('Admin total:', resAdmin.total); + + const resUser = await databases.listDocuments('almeira', 'posts', queriesUser); + console.log('User total:', resUser.total); + } catch (e) { + console.error(e); + } +} +run();