mirror of
https://github.com/ChronosX88/wind-vue.git
synced 2024-10-23 03:51:04 +00:00
Compare commits
No commits in common. "04f80c58339c1fafce822fac1bd2ee9a62bb8a05" and "ea3defeeb0edbb0e46353a5e89b50a421fa64678" have entirely different histories.
04f80c5833
...
ea3defeeb0
13
package-lock.json
generated
13
package-lock.json
generated
@ -9,8 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pinia": "^2.0.13",
|
"pinia": "^2.0.13",
|
||||||
"vue": "^3.2.33",
|
"vue": "^3.2.33"
|
||||||
"websocket-ts": "^1.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.1.0",
|
"@rushstack/eslint-patch": "^1.1.0",
|
||||||
@ -2822,11 +2821,6 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/websocket-ts": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/websocket-ts/-/websocket-ts-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-rm+S60J74Ckw5iizzgID12ju+OfaHAa6dhXhULIOrXkl0e05RzxfY42/vMStpz5jWL3iz9mkyjPcFUY1IgI0fw=="
|
|
||||||
},
|
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@ -4771,11 +4765,6 @@
|
|||||||
"@volar/vue-typescript": "0.34.12"
|
"@volar/vue-typescript": "0.34.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"websocket-ts": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/websocket-ts/-/websocket-ts-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-rm+S60J74Ckw5iizzgID12ju+OfaHAa6dhXhULIOrXkl0e05RzxfY42/vMStpz5jWL3iz9mkyjPcFUY1IgI0fw=="
|
|
||||||
},
|
|
||||||
"which": {
|
"which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pinia": "^2.0.13",
|
"pinia": "^2.0.13",
|
||||||
"vue": "^3.2.33",
|
"vue": "^3.2.33"
|
||||||
"websocket-ts": "^1.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.1.0",
|
"@rushstack/eslint-patch": "^1.1.0",
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import { createPinia } from "pinia";
|
import { createPinia } from "pinia";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import { NNTPClient } from "./nntp";
|
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
app.use(createPinia());
|
app.use(createPinia());
|
||||||
|
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|
||||||
const nntp = new NNTPClient("wss://nntp.antiope.link");
|
|
||||||
|
117
src/nntp.ts
117
src/nntp.ts
@ -1,117 +0,0 @@
|
|||||||
import { Websocket, WebsocketBuilder } from "websocket-ts";
|
|
||||||
import type { GroupInfo } from "./nntp/group_info";
|
|
||||||
import type {
|
|
||||||
CommandRequest,
|
|
||||||
CommandResponse,
|
|
||||||
NNTPCommand,
|
|
||||||
} from "./nntp/nntp_command";
|
|
||||||
import { Completer } from "./utils/completer";
|
|
||||||
import { Queue } from "./utils/queue";
|
|
||||||
|
|
||||||
export class NNTPClient {
|
|
||||||
private commandQueue = new Queue<NNTPCommand>();
|
|
||||||
private tempBuffer: Array<string> = [];
|
|
||||||
private ws: Websocket;
|
|
||||||
|
|
||||||
constructor(url: string) {
|
|
||||||
this.ws = new WebsocketBuilder(url)
|
|
||||||
.onMessage((_, evt) => {
|
|
||||||
if ((evt.data as string).startsWith("201")) {
|
|
||||||
console.debug("skipping welcome message");
|
|
||||||
}
|
|
||||||
let data = evt.data as string;
|
|
||||||
|
|
||||||
let responseLines = data.split("\r\n");
|
|
||||||
|
|
||||||
if (
|
|
||||||
(responseLines.length > 1 || this.tempBuffer.length != 0) &&
|
|
||||||
responseLines[responseLines.length - 1] != "."
|
|
||||||
) {
|
|
||||||
// if it's multiline response and it doesn't contain dot in the end
|
|
||||||
// then looks like we need to wait for next message to concatenate with current msg
|
|
||||||
this.tempBuffer.push(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.tempBuffer.length != 0) {
|
|
||||||
this.tempBuffer.push(data);
|
|
||||||
data = this.tempBuffer.join();
|
|
||||||
responseLines = data.split("\r\n");
|
|
||||||
responseLines.pop();
|
|
||||||
this.tempBuffer = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = this.commandQueue.dequeue();
|
|
||||||
const respCode = parseInt(responseLines[0].split(" ")[0]);
|
|
||||||
command?.response.complete({
|
|
||||||
responseCode: respCode,
|
|
||||||
lines: responseLines,
|
|
||||||
} as CommandResponse);
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async sendCommand(
|
|
||||||
command: string,
|
|
||||||
args: string[],
|
|
||||||
prepareResponse = false // remove unnecessary things in lines array
|
|
||||||
): Promise<CommandResponse> {
|
|
||||||
const cmd = {
|
|
||||||
request: { command: command, args: args } as CommandRequest,
|
|
||||||
response: new Completer<CommandResponse>(),
|
|
||||||
} as NNTPCommand;
|
|
||||||
|
|
||||||
this.commandQueue.enqueue(cmd);
|
|
||||||
if (args.length > 0) {
|
|
||||||
this.ws.send(`${command} ${args.join(" ")}\r\n`);
|
|
||||||
} else {
|
|
||||||
this.ws.send(`${command}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await cmd.response.promise;
|
|
||||||
if (prepareResponse) {
|
|
||||||
result.lines.shift();
|
|
||||||
result.lines.pop();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private completeGroupInfo(obj: Partial<GroupInfo>): GroupInfo {
|
|
||||||
return Object.assign(
|
|
||||||
{
|
|
||||||
name: "",
|
|
||||||
description: "",
|
|
||||||
lowWater: -1,
|
|
||||||
highWater: -1,
|
|
||||||
},
|
|
||||||
obj
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getNewsGroupList(): Promise<GroupInfo[]> {
|
|
||||||
const groupMap: Record<string, Partial<GroupInfo>> = {};
|
|
||||||
const [newsgroupsResponse, activeResponse]: CommandResponse[] =
|
|
||||||
await Promise.all([
|
|
||||||
this.sendCommand("LIST", ["NEWSGROUPS"], true),
|
|
||||||
this.sendCommand("LIST", ["ACTIVE"], true),
|
|
||||||
]);
|
|
||||||
|
|
||||||
newsgroupsResponse.lines.forEach((val: string) => {
|
|
||||||
const firstSpace = val.indexOf(" ");
|
|
||||||
const name = val.substring(0, firstSpace);
|
|
||||||
groupMap[name] = { description: val.substring(firstSpace + 1) };
|
|
||||||
});
|
|
||||||
|
|
||||||
activeResponse.lines.forEach((val: string) => {
|
|
||||||
const splitted = val.split(" ");
|
|
||||||
const [name, high, low]: string[] = splitted;
|
|
||||||
groupMap[name].highWater = Number(high);
|
|
||||||
groupMap[name].lowWater = Number(low);
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = Object.entries(groupMap).map(([, obj]) =>
|
|
||||||
this.completeGroupInfo(obj)
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
export interface GroupInfo {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
lowWater: number;
|
|
||||||
highWater: number;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import type { Completer } from "@/utils/completer";
|
|
||||||
|
|
||||||
export interface NNTPCommand {
|
|
||||||
request: CommandRequest;
|
|
||||||
response: Completer<CommandResponse>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommandRequest {
|
|
||||||
command: string;
|
|
||||||
args: Array<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommandResponse {
|
|
||||||
responseCode: number;
|
|
||||||
lines: Array<string>;
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
export class Completer<T> {
|
|
||||||
public readonly promise: Promise<T>;
|
|
||||||
public complete!: (value: (PromiseLike<T> | T)) => void;
|
|
||||||
private reject!: (reason?: any) => void;
|
|
||||||
|
|
||||||
public constructor() {
|
|
||||||
this.promise = new Promise<T>((resolve, reject) => {
|
|
||||||
this.complete = resolve;
|
|
||||||
this.reject = reject;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
export interface IQueue<T> {
|
|
||||||
enqueue(item: T): void;
|
|
||||||
dequeue(): T | undefined;
|
|
||||||
size(): number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Queue<T> implements IQueue<T> {
|
|
||||||
private storage: T[] = [];
|
|
||||||
|
|
||||||
constructor(private capacity: number = Infinity) {}
|
|
||||||
|
|
||||||
enqueue(item: T): void {
|
|
||||||
if (this.size() === this.capacity) {
|
|
||||||
throw Error("Queue has reached max capacity, you cannot add more items");
|
|
||||||
}
|
|
||||||
this.storage.push(item);
|
|
||||||
}
|
|
||||||
dequeue(): T | undefined {
|
|
||||||
return this.storage.shift();
|
|
||||||
}
|
|
||||||
size(): number {
|
|
||||||
return this.storage.length;
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,13 +5,12 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
}
|
||||||
"lib": ["es2021"]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.vite-config.json"
|
"path": "./tsconfig.vite-config.json"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user