From d0b5890b99079d0ac0c877891f21043e6ab728b0 Mon Sep 17 00:00:00 2001 From: CrescentLeaf Date: Fri, 1 Aug 2025 22:53:05 +0800 Subject: [PATCH] initial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复 ignore 2. 推翻旧后端代码全部重写, 使用 SQLite --- .gitignore | 2 +- src/config.ts | 17 ++++++++ src/data/Chat.ts | 64 +++++++++++++++++++++++++++ src/data/ChatBean.ts | 3 ++ src/data/User.ts | 101 +++++++++++++++++++++++++++++++++++++++++++ src/data/UserBean.ts | 8 ++++ src/data/_user.ts | 47 ++++++++++++++++++++ src/main.ts | 0 src/main_test.ts | 42 ++++++++++++++++++ 9 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 src/config.ts create mode 100644 src/data/Chat.ts create mode 100644 src/data/ChatBean.ts create mode 100644 src/data/User.ts create mode 100644 src/data/UserBean.ts create mode 100644 src/data/_user.ts create mode 100644 src/main.ts create mode 100644 src/main_test.ts diff --git a/.gitignore b/.gitignore index a7cd50d..4ea0f9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ # 配置文件 thewhitesilk_config.json # **默认**数据目录 -data/ +thewhitesilk_data/ diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..f39cc99 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,17 @@ +import fs from 'node:fs/promises' +import chalk from 'chalk' + +let config = { + data_path: "./thewhitesilk_data" +} + +try { + config = JSON.parse(await fs.readFile('thewhitesilk_config.json')) +} catch (_e) { + console.log(chalk.yellow("配置文件貌似不存在, 正在创建...")) + await fs.writeFile('thewhitesilk_config.json', JSON.stringify(config)) +} + +await fs.mkdir(config.data_path, { recursive: true }) + +export default config diff --git a/src/data/Chat.ts b/src/data/Chat.ts new file mode 100644 index 0000000..a908629 --- /dev/null +++ b/src/data/Chat.ts @@ -0,0 +1,64 @@ +import { DatabaseSync } from "node:sqlite" +import { Buffer } from "node:buffer" +import path from 'node:path' + +import config from '../config.ts' +import ChatBean from './ChatBean.ts' + +/** + * Chat.ts - Wrapper and manager + * Wrap with ChatBean to directly update database + * Manage the database by itself (static) + */ +export default class Chat { + static table_name: string = "Chat" + private static database: DatabaseSync = Chat.init() + private static init(): DatabaseSync { + const db: DatabaseSync = new DatabaseSync(path.join(config.data_path, 'Chats.db')) + db.exec(` + CREATE TABLE IF NOT EXISTS ${Chat.table_name} ( + /* 序号 */ count INTEGER PRIMARY KEY AUTOINCREMENT, + /* Chat ID, 哈希 */ id TEXT, + /* 设置 */ settings TEXT NOT NULL + ); + `) + return db + } + + private static findAllByCondition(condition: string, ...args: unknown[]): UserBean[] { + return database.prepare(`SELECT count, id FROM ${User.table_name} WHERE ${condition}`).all(...args) + } + + + + declare bean: ChatBean + constructor(bean: ChatBean) { + this.bean = bean + } + private setAttr(key: string, value: unknown): void { + User.database.prepare(`UPDATE ${User.table_name} SET ${key} = ? WHERE id = ?`).run(value, this.bean.id) + this.bean[key] = value + } + getUserName(): string { + return this.bean.username + } + setUserName(userName: string): void { + this.setAttr("username", userName) + } + getNickName(): string { + return this.bean.nickname + } + setNickName(nickName: string): void { + this.setAttr("nickname", nickName) + } + getAvatar(): Uint8Array { + return this.bean.avatar + } + setAvatar(avatar: Buffer): void { + this.setAttr("avatar", avatar) + } + + getContacts() { + + } +} diff --git a/src/data/ChatBean.ts b/src/data/ChatBean.ts new file mode 100644 index 0000000..50a332d --- /dev/null +++ b/src/data/ChatBean.ts @@ -0,0 +1,3 @@ +export default interface ChatBean { + id: string +} diff --git a/src/data/User.ts b/src/data/User.ts new file mode 100644 index 0000000..9060355 --- /dev/null +++ b/src/data/User.ts @@ -0,0 +1,101 @@ +import { DatabaseSync } from "node:sqlite" +import { Buffer } from "node:buffer" +import path from 'node:path' +import crypto from 'node:crypto' + +import config from '../config.ts' +import UserBean from './UserBean.ts' + +/** + * User.ts - Wrapper and manager + * Wrap with UserBean to directly update database + * Manage the database by itself (static) + */ +export default class User { + static table_name: string = "Users" + private static database: DatabaseSync = User.init() + private static init(): DatabaseSync { + const db: DatabaseSync = new DatabaseSync(path.join(config.data_path, 'Users.db')) + db.exec(` + CREATE TABLE IF NOT EXISTS ${TABEL_NAME} ( + /* 序号 */ count INTEGER PRIMARY KEY AUTOINCREMENT, + /* 用户 ID, 哈希 */ id TEXT + /* 注册时间, 时间戳 */ registered_time INT8 NOT NULL, + /* 用戶名, 可選 */ username TEXT, + /* 昵称 */ nickname TEXT NOT NULL, + /* 头像, 可选 */ avatar BLOB, + /* 设置 */ settings TEXT NOT NULL + ); + `) + return db + } + + static create(userName: string | null, nickName: string, avatar: Buffer | null): User { + return new User( + User.findAllByCondition( + 'count = ?', + database.prepare(`INSERT INTO ${User.table_name} (id, username, registered_time, nickname, avatar, settings) VALUES (?, ?, ?, ?, ?)`).run( + crypto.randomUUID(), + userName, + Date.now(), + nickName, + avatar, + "{}" + ).lastInsertRowid + ) + ) + } + + private static findAllByCondition(condition: string, ...args: unknown[]): UserBean[] { + return database.prepare(`SELECT * FROM ${User.table_name} WHERE ${condition}`).all(...args) + } + private static checkLengthOrThrow(array: Array, leng: number, errMsg: string): Array { + if (array.length != leng) throw new Error(errMsg) + return array + } + static findById(id: string): User { + return new User(checkLengthOrThrow(User.findAllByCondition('id = ?', id), 1, `找不到用户 ID 为 ${id} 的用户`)[0]) + } + static findByUserName(userName: string): User { + return new User(checkLengthOrThrow(User.findAllByCondition('username = ?', userName), 1, `找不到用户名为 ${userName} 的用户`)[0]) + } + + declare bean: UserBean + constructor(bean: UserBean) { + this.bean = bean + } + /* 一切的基础都是 count ID */ + private setAttr(key: string, value: unknown): void { + User.database.prepare(`UPDATE ${User.table_name} SET ${key} = ? WHERE count = ?`).run(value, this.bean.count) + this.bean[key] = value + } + getUserName(): string { + return this.bean.username + } + setUserName(userName: string): void { + this.setAttr("username", userName) + } + getNickName(): string { + return this.bean.nickname + } + setNickName(nickName: string): void { + this.setAttr("nickname", nickName) + } + getAvatar(): Uint8Array { + return this.bean.avatar + } + setAvatar(avatar: Buffer): void { + this.setAttr("avatar", avatar) + } + + /* getSettings(): Settings { + + } + + static Settings = class { + + } + static SettingsBean = interface { + + } */ +} diff --git a/src/data/UserBean.ts b/src/data/UserBean.ts new file mode 100644 index 0000000..3df07f1 --- /dev/null +++ b/src/data/UserBean.ts @@ -0,0 +1,8 @@ +export default interface UserBean { + count: number, + username: string, + registered_time: number, + nickname: string, + avatar: Uint8Array, + settings: string, +} diff --git a/src/data/_user.ts b/src/data/_user.ts new file mode 100644 index 0000000..6256543 --- /dev/null +++ b/src/data/_user.ts @@ -0,0 +1,47 @@ +import { DatabaseSync } from "node:sqlite" +import fs from 'node:fs/promises' + +await fs.mkdir('data', { recursive: true }) + +const db = new DatabaseSync("data/users.db") +const TABEL_NAME = "Users" + +// 初始化表格 +db.exec( + ` + CREATE TABLE IF NOT EXISTS ${TABEL_NAME} ( + /* 伺服器中 ID */ id INTEGER PRIMARY KEY AUTOINCREMENT, + /* 用戶名, 可選 */ username TEXT, + /* 姓名 */ nickname TEXT NOT NULL, + /* 头像, 可选 */ avatar BLOB + ); + `, +) + +// 插入测试数据 +db.prepare( + ` + INSERT INTO ${TABEL_NAME} (username, nickname, avatar) VALUES (?, ?, ?); + `, +).run("SisterWen", "文姐", await fs.readFile('in.webp')) + +let rows = db.prepare(`SELECT id, username, nickname, avatar FROM ${TABEL_NAME}`).all(); +for (const row of rows) { + console.log(row) +} + +// 更新用户名 +// 用户名要合规, 以免导致 SQL 注入! +db.prepare( + ` + UPDATE ${TABEL_NAME} SET username = '${ "Sister_Wen" }' WHERE ${ "username" } = ${ "'SisterWen'" }; + `, +).run() + +rows = db.prepare(`SELECT id, username, nickname, avatar FROM ${TABEL_NAME}`).all(); +for (const row of rows) { + console.log(row) + await fs.writeFile('out.webp', row.avatar) +} + +db.close() diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/main_test.ts b/src/main_test.ts new file mode 100644 index 0000000..adfa4be --- /dev/null +++ b/src/main_test.ts @@ -0,0 +1,42 @@ +import { DatabaseSync } from "node:sqlite" +import fs from 'node:fs/promises' + +await fs.mkdir('data', { recursive: true }) + +const db = new DatabaseSync("data/users.db") +const TABEL_NAME = "Users" + +// 初始化表格 +db.exec( + ` + CREATE TABLE IF NOT EXISTS ${TABEL_NAME} ( + /* 伺服器中 ID */ id INTEGER PRIMARY KEY AUTOINCREMENT, + /* 用戶名, 可選 */ username TEXT, + /* 姓名 */ nickname TEXT NOT NULL, + /* 头像, 可选 */ avatar BLOB + ); + `, +) + +// 插入测试数据 +db.prepare( + ` + INSERT INTO ${TABEL_NAME} (username, nickname, avatar) VALUES (?, ?, ?); + `, +).run("SisterWen", "文姐", null) + +let rows = db.prepare(`SELECT id, username, nickname, avatar FROM ${TABEL_NAME}`).all(); +for (const row of rows) { + console.log(row) +} + +// 更新用户名 +// 用户名要合规, 以免导致 SQL 注入! +db.prepare(`UPDATE ${TABEL_NAME} SET username = ? WHERE id = ?`).run("文姐", 1) + +rows = db.prepare(`SELECT id, username, nickname, avatar FROM ${TABEL_NAME} WHERE username = ?`).all("文姐") +for (const row of rows) { + console.log(row) +} + +db.close()