diff --git a/flake.nix b/flake.nix index 0446250..ca9c993 100644 --- a/flake.nix +++ b/flake.nix @@ -14,10 +14,15 @@ devShells = forEachSupportedSystem ({ pkgs }: { default = pkgs.mkShell { packages = with pkgs; [ + # Frontend bun sqlite sqls sql-studio + # Backend + dotnetCorePackages.sdk_9_0 + dotnetCorePackages.aspnetcore_9_0 + nuget # LSP typescript-language-server diff --git a/server/common.test.ts b/server/common.test.ts deleted file mode 100644 index 1a513d0..0000000 --- a/server/common.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { type } from "./common" -import _ from "lodash" -import { expect, test } from "bun:test" - -const CYCLES = 10000 -test("Test Integer and Unsigned Integer", () => { - for (let i = 0; i < CYCLES; i++) { - - // Unsigned Integer - expect(type.UInteger.safeParse(_.random(0, Math.pow(2, 53), false)).success).toBeTrue() - expect(type.UInt8.safeParse(_.random(0, Math.pow(2, 8) - 1, false)).success).toBeTrue() - expect(type.UInt8.safeParse(_.random(Math.pow(2, 8), Math.pow(2, 53), false)).success).toBeFalse() - expect(type.UInt16.safeParse(_.random(0, Math.pow(2, 16) - 1, false)).success).toBeTrue() - expect(type.UInt16.safeParse(_.random(Math.pow(2, 16), Math.pow(2, 53), false)).success).toBeFalse() - expect(type.UInt32.safeParse(_.random(0, Math.pow(2, 32) - 1, false)).success).toBeTrue() - expect(type.UInt32.safeParse(_.random(Math.pow(2, 32), Math.pow(2, 53), false)).success).toBeFalse() - - // Integer - expect(type.Integer.safeParse(_.random(-Math.pow(2, 52), Math.pow(2, 52) - 1, false)).success).toBeTrue() - expect(type.Int8.safeParse(_.random(-Math.pow(2, 7), Math.pow(2, 7) - 1, false)).success).toBeTrue() - expect(type.Int8.safeParse(_.random(Math.pow(2, 7), Math.pow(2, 52), false)).success).toBeFalse() - expect(type.Int8.safeParse(_.random(-Math.pow(2, 52), -Math.pow(2, 7) - 1, false)).success).toBeFalse() - expect(type.Int16.safeParse(_.random(-Math.pow(2, 15), Math.pow(2, 15) - 1, false)).success).toBeTrue() - expect(type.Int16.safeParse(_.random(Math.pow(2, 15), Math.pow(2, 52), false)).success).toBeFalse() - expect(type.Int16.safeParse(_.random(-Math.pow(2, 52), -Math.pow(2, 15) - 1, false)).success).toBeFalse() - expect(type.Int32.safeParse(_.random(-Math.pow(2, 31), Math.pow(2, 31) - 1, false)).success).toBeTrue() - expect(type.Int32.safeParse(_.random(Math.pow(2, 31), Math.pow(2, 52), false)).success).toBeFalse() - expect(type.Int32.safeParse(_.random(-Math.pow(2, 52), -Math.pow(2, 31) - 1, false)).success).toBeFalse() - } -}) - -test("Test Number Processor Function", () => { - // Convert Number to Uint8Array - expect(type.numberToBytes(0xFF, 1).unwrap()[0]).toBe(255) - expect(type.numberToBytes(0xAAAA, 2).unwrap()).toEqual(new Uint8Array([0xAA, 0xAA])) - expect(type.numberToBytes(0x12345678, 4).unwrap()).toEqual(new Uint8Array([0x78, 0x56, 0x34, 0x12])) - expect(type.numberToBytes(0x12345678, 4, true).unwrap()).toEqual(new Uint8Array([0x12, 0x34, 0x56, 0x78])) - - // Convert Uint8Array to Number - expect(type.bytesToNumber(new Uint8Array([0xFF]))).toBe(255) - expect(type.bytesToNumber(new Uint8Array([0xAA, 0xAA]))).toEqual(0xAAAA) - expect(type.bytesToNumber(new Uint8Array([0x78, 0x56, 0x34, 0x12]))).toEqual(0x12345678) - expect(type.bytesToNumber(new Uint8Array([0x12, 0x34, 0x56, 0x78]), true)).toEqual(0x12345678) - - - // Number Match - for (let i = 0; i < CYCLES; i++) { - const num1 = _.random(CYCLES / 2, false) - const num2 = _.random(CYCLES / 2, false) - - expect(type.numberMatch(num1, num2)).toBe((num1 & num2) === num2 ? true : false) - expect(type.numberMatch(num1, num2, "True", "False")).toBe((num1 & num2) === num2 ? "True" : "False") - } - - // Number Set, Unset, Toggle and Get Bit - expect(type.numberSetBit(0, 5)).toBe(0b10000) - expect(type.numberUnsetBit(0b1111, 3)).toBe(0b1011) - expect(type.numberToggleBit(0b1010, 3)).toBe(0b1110) - expect(type.numberBit(0b1100, 2)).toBe(0) - - // Get High / Low Bits Num - expect(type.numberHighBitsNum(0xFF).unwrap()).toBe(8) - expect(type.numberHighBitsNum(0xAA).unwrap()).toBe(4) - expect(type.numberLowBitsNum(0xFF, 8).unwrap()).toBe(0) - expect(type.numberLowBitsNum(0xAA, 8).unwrap()).toBe(4) -}) diff --git a/server/common.ts b/server/common.ts deleted file mode 100644 index f8b5d9d..0000000 --- a/server/common.ts +++ /dev/null @@ -1,191 +0,0 @@ -import _ from "lodash" -import { Option, Some, None, Result, Ok, Err } from "ts-results-es"; -import { z } from "zod"; - -export function UNUSED(_: unknown): void { } - -export namespace type { - - const NUMBER_MAX_LENGTH = 32 - - - export const ErrorTypeSchema = z.union([ - z.literal("Not Integer"), - z.literal("Not Unsigned Integer"), - z.literal("Not 32Bits Integer") - ]) - - export type ErrorType = z.infer - - export const Integer = z.number().int() - export const UInteger = z.number().int().nonnegative() - - export const UInt8 = UInteger.lt(Math.pow(2, 8)) - export const UInt16 = UInteger.lt(Math.pow(2, 16)) - export const UInt32 = UInteger.lt(Math.pow(2, 32)) - - export const Int8 = Integer.lt(Math.pow(2, 7)).gte(-Math.pow(2, 8)) - export const Int16 = Integer.lt(Math.pow(2, 15)).gte(-Math.pow(2, 16)) - export const Int32 = Integer.lt(Math.pow(2, 31)).gte(-Math.pow(2, 32)) - - export function numberToBytes(num: number, bytesLength: number, isRightHigh: boolean = false) - : Result { - // Check Integer - if (!Int32.safeParse(num).success && !UInt32.lte(32).safeParse(bytesLength).success) { - console.error(`Number : ${num}, 2 ^ 31 = ${2 ^ 31}`) - console.error(Int32.safeParse(num).error?.message) - return new Err("Not 32Bits Integer") - } - - var array = new Uint8Array(bytesLength) - - if (isRightHigh) { - for (let i = 0; i < bytesLength; i++) { - array[bytesLength - 1 - i] = ((num >> (i << 3)) & 0xFF) - } - } else { - for (let i = 0; i < bytesLength; i++) { - array[i] = ((num >> (i << 3)) & 0xFF) - } - } - - return new Ok(array) - } - - export function bytesToNumber(bytes: Uint8Array, isRightHigh: boolean = false): number { - let num = 0 - const len = bytes.length - if (isRightHigh) { - for (let i = 0; i < len; i++) { - num += bytes[len - 1 - i] << (i << 3) - } - } else { - for (let i = 0; i < len; i++) { - num += bytes[i] << (i << 3) - } - } - return num - } - - - export function numberMatch( - srcNum: number, - destNum: number - ): boolean; - - export function numberMatch( - srcNum: number, - destNum: number, - True: T, - False: T - ): T; - - export function numberMatch( - srcNum: number, - destNum: number, - True: T = true as T, - False: T = false as T - ): T { - const ret = (srcNum & destNum) === destNum; - return ret ? True : False; - } - - export function numberMatchEnum() { - - } - - export function numberSetBit(num: number, loc: number): number { - return num | (1 << (loc - 1)) - } - - export function numberUnsetBit(num: number, loc: number): number { - return num & ~(1 << (loc - 1)) - } - - export function numberToggleBit(num: number, loc: number): number { - return num ^ (1 << (loc - 1)) - } - - export function numberBit(num: number, loc: number): number { - return (num >> (loc - 1)) & 1 - } - - export function numberHighBitsNum(num: number, maxLen: number = NUMBER_MAX_LENGTH): Result { - if (!Int32.safeParse(num).success && maxLen > 32) { - return new Err("Not 32Bits Integer") - } - - let cnt = 0 - for (let i = 0; i < maxLen; i++) { - if (num & (1 << i)) { - cnt++ - } - } - return new Ok(cnt) - } - - export function numberLowBitsNum(num: number, maxLen: number): Result { - if (!Int32.safeParse(num).success && maxLen > 32) { - return new Err("Not 32Bits Integer") - } - - let cnt = 0 - for (let i = 0; i < maxLen; i++) { - if (!(num & (1 << i))) { - cnt++ - } - } - return new Ok(cnt) - } - - export function isStringArray(obj: any): obj is Array { - return z.string().array().safeParse(obj).success - } - - export function isNumberArray(obj: any): obj is Array { - return z.number().array().safeParse(obj).success - } -} - -export namespace fun { - - export function randomFromArray(array: Array) { - return array[_.random(0, array.length - 1, false)] - } - - export function sqlConditionFromArray( - columnName: string, - array: Array, - type: "AND" | "OR" - ): Option { - let condition: string = "" - const len = array.length - if (len == 0) { - return None - } - for (let i = 0; i < len; i++) { - condition.concat(`${columnName}=${array[i]}`) - if (i != len - 1) { - condition.concat(` ${type} `) - } - } - - return new Some(condition) - } - - export function sqlConditionFromString( - columnName: string, - str: string, - type: "AND" | "OR", - delimiter: string = " " - ): Option { - if (str.length == 0) { - return None - } - - const array = str.split(delimiter) - return sqlConditionFromArray(columnName, array, type) - } -} - - diff --git a/server/database.test.ts b/server/database.test.ts deleted file mode 100644 index 1e85dc1..0000000 --- a/server/database.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { test, expect } from "bun:test" -import * as db from "./database.ts" -import _, { rearg } from "lodash" -import { None, Ok, Option, Some } from "ts-results-es" - -// Test basic function for database -test("DataBase", () => { - const allTables = db.allTables() - expect(allTables).toBeArray() - expect(allTables).toEqual(["Users", "Boards"]) - expect(db.BoardTable.countAll()).toBe(0) - expect(db.UserTable.countAll()).toBe(0) -}) - -// Test Boards table function -const boardsNumber = 10 -const rooms = ["A1", "A1", "A1", "A1", "A1", "A2", "A2", "A2", "A2", "A2"] -test("Find something empty", () => { - const findEmptyByID = db.BoardTable.find(_.random(0, boardsNumber)) - expect(findEmptyByID).toEqual(Ok(None)) - const findEmptyByName = db.BoardTable.find("Hello", "World") - expect(findEmptyByName).toEqual(Ok(None)) -}) - -test("Add some boards", () => { - const boardsArray: Array = [] - for (let i = 0; i < boardsNumber; i++) { - boardsArray.push({ - id: i, - name: `Board ${i}`, - room: rooms[i], - ipv4: `192.168.172.${i}`, - port: i, - }) - } - const retAdd = db.BoardTable.addFromArray(boardsArray) - const isAddOk = retAdd.isOk() - expect(isAddOk).toBeTrue() -}) - -test("Get boards table column unique element", () => { - expect(db.BoardTable.rooms().unwrap()).toEqual(["A1", "A2"]) -}) - -test("Find something from boards table", () => { - expect(db.BoardTable.find(1)).toEqual(Ok(Some({ - id: 1, - name: `Board ${1}`, - room: rooms[1], - ipv4: `192.168.172.${1}`, - port: 1, - } as db.Board))) - expect(db.BoardTable.find("Board 3", "A1")).toEqual(Ok(Some({ - id: 3, - name: `Board ${3}`, - room: rooms[3], - ipv4: `192.168.172.${3}`, - port: 3, - } as db.Board))) -}) - -test("Get count of elements from boards table", () => { - expect(db.BoardTable.countByName("Board 1")).toBe(1) - expect(db.BoardTable.countByRoom("A1")).toBe(5) -}) diff --git a/server/database.ts b/server/database.ts deleted file mode 100644 index 8bc507e..0000000 --- a/server/database.ts +++ /dev/null @@ -1,478 +0,0 @@ -import { Database, type Changes } from "bun:sqlite"; -import _ from "lodash"; -import { Ok, Err, Result, None, Some, Option } from "ts-results-es"; -import { z } from "zod"; -import { fun, type } from "./common"; - -const db = new Database("lab.sqlite", { strict: true }) -initDB(db); - -// Error Type -const BoardErrorTypeSchema = z.union([ - z.literal("Wrong Type"), - z.literal("No Such Board(s)"), - z.literal("ID Conflict"), - z.literal("Name Conflict") -]) - -export type BoardErrorType = z.infer - -const boardSchema = z.object({ - id: z.number().nonnegative(), - name: z.string(), - room: z.string(), - ipv4: z.string().ip({ version: "v4" }), - ipv6: z.string().ip({ version: "v6" }), - port: z.number().nonnegative().lte(65535), - cmdID: z.number().nonnegative() -}).partial({ - ipv6: true, - cmdID: true -}) - -export type Board = z.infer -export type BoardColumn = keyof Board - -export function isBoard(obj: any): obj is Board { - return boardSchema.safeParse(obj).success -} - -export function isBoardArray(obj: any): obj is Array { - return boardSchema.array().safeParse(obj).success -} - -export function isBoardColumn(obj: any): obj is BoardColumn { - return boardSchema.keyof().safeParse(obj).success -} - -const userSchema = z.object({ - id: z.number().nonnegative(), - name: z.string(), - password: z.string(), - boardID: z.number(), -}).partial({ - boardID: true, -}) - -export type User = z.infer -export type UserColumn = keyof User - -export function isUser(obj: any): obj is User { - return userSchema.safeParse(obj).success -} - -export function isUserArray(obj: any): obj is Array { - return userSchema.array().safeParse(obj).success -} - -export function isUserColumn(obj: any): obj is UserColumn { - return userSchema.keyof().safeParse(obj).success -} - -function initDB(db: Database) { - const tables = allTables() - - if (!tables.includes("Users")) { - db.query(` - CREATE TABLE Users( - id INT PRIMARY KEY NOT NULL, - name TEXT NOT NULL, - password TEXT NOT NULL - ); - `).run(); - } - - if (!tables.includes("Boards")) - db.query(` - CREATE TABLE Boards( - id INT PRIMARY KEY NOT NULL, - name TEXT NOT NULL, - room TEXT NOT NULL, - ipv4 CHAR(16) NOT NULL, - ipv6 CHAR(46) , - port INT NOT NULL - ) - `).run(); -} - -export function allTables(): Array { - const query = db.query(`SELECT name FROM sqlite_master WHERE type='table'`) - var tables = new Array() - // Flaten array - for (const item of query.values()) { - tables.push(item[0]) - } - - query.finalize() - return tables -} - -export function tableColumnName(table: string): Array { - const query = db.query(`PRAGMA table_info(${table})`) - - var columnName = new Array() - for (const column of query.values()) { - columnName.push(column[1]) - } - - return columnName -} - -export namespace BoardTable { - - export function add(board: Board): Result { - if (!isBoard(board)) { - return new Err("Wrong Type") - } - - // Ensure no id conflict - if (board.id == 0) { - const cnt = countAll() - board.id = cnt + 1 - } else { - const retID = includes(board.id) - if (retID.isOk()) { - if (retID.value) return new Err("ID Conflict") - } else { - return new Err("Wrong Type") - } - } - - // Ensure no name conflict in the same room - { - const retName = includes(board.name, board.room) - if (retName.isOk()) { - if (retName.value) { - return new Err("Name Conflict") - } - } else { - return new Err("Wrong Type") - } - } - - const query = db.query(` - INSERT INTO Boards VALUES - (${board.id}, - '${board.name}', - '${board.room}', - '${board.ipv4}', - '${_.isUndefined(board.ipv6) ? "NULL" : board.ipv6}', - ${board.port}); - `) - - return Ok(query.run()) - } - - export function addFromArray(array: Array) - : Result, BoardErrorType> { - - let arrayChanges: Array = [] - for (const item of array) { - const ret = add(item) - if (ret.isErr()) { - return ret - } else { - arrayChanges.push(ret.value) - } - } - - return new Ok(arrayChanges) - } - - export function all(): Option> { - const query = db.query(`SELECT * FROM Boards`) - const ret = query.all() - query.finalize() - - if (isBoardArray(ret)) { - return Some(ret) - } else { - return None - } - } - - export function countAll(): number { - const query = db.query(`SELECT COUNT(*) FROM Boards`) - return query.values()[0][0] as number - } - - export function countByName(name: string): number { - const query = db.query(`SELECT * FROM Boards WHERE name=${name}`) - return query.values()[0][0] as number - } - - export function countByRoom(room: string): number { - const query = db.query(`SELECT * FROM Boards WHERE room=${room}`) - return query.values()[0][0] as number - } - - export function remove(name: string, room: string): Result - export function remove(id: number): Result - export function remove(board: Board): Result - export function remove(arg1: any, arg2?: any): Result { - let retBoard - let condition: string - if (isBoard(arg1)) { - retBoard = _.cloneDeep(arg1) - condition = `id=${arg1.id}` - - } else if (_.isNumber(arg1)) { - retBoard = find(arg1) - if (retBoard.isOk() && retBoard.value.isSome()) { - retBoard = _.cloneDeep(retBoard.value.value) - condition = `id=${arg1}` - } else { - return new Err("Wrong Type") - } - - } else if (_.isString(arg1) && _.isString(arg2)) { - retBoard = find(arg1, arg2) - if (retBoard.isOk() && retBoard.value.isSome()) { - retBoard = _.cloneDeep(retBoard.value.value) - condition = `name=${arg1}` - } else { - return new Err("No Such Board(s)") - } - - } else { - return new Err("Wrong Type") - } - - const query = db.query(`DELETE FROM Boards WHERE ${condition}`) - query.run() - - return new Ok(retBoard) - } - - export function removeAll(): Option> { - const array = all() - const query = db.query(`DELETE FROM Boards`) - query.run() - - - if (array.isSome()) { - return new Some(array.value) - } else { - return None - } - } - - export function removeByCondition(condition: string): Result, BoardErrorType> { - const rsArr = findByCondition(condition) - if (rsArr.isNone()) { - return new Err("No Such Board(s)") - } - - const query = db.query(`DELETE FROM Boards WHERE ${condition}`) - query.run() - - return new Ok(rsArr.value) - } - - export function removeByValue(columnName: string, val: string | Array) - : Result, BoardErrorType> { - - if (!isBoardColumn(columnName)) { - return new Err("Wrong Type") - } - - let condition: string - if (_.isString(val)) { - const retCond = fun.sqlConditionFromString(columnName, val, "OR") - if (retCond.isSome()) { - condition = retCond.value - } else { - return new Err("Wrong Type") - } - } else if (type.isStringArray(val)) { - const retCond = fun.sqlConditionFromArray(columnName, val, "OR") - if (retCond.isSome()) { - condition = retCond.value - } else { - return new Err("Wrong Type") - } - } else { - return new Err("Wrong Type") - } - - return removeByCondition(condition) - } - - export function removeByName(name: string | Array) - : Result, BoardErrorType> { - return removeByValue("name", name) - } - - export function removeByRoom(room: string | Array) - : Result, BoardErrorType> { - - return removeByValue("room", room) - } - - export function rooms(): Option> { - const query = db.query(`SELECT DISTINCT room FROM Boards`) - let rooms: Array = [] - const retVal = query.values() - - if (retVal.length > 0) { - for (const item of retVal) { - rooms.push(item[0] as string) - } - return new Some(rooms) - } else { - return None - } - } - - - export function find(name: string, room: string): Result, BoardErrorType> - export function find(id: number): Result, BoardErrorType> - export function find(arg1: any, arg2?: any): Result, BoardErrorType> { - let condition: string - if (_.isNumber(arg1)) { - condition = `id=${arg1}` - - } else if (_.isString(arg1) && _.isString(arg2)) { - condition = `name='${arg1}' AND room='${arg2}'` - - } else { - return new Err("Wrong Type") - } - - const query = db.query(`SELECT * FROM Boards WHERE ${condition}`) - - const spRet = boardSchema.safeParse(query.get()) - - if (spRet.success) { - return new Ok(Some(spRet.data)) - } else { - return new Ok(None) - } - } - - export function findByName(name: string | Array): Result>, BoardErrorType> { - let condition: string - if (_.isString(name)) { - const retCond = fun.sqlConditionFromString("name", name, "OR") - if (retCond.isSome()) { - condition = retCond.value - } else { - return new Err("Wrong Type") - } - } else if (type.isStringArray(name)) { - const retCond = fun.sqlConditionFromArray("name", name, "OR") - if (retCond.isSome()) { - condition = retCond.value - } else { - return new Err("Wrong Type") - } - } else { - return new Err("Wrong Type") - } - - return new Ok(findByCondition(condition)) - } - - export function findByCondition(condition: string): Option> { - const query = db.query(`SELECT * FROM Boards WHERE ${condition}`) - const ret = query.all() - if (isBoardArray(ret)) { - return new Some(ret) - } else { - return None - } - } - - export function findByValue(columnName: string, val: string | Array) - : Result>, BoardErrorType> { - - if (!isBoardColumn(columnName)) { - return new Err("Wrong Type") - } - - let condition: string - if (_.isString(val)) { - const retCond = fun.sqlConditionFromString(columnName, val, "OR") - if (retCond.isSome()) { - condition = retCond.value - } else { - return new Err("Wrong Type") - } - } else if (type.isStringArray(val)) { - const retCond = fun.sqlConditionFromArray(columnName, val, "OR") - if (retCond.isSome()) { - condition = retCond.value - } else { - return new Err("Wrong Type") - } - } else { - return new Err("Wrong Type") - } - - return new Ok(findByCondition(condition)) - } - - export function includes(name: string, room?: string): Result - export function includes(id: number): Result - export function includes(arg1: any, arg2?: any): Result { - let condition: string - if (_.isUndefined(arg2)) { - if (_.isNumber(arg1)) { - condition = `id=${arg1}` - - } else if (_.isString(arg1)) { - condition = `name='${arg1}'` - - } else { - return new Err("Wrong Type") - } - } else { - if (_.isString(arg1) && _.isString(arg2)) { - condition = `name='${arg1} AND room=${arg2}'` - } else { - return new Err("Wrong Type") - } - } - - const query = db.query(`SELECT COUNT(*) FROM Boards WHERE ${condition}`) - const num = query.values()[0][0] as number - - return new Ok(num > 0 ? true : false) - } - -} - -export namespace UserTable { - - export function countAll(): number { - const query = db.query(`SELECT COUNT(*) FROM Users`) - return query.values()[0][0] as number - } - - - export function find(id: number): Result, "Wrong Type"> - export function find(name: string): Result, "Wrong Type"> - export function find(arg: any): Result, "Wrong Type"> { - let condition: string - if (_.isNumber(arg)) { - condition = `id=${arg}` - } else if (_.isString(arg)) { - condition = `name=${arg}` - } else { - return new Err("Wrong Type") - } - - const query = db.query(`SELECT * FROM Users WHERE name='${arg}'`) - - const spRet = userSchema.safeParse(query.get()) - - if (spRet.success) { - return new Ok(Some(spRet.data)) - } else { - return new Ok(None) - } - } - -} - - diff --git a/server/index.ts b/server/index.ts deleted file mode 100644 index 1de8dd6..0000000 --- a/server/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createBunServeHandler } from "trpc-bun-adapter"; -import { appRouter } from "./router.ts" - -Bun.serve(createBunServeHandler({ - router: appRouter, - responseMeta() { - return { - status: 200, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET, PUT, POST, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type, Authorization" - } - } - } -}, { - port: 3002, - fetch() { - return Response.json({ message: "Not Found" }, { status: 404 }); - } -})) diff --git a/server/msgBus.ts b/server/msgBus.ts deleted file mode 100644 index c413269..0000000 --- a/server/msgBus.ts +++ /dev/null @@ -1 +0,0 @@ -import { MsgProtocol } from "./msgProtocol"; diff --git a/server/msgProtocol.ts b/server/msgProtocol.ts deleted file mode 100644 index 5c8fdab..0000000 --- a/server/msgProtocol.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { type TransferListItem } from "worker_threads"; -import { z } from "zod"; -import { UNUSED } from "./common"; - -export namespace MsgProtocol { - const QueryDataSchema = z.object({ type: z.literal("Query"), args: z.any() }) - const ResultDataSchema = z.object({ type: z.literal("Result"), result: z.any() }) - const ErrorDataSchema = z.object({ type: z.literal("Error"), error: z.string() }) - - const MessageSchema = z.object({ - command: z.string(), - data: z.discriminatedUnion("type", [QueryDataSchema, ResultDataSchema, ErrorDataSchema]), - dest: z.string(), - src: z.string(), - }) - const MessageQuerySchema = z.object({ - command: z.string(), - data: QueryDataSchema, - dest: z.string(), - src: z.string(), - }) - const MessageResultSchema = z.object({ - command: z.string(), - data: ResultDataSchema, - dest: z.string(), - src: z.string(), - }) - const MessageErrorSchema = z.object({ - command: z.string(), - data: ErrorDataSchema, - dest: z.string(), - src: z.string(), - }) - const MessageHandlerSchema = z.function() - .args(z.union([MessageResultSchema, MessageErrorSchema])) - .returns(z.void()) - - export type Message = z.infer - export type MessageQuery = z.infer - export type MessageResult = z.infer - export type MessageError = z.infer - export type MessageHandler = z.infer - - export function isMessage(obj: any): obj is Message { - return MessageSchema.safeParse(obj).success - } - export function isMessageQuery(obj: any): obj is MessageQuery { - return MessageQuerySchema.safeParse(obj).success - } - export function isMessageResult(obj: any): obj is MessageResult { - return MessageResultSchema.safeParse(obj).success - } - export function isMessageError(obj: any): obj is MessageError { - return MessageErrorSchema.safeParse(obj).success - } - - export function genMessageResult(result: any, srcMsg: MessageQuery): MessageResult { - return { - command: srcMsg.command, - dest: srcMsg.src, - src: srcMsg.dest, - data: { - type: "Result", - result: result - } - } as MessageResult - } - export function genMessageError(error: string, srcMsg: MessageQuery): MessageError { - return { - command: srcMsg.command, - dest: srcMsg.src, - src: srcMsg.dest, - data: { - type: "Error", - error: error - } - } as MessageError - } -} diff --git a/server/router.ts b/server/router.ts deleted file mode 100644 index 19690b6..0000000 --- a/server/router.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { router, publicProcedure } from "./trpc.ts" - -export const appRouter = router({ - api: router({ - status: publicProcedure.query(() => "OK"), - signUp: publicProcedure.query((opts) => { - - }) - }) -}); - -export type AppRouter = typeof appRouter; diff --git a/server/trpc.ts b/server/trpc.ts deleted file mode 100644 index 0db673e..0000000 --- a/server/trpc.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { initTRPC } from '@trpc/server'; - -const t = initTRPC.create(); - -export const router = t.router; -export const publicProcedure = t.procedure; diff --git a/server/udp.test.ts b/server/udp.test.ts deleted file mode 100644 index 690b7d0..0000000 --- a/server/udp.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { expect, test } from "bun:test" -import { udpServer, udpClient } from "./udp" - -const udpServerAddr = "127.0.0.1" -const udpServerPort = await udpServer.port() - -console.info("Ready to Test UDP") - -test("Test Send String", async () => { - const str = "Hello My Server" - const retSend = await udpClient.sendString(str, udpServerPort, udpServerAddr) - expect(retSend).toBe(true) - - const retReceive = await udpServer.lastestData(udpServerAddr) - if (retReceive.isSome()) { - const data = retReceive.value - expect(data.body).toBe(str) - expect(data.address).toBe(udpServerAddr) - } else { - expect().fail("Not Receive Anything") - } -}) diff --git a/server/udp.ts b/server/udp.ts deleted file mode 100644 index 7c1fc81..0000000 --- a/server/udp.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { resolve } from "path"; -import Tinypool from "tinypool"; -import type { UDPReceiveProcotol, UDPSendProtocol } from "./udpProtocol"; -import type { udp } from "bun"; -import { Option } from "ts-results-es"; - -const SERVER_PATH = resolve(__dirname, "./udpServer.ts") -const CLIENT_PATH = resolve(__dirname, "./udpClient.ts") - -export type UDPBodyType = ( - udp.Data | - UDPSendProtocol.CmdPackage | - UDPSendProtocol.DataPackage | - UDPReceiveProcotol.ReadPackage | - UDPReceiveProcotol.WritePackage -) - -type UDPDataType = { - address: string, - body: T, - port: number, - date?: string, -} - - -const udpClientsPool = new Tinypool({ - filename: CLIENT_PATH, - workerData: {} -}) - -const udpServerPool = new Tinypool({ - filename: SERVER_PATH, - workerData: {}, - maxThreads: 1, - minThreads: 1, -}) - -export namespace udpServer { - - export async function port(): Promise { - return udpServerPool.run(null, { name: "port" }) - } - - export async function lastestData(address: string): Promise>> { - return udpServerPool.run(address, { name: "lastestData" }) - } - - export async function oldestData(address: string): Promise>> { - return udpServerPool.run(address, { name: "oldestData" }) - } - -} - -export namespace udpClient { - - export async function sendString(data: string, port: number, address: string): Promise { - return udpClientsPool.run({ - body: data, - port: port, - address: address - } as UDPDataType, { name: "sendString" }) - } - - export async function sendUint8Array(data: Uint8Array, port: number, address: string): Promise { - return udpClientsPool.run({ - body: data, - port: port, - address: address - } as UDPDataType, { name: "sendUint8Array" }) - } - - export async function sendBunData(data: udp.Data, port: number, address: string): Promise { - return udpClientsPool.run({ - body: data, - port: port, - address: address - } as UDPDataType, { name: "sendUint8Array" }) - } -} -export type { UDPDataType } diff --git a/server/udpClient.ts b/server/udpClient.ts deleted file mode 100644 index c59ff76..0000000 --- a/server/udpClient.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { udp } from "bun" -import type { UDPDataType } from "./udp" - -const udpClient = await Bun.udpSocket({}) - -export function sendString(data: UDPDataType): boolean { - return udpClient.send(data.body, data.port, data.address) -} - -export function sendUint8Array(data: UDPDataType): boolean { - return udpClient.send(data.body, data.port, data.address) -} - -export function sendBunData(data: UDPDataType): boolean { - return udpClient.send(data.body, data.port, data.address) -} - diff --git a/server/udpProtocol.ts b/server/udpProtocol.ts deleted file mode 100644 index 3198958..0000000 --- a/server/udpProtocol.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { type } from "./common"; -import { z } from "zod"; -import _ from "lodash"; -import { Err, Ok, type Result } from "ts-results-es"; -import { isUint8Array } from "util/types"; - -const PKG_SIGN_ADDR = 0x00 -const PKG_SIGN_DATA = 0xFF -const PKG_SIGN_READ = 0x0F -const PKG_SIGN_WRITE = 0xF0 - -export namespace UDPSendProtocol { - - const CMDLOC_BURST_TYPE = 5 - const CMDLOC_TASK_ID = 3 - const CMDLOC_READ_WRITE_TYPE = 0 - - const CmdPackageOptionsSchema = z.object({ - burstType: z.enum(["Fixed", "Extend"]), - taskID: z.number().nonnegative().lt(4), - readWriteType: z.enum(["r", "w"]) - }) - - export type CmdPackageOptions = z.infer - - export function isCmdPackageOptions(obj: any): obj is CmdPackageOptions { - return CmdPackageOptionsSchema.safeParse(obj).success - } - - export class CmdPackage { - private ID: number = PKG_SIGN_ADDR - private commandType: number = 0 - private burstLength: number = 0 - private _reserved: number = 0 - private address: number = 0 - - - constructor(array: Uint8Array) - constructor(options: CmdPackageOptions) - constructor(arg: any) { - if (isCmdPackageOptions(arg)) { - this.setCommandType(arg) - } else if (isUint8Array(arg)) { - this.ID = arg[0] - this.commandType = arg[1] - this.burstLength = arg[2] - this._reserved = arg[3] - this.address = type.bytesToNumber(arg.slice(4)) - } else { - throw new Err("Wrong Type") - } - } - - setCommandType(options: CmdPackageOptions) { - const validOptions = CmdPackageOptionsSchema.parse(options) - this.commandType = ( - ((validOptions.burstType === "Fixed") ? 1 << CMDLOC_BURST_TYPE : 0) | - (validOptions.taskID << CMDLOC_TASK_ID) | - ((validOptions.readWriteType === "r") ? 1 << CMDLOC_READ_WRITE_TYPE : 0) - ) - } - - setBurstLength(len: number): Result { - if (!type.UInt8.safeParse(len).success) { - return new Err("Not 8Bits Unsigned Integer") - } - this.burstLength = len - return new Ok(null) - } - - setAddress(addr: number): Result { - if (!type.UInt32.safeParse(addr).success) { - return new Err("Not 32Bits Unsigned Integer") - } - this.address = addr - return new Ok(null) - } - - options(): CmdPackageOptions { - return { - burstType: type.numberMatch(this.commandType, CMDLOC_BURST_TYPE, "Extend", "Fixed"), - taskID: (this.commandType >> CMDLOC_TASK_ID) & 0b0011, - readWriteType: type.numberMatch(this.commandType, CMDLOC_READ_WRITE_TYPE, "w", "r") - } - } - - toUint8Array(): Uint8Array { - var array = new Uint8Array(8) - - array[0] = this.ID - array[1] = this.commandType - array[2] = this.burstLength - array[3] = this._reserved - - // Already check address length at the begining of set address - let addressBytes = type.numberToBytes(this.address, 4).unwrap() - array.set(addressBytes, 4) - - return array - } - } - - export function isCmdPackage(obj: any): obj is CmdPackage { - return obj instanceof CmdPackage - } - - export class DataPackage { - private ID: number = PKG_SIGN_DATA - private _reserved: number = 0 - private body: Uint8Array - - constructor(body: Uint8Array) { - this.body = body - } - - toUint8Array(): Uint8Array { - var array = new Uint8Array(4 + this.body.length) - - array[0] = this.ID - array.fill(this._reserved, 1, 4) - array.set(this.body, 4) - - return array - } - } - - export function isDataPackage(obj: any): obj is DataPackage { - return obj instanceof DataPackage - } - -} - -export namespace UDPReceiveProcotol { - - export class ReadPackage { - private ID: number = PKG_SIGN_READ - private taskID: number = 0 - private resp: number = 0 - private _reserved: number = 0 - body: Uint8Array - - constructor(array: Uint8Array) { - if (array.length < 5) { - throw new Err("Not Long Enough") - } - this.ID = array[0] - this.taskID = array[1] - this.resp = array[2] - this._reserved = array[3] - this.body = new Uint8Array(array.slice(4)) - } - - toUint8Array(): Uint8Array { - let array = new Uint8Array(4 + this.body.length) - - array[0] = this.ID - array[1] = this.taskID - array[2] = this.resp - array[3] = this._reserved - array.set(this.body, 4) - - return array - } - } - - export function isReadPackage(obj: any): obj is ReadPackage { - return obj instanceof ReadPackage - } - - export class WritePackage { - private ID: number = PKG_SIGN_WRITE - private taskID: number = 0 - private resp: number = 0 - private _reserved: number = 0 - - - constructor(array: Uint8Array) { - if (array.length < 4) { - throw new Err("Not Long Enough") - } - this.ID = array[0] - this.taskID = array[1] - this.resp = array[2] - this._reserved = array[3] - } - - toUint8Array(): Uint8Array { - return new Uint8Array([ - this.ID, - this.taskID, - this.resp, - this._reserved - ]) - } - } - - export function isWritePackage(obj: any): obj is WritePackage { - return obj instanceof WritePackage - } - -} diff --git a/server/udpServer.ts b/server/udpServer.ts deleted file mode 100644 index d9fe296..0000000 --- a/server/udpServer.ts +++ /dev/null @@ -1,97 +0,0 @@ -import _ from "lodash" -import { type udp } from "bun" -import type { UDPDataType } from "./udp"; -import { None, Some, type Option } from "ts-results-es"; -import { z } from "zod"; - -// Bun Data Type -interface BinaryTypeList { - arraybuffer: ArrayBuffer; - buffer: Buffer; - uint8array: Uint8Array; -} -type BinaryType = keyof BinaryTypeList; - -const receivedData: Map> = new Map() - -const udpServer = await Bun.udpSocket({ - port: 33000, - socket: { - data( - _socket: udp.Socket, - data: BinaryTypeList[BinaryType], - port: number, - address: string, - ) { - // Add Received Data - let arrayData = receivedData.get(address) - if (_.isUndefined(arrayData)) { - - } else { - receivedData.set(address, []) - arrayData.push({ - body: data, - port: port, - date: new Date().toUTCString() - }) - } - } - } -}) - -function port(): number { - return udpServer.port -} - -function lastestData(address: string): Option> { - if (!z.string().ip().safeParse(address).success) { - return None - } - - const arrayData = receivedData.get(address) - if (_.isUndefined(arrayData)) { - return None - } - - const data = arrayData.pop() - if (_.isUndefined(data)) { - return None - } - - return Some({ - address: address, - body: data.body, - port: data.port, - date: data.date, - }) -} - -function oldestData(address: string): Option> { - if (!z.string().ip().safeParse(address).success) { - return None - } - - const arrayData = receivedData.get(address) - if (_.isUndefined(arrayData)) { - return None - } - - const data = arrayData.shift() - if (_.isUndefined(data)) { - return None - } - - return Some({ - address: address, - body: data.body, - port: data.port, - date: data.date, - }) -} - -export { port, lastestData, oldestData } - diff --git a/tsconfig.bun.json b/tsconfig.bun.json deleted file mode 100644 index 4289b08..0000000 --- a/tsconfig.bun.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - // Enable latest features - "lib": [ - "ESNext" - ], - "target": "ESNext", - "module": "ESNext", - "moduleDetection": "force", - "jsx": "react-jsx", - "allowJs": true, - // Bundler mode - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - // Best practices - "strict": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - // Some stricter flags - "noUnusedLocals": true, - "noUnusedParameters": true, - "noPropertyAccessFromIndexSignature": true - } -}