summaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
authorrookisbusy <[email protected]>2023-04-07 20:43:19 +0800
committerrookisbusy <[email protected]>2023-04-07 20:43:19 +0800
commit2a3579e447459aaeb7998cf968f73c6ef7ff7a0b (patch)
tree5422a8cc8732066ae3d6ef2563b939d03087c20c /src/api
parente6852509670fa91fda6a2ef2375608a7b9a4b175 (diff)
feat: add memory chat
Diffstat (limited to 'src/api')
-rw-r--r--src/api/memory.ts119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/api/memory.ts b/src/api/memory.ts
new file mode 100644
index 0000000..b6614bb
--- /dev/null
+++ b/src/api/memory.ts
@@ -0,0 +1,119 @@
+import { ClashAPIConfig } from '~/types';
+
+import { buildWebSocketURL, getURLAndInit } from '../misc/request-helper';
+
+const endpoint = '/memory';
+const textDecoder = new TextDecoder('utf-8');
+
+const Size = 150;
+
+const memory = {
+ labels: Array(Size).fill(0),
+ inuse: Array(Size),
+ oslimit: Array(Size),
+
+ size: Size,
+ subscribers: [],
+ appendData(o: { inuse: number; oslimit: number }) {
+ this.inuse.shift();
+ this.oslimit.shift();
+ this.labels.shift();
+
+ const l = Date.now();
+ this.inuse.push(o.inuse);
+ this.oslimit.push(o.oslimit);
+ this.labels.push(l);
+
+ this.subscribers.forEach((f) => f(o));
+ },
+
+ subscribe(listener: (x: any) => void) {
+ this.subscribers.push(listener);
+ return () => {
+ const idx = this.subscribers.indexOf(listener);
+ this.subscribers.splice(idx, 1);
+ };
+ },
+};
+
+let fetched = false;
+let decoded = '';
+
+function parseAndAppend(x: string) {
+ memory.appendData(JSON.parse(x));
+}
+
+function pump(reader: ReadableStreamDefaultReader) {
+ return reader.read().then(({ done, value }) => {
+ const str = textDecoder.decode(value, { stream: !done });
+ decoded += str;
+
+ const splits = decoded.split('\n');
+
+ const lastSplit = splits[splits.length - 1];
+
+ for (let i = 0; i < splits.length - 1; i++) {
+ parseAndAppend(splits[i]);
+ }
+
+ if (done) {
+ parseAndAppend(lastSplit);
+ decoded = '';
+
+ // eslint-disable-next-line no-console
+ console.log('GET /memory streaming done');
+ fetched = false;
+ return;
+ } else {
+ decoded = lastSplit;
+ }
+ return pump(reader);
+ });
+}
+
+// 1 OPEN
+// other value CLOSED
+// similar to ws readyState but not the same
+// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
+let wsState: number;
+function fetchData(apiConfig: ClashAPIConfig) {
+ if (fetched || wsState === 1) return memory;
+ wsState = 1;
+ const url = buildWebSocketURL(apiConfig, endpoint);
+ const ws = new WebSocket(url);
+ ws.addEventListener('error', function (_ev) {
+ wsState = 3;
+ });
+ ws.addEventListener('close', function (_ev) {
+ wsState = 3;
+ fetchDataWithFetch(apiConfig);
+ });
+ ws.addEventListener('message', function (event) {
+ parseAndAppend(event.data);
+ });
+ return memory;
+}
+
+function fetchDataWithFetch(apiConfig: ClashAPIConfig) {
+ if (fetched) return memory;
+ fetched = true;
+ const { url, init } = getURLAndInit(apiConfig);
+ fetch(url + endpoint, init).then(
+ (response) => {
+ if (response.ok) {
+ const reader = response.body.getReader();
+ pump(reader);
+ } else {
+ fetched = false;
+ }
+ },
+ (err) => {
+ // eslint-disable-next-line no-console
+ console.log('fetch /memory error', err);
+ fetched = false;
+ }
+ );
+ return memory;
+}
+
+export { fetchData };