Compare commits
18 Commits
3617292409
...
546f04dc0e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
546f04dc0e | ||
|
|
bc11034892 | ||
|
|
dfe8b27a12 | ||
|
|
5eb7e0018a | ||
|
|
b3015084a6 | ||
|
|
316fd140bc | ||
|
|
3cb9bcc148 | ||
|
|
4ca3bd44da | ||
|
|
39c1473c57 | ||
|
|
3c3beebfc5 | ||
|
|
9b3a24e37a | ||
|
|
182236964b | ||
|
|
a3920f9084 | ||
|
|
45aef8204a | ||
|
|
e2c385b559 | ||
|
|
4a942f1e77 | ||
|
|
fb541849b4 | ||
|
|
9e92fad8fa |
@@ -1,10 +1,11 @@
|
|||||||
import * as CryptoES from 'crypto-es'
|
// @ts-types="npm:@types/crypto-js"
|
||||||
|
import * as CryptoJS from 'crypto-js'
|
||||||
|
|
||||||
const dataIsEmpty = !localStorage.tws_data || localStorage.tws_data == ''
|
const dataIsEmpty = !localStorage.tws_data || localStorage.tws_data == ''
|
||||||
|
|
||||||
const aes = {
|
const aes = {
|
||||||
enc: (m: string, k: string) => CryptoES.AES.encrypt(m, k).toString(CryptoES.HexFormatter),
|
enc: (data: string, key: string) => CryptoJS.AES.encrypt(data, key).toString(),
|
||||||
dec: (m: string, k: string) => CryptoES.AES.decrypt(m, k).toString(CryptoES.Utf8),
|
dec: (data: string, key: string) => CryptoJS.AES.decrypt(data, key).toString(CryptoJS.enc.Utf8),
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = location.host + '_TWS_姐姐'
|
const key = location.host + '_TWS_姐姐'
|
||||||
@@ -20,7 +21,7 @@ const _data_cached = JSON.parse(_dec)
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
data: {
|
data: {
|
||||||
apply: () => void
|
apply(): void
|
||||||
access_token?: string
|
access_token?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,17 +29,18 @@ declare global {
|
|||||||
|
|
||||||
// deno-lint-ignore no-window
|
// deno-lint-ignore no-window
|
||||||
(window.data == null) && (window.data = new Proxy({
|
(window.data == null) && (window.data = new Proxy({
|
||||||
apply() {
|
apply() {}
|
||||||
localStorage.tws_data = aes.enc(JSON.stringify(_data_cached), key)
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
get(_obj, k) {
|
get(_obj, k) {
|
||||||
|
if (k == '_cached') return _data_cached
|
||||||
|
if (k == 'apply') return () => localStorage.tws_data = aes.enc(JSON.stringify(_data_cached), key)
|
||||||
return _data_cached[k]
|
return _data_cached[k]
|
||||||
},
|
},
|
||||||
set(_obj, k, v) {
|
set(_obj, k, v) {
|
||||||
|
if (k == '_cached') return false
|
||||||
_data_cached[k] = v
|
_data_cached[k] = v
|
||||||
return true
|
return true
|
||||||
},
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// deno-lint-ignore no-window
|
// deno-lint-ignore no-window
|
||||||
|
|||||||
5
client/api/client_data/Message.ts
Normal file
5
client/api/client_data/Message.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default class Message {
|
||||||
|
declare id: number
|
||||||
|
declare text: string
|
||||||
|
declare user_id: string
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
"mdui": "npm:mdui@2.1.4",
|
"mdui": "npm:mdui@2.1.4",
|
||||||
"split.js": "npm:split.js@1.3.2",
|
"split.js": "npm:split.js@1.3.2",
|
||||||
"crypto-es": "npm:crypto-es@3.1.0",
|
"crypto-js": "npm:crypto-js@4.2.0",
|
||||||
"socket.io-client": "npm:socket.io-client@4.8.1"
|
"socket.io-client": "npm:socket.io-client@4.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import MessageContainer from "./MessageContainer.jsx"
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
export default function ChatFragment({ ...props } = {}) {
|
export default function ChatFragment({ ...props } = {}) {
|
||||||
|
const messageList = React.useState([])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@@ -31,11 +33,6 @@ export default function ChatFragment({ ...props } = {}) {
|
|||||||
<mdui-button variant="text">加載更多</mdui-button>
|
<mdui-button variant="text">加載更多</mdui-button>
|
||||||
</div>
|
</div>
|
||||||
<MessageContainer>
|
<MessageContainer>
|
||||||
<Message
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
</MessageContainer>
|
</MessageContainer>
|
||||||
{
|
{
|
||||||
// 输入框
|
// 输入框
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import useEventListener from "../useEventListener.ts"
|
|||||||
import { checkApiSuccessOrSncakbar } from "../snackbar.ts"
|
import { checkApiSuccessOrSncakbar } from "../snackbar.ts"
|
||||||
import Client from "../../api/Client.ts"
|
import Client from "../../api/Client.ts"
|
||||||
|
|
||||||
import * as CryptoES from 'crypto-es'
|
import * as CryptoJS from 'crypto-js'
|
||||||
import data from "../../Data.ts";
|
import data from "../../Data.ts";
|
||||||
|
|
||||||
interface Refs {
|
interface Refs {
|
||||||
@@ -29,12 +29,13 @@ export default function LoginDialog({
|
|||||||
|
|
||||||
const re = await Client.invoke("User.login", {
|
const re = await Client.invoke("User.login", {
|
||||||
account: account,
|
account: account,
|
||||||
password: CryptoES.SHA256(password).toString(CryptoES.Hex),
|
password: CryptoJS.SHA256(password).toString(CryptoJS.enc.Hex),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (checkApiSuccessOrSncakbar(re, "登錄失敗")) return
|
if (checkApiSuccessOrSncakbar(re, "登錄失敗")) return
|
||||||
|
|
||||||
data.access_token = re.data!.access_token as string
|
data.access_token = re.data!.access_token as string
|
||||||
|
data.apply()
|
||||||
location.reload()
|
location.reload()
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import useEventListener from "../useEventListener.ts";
|
|||||||
import Client from "../../api/Client.ts";
|
import Client from "../../api/Client.ts";
|
||||||
import { checkApiSuccessOrSncakbar, snackbar } from "../snackbar.ts";
|
import { checkApiSuccessOrSncakbar, snackbar } from "../snackbar.ts";
|
||||||
|
|
||||||
import * as CryptoES from 'crypto-es'
|
import * as CryptoJS from 'crypto-js'
|
||||||
|
|
||||||
interface Refs {
|
interface Refs {
|
||||||
loginInputAccountRef: React.MutableRefObject<TextField | null>
|
loginInputAccountRef: React.MutableRefObject<TextField | null>
|
||||||
@@ -31,7 +31,7 @@ export default function RegisterDialog({
|
|||||||
const re = await Client.invoke("User.register", {
|
const re = await Client.invoke("User.register", {
|
||||||
username: username,
|
username: username,
|
||||||
nickname: registerInputNickNameRef.current!.value,
|
nickname: registerInputNickNameRef.current!.value,
|
||||||
password: CryptoES.SHA256(registerInputPasswordRef.current!.value).toString(CryptoES.Hex),
|
password: CryptoJS.SHA256(registerInputPasswordRef.current!.value).toString(CryptoJS.enc.Hex),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (checkApiSuccessOrSncakbar(re, "注冊失敗")) return
|
if (checkApiSuccessOrSncakbar(re, "注冊失敗")) return
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
export type CallMethod =
|
export type CallMethod =
|
||||||
"User.auth" |
|
"User.auth" |
|
||||||
"User.register" |
|
"User.register" |
|
||||||
"User.login"
|
"User.login" |
|
||||||
|
|
||||||
|
"User.setAvatar" |
|
||||||
|
|
||||||
|
"Chat.sendMessage" |
|
||||||
|
"Chat.getMessageHistory"
|
||||||
|
|
||||||
export type ClientEvent =
|
export type ClientEvent =
|
||||||
"Client.onMessage"
|
"Client.onMessage"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import ApiCallbackMessage from "./ApiCallbackMessage.ts"
|
|||||||
import EventCallbackFunction from "../typedef/EventCallbackFunction.ts"
|
import EventCallbackFunction from "../typedef/EventCallbackFunction.ts"
|
||||||
import BaseApi from "./BaseApi.ts"
|
import BaseApi from "./BaseApi.ts"
|
||||||
import DataWrongError from "./DataWrongError.ts";
|
import DataWrongError from "./DataWrongError.ts";
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
export default class ApiManager {
|
export default class ApiManager {
|
||||||
static httpServer: HttpServerLike
|
static httpServer: HttpServerLike
|
||||||
@@ -32,16 +33,22 @@ export default class ApiManager {
|
|||||||
static initEvents() {
|
static initEvents() {
|
||||||
const io = this.socketIoServer
|
const io = this.socketIoServer
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
socket.on("The_White_Silk", (name: string, args: { [key: string]: unknown }, callback: (ret: ApiCallbackMessage) => void) => {
|
socket.on("The_White_Silk", (name: string, args: { [key: string]: unknown }, callback_: (ret: ApiCallbackMessage) => void) => {
|
||||||
|
function callback(ret: ApiCallbackMessage) {
|
||||||
|
console.log(chalk.blue('[發]') + ` ${socket.request.socket.remoteAddress} <- ${ret.code == 200 ? chalk.green(ret.msg) : chalk.red(ret.msg)} [${ret.code}]${ ret.data ? (' <extras: ' + JSON.stringify(ret.data) + '>') : ''}`)
|
||||||
|
return callback_(ret)
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (name == null || args == null) return callback({
|
if (name == null || args == null) return callback({
|
||||||
msg: "Invalid request.",
|
msg: "Invalid request.",
|
||||||
code: 400
|
code: 400
|
||||||
})
|
})
|
||||||
|
console.log(chalk.red('[收]') + ` ${socket.request.socket.remoteAddress} -> ${chalk.yellow(name)} <args: ${JSON.stringify(args)}>`)
|
||||||
|
|
||||||
return callback(this.event_listeners[name]?.(args))
|
return callback(this.event_listeners[name]?.(args))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = e as Error
|
const err = e as Error
|
||||||
|
console.log(chalk.yellow('[壞]') + ` ${err.message} (${err.stack})`)
|
||||||
try {
|
try {
|
||||||
callback({
|
callback({
|
||||||
code: err instanceof DataWrongError ? 400 : 500,
|
code: err instanceof DataWrongError ? 400 : 500,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import EventCallbackFunction from "../typedef/EventCallbackFunction.ts"
|
import EventCallbackFunction from "../typedef/EventCallbackFunction.ts"
|
||||||
import ApiManager from "./ApiManager.ts"
|
import ApiManager from "./ApiManager.ts"
|
||||||
import { CallMethod } from './ApiDeclare.ts'
|
import { CallMethod } from './ApiDeclare.ts'
|
||||||
|
import User from "../data/User.ts"
|
||||||
|
import Token from "./Token.ts"
|
||||||
|
|
||||||
export default abstract class BaseApi {
|
export default abstract class BaseApi {
|
||||||
abstract getName(): string
|
abstract getName(): string
|
||||||
@@ -20,6 +22,16 @@ export default abstract class BaseApi {
|
|||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
checkUserToken(user: User, token: Token) {
|
||||||
|
if (!this.checkToken(token)) return false
|
||||||
|
if (token.author != user.bean.id) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
checkToken(token: Token) {
|
||||||
|
if (token.expired_time < Date.now()) return false
|
||||||
|
if (!User.findById(token.author)) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
registerEvent(name: CallMethod, func: EventCallbackFunction) {
|
registerEvent(name: CallMethod, func: EventCallbackFunction) {
|
||||||
if (!name.startsWith(this.getName() + ".")) throw Error("注冊的事件應該與接口集合命名空間相匹配: " + name)
|
if (!name.startsWith(this.getName() + ".")) throw Error("注冊的事件應該與接口集合命名空間相匹配: " + name)
|
||||||
ApiManager.addEventListener(name, func)
|
ApiManager.addEventListener(name, func)
|
||||||
|
|||||||
24
server/api/ChatApi.ts
Normal file
24
server/api/ChatApi.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import User from "../data/User.ts"
|
||||||
|
import BaseApi from "./BaseApi.ts"
|
||||||
|
|
||||||
|
export default class UserApi extends BaseApi {
|
||||||
|
override getName(): string {
|
||||||
|
return "Chat"
|
||||||
|
}
|
||||||
|
override onInit(): void {
|
||||||
|
this.registerEvent("Chat.sendMessage", (args) => {
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: 501,
|
||||||
|
msg: "未實現",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.registerEvent("Chat.getMessageHistory", (args) => {
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: 501,
|
||||||
|
msg: "未實現",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
6
server/api/Token.ts
Normal file
6
server/api/Token.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default interface Token {
|
||||||
|
author: string
|
||||||
|
auth: string
|
||||||
|
made_time: number
|
||||||
|
expired_time: number
|
||||||
|
}
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
import { Buffer } from "node:buffer";
|
import { Buffer } from "node:buffer"
|
||||||
import config from "../config.ts"
|
import config from "../config.ts"
|
||||||
import User from "../data/User.ts"
|
import User from "../data/User.ts"
|
||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
|
import Token from "./Token.ts"
|
||||||
interface Token {
|
|
||||||
author: string
|
|
||||||
auth: string
|
|
||||||
made_time: number
|
|
||||||
expired_time: number
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeKey(key: string, keyLength = 32) {
|
function normalizeKey(key: string, keyLength = 32) {
|
||||||
const hash = crypto.createHash('sha256')
|
const hash = crypto.createHash('sha256')
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Buffer } from "node:buffer";
|
||||||
import User from "../data/User.ts";
|
import User from "../data/User.ts";
|
||||||
import BaseApi from "./BaseApi.ts"
|
import BaseApi from "./BaseApi.ts"
|
||||||
import TokenManager from "./TokenManager.ts";
|
import TokenManager from "./TokenManager.ts";
|
||||||
@@ -15,8 +16,8 @@ export default class UserApi extends BaseApi {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const access_token = TokenManager.decode(args.access_token as string)
|
const access_token = TokenManager.decode(args.access_token as string)
|
||||||
|
|
||||||
if (access_token.expired_time > Date.now()) return {
|
if (access_token.expired_time < Date.now()) return {
|
||||||
msg: "登錄令牌失效",
|
msg: "登錄令牌失效",
|
||||||
code: 401,
|
code: 401,
|
||||||
}
|
}
|
||||||
@@ -91,5 +92,30 @@ export default class UserApi extends BaseApi {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// 更新頭像
|
||||||
|
this.registerEvent("User.setAvatar", (args) => {
|
||||||
|
if (this.checkArgsMissing(args, ['avatar', 'token'])) return {
|
||||||
|
msg: "參數缺失",
|
||||||
|
code: 400,
|
||||||
|
}
|
||||||
|
if (!(args.avatar instanceof Buffer)) return {
|
||||||
|
msg: "參數不合法",
|
||||||
|
code: 400,
|
||||||
|
}
|
||||||
|
const token = TokenManager.decode(args.token as string)
|
||||||
|
if (!this.checkToken(token)) return {
|
||||||
|
code: 401,
|
||||||
|
msg: "令牌無效",
|
||||||
|
}
|
||||||
|
|
||||||
|
const avatar: Buffer = args.avatar as Buffer
|
||||||
|
const user = User.findById(token.author)
|
||||||
|
user!.setAvatar(avatar)
|
||||||
|
|
||||||
|
return {
|
||||||
|
msg: "成功",
|
||||||
|
code: 200,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import path from 'node:path'
|
|||||||
|
|
||||||
import config from '../config.ts'
|
import config from '../config.ts'
|
||||||
import ChatBean from './ChatBean.ts'
|
import ChatBean from './ChatBean.ts'
|
||||||
|
import { SQLInputValue } from "node:sqlite"
|
||||||
|
import chalk from "chalk"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chat.ts - Wrapper and manager
|
* Chat.ts - Wrapper and manager
|
||||||
@@ -25,12 +27,12 @@ export default class Chat {
|
|||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
private static findAllByCondition(condition: string, ...args: unknown[]): ChatBean[] {
|
private static findAllBeansByCondition(condition: string, ...args: SQLInputValue[]): ChatBean[] {
|
||||||
return database.prepare(`SELECT * FROM ${Chat.table_name} WHERE ${condition}`).all(...args)
|
return this.database.prepare(`SELECT * FROM ${Chat.table_name} WHERE ${condition}`).all(...args) as unknown as ChatBean[]
|
||||||
}
|
}
|
||||||
|
|
||||||
static findById(id: string): Chat {
|
static findById(id: string): Chat {
|
||||||
const beans = Chat.findAllBeansByCondition('id = ?', id)
|
const beans = this.findAllBeansByCondition('id = ?', id)
|
||||||
if (beans.length == 0)
|
if (beans.length == 0)
|
||||||
throw new Error(`找不到 id 为 ${id} 的 Chat`)
|
throw new Error(`找不到 id 为 ${id} 的 Chat`)
|
||||||
else if (beans.length > 1)
|
else if (beans.length > 1)
|
||||||
@@ -42,41 +44,8 @@ export default class Chat {
|
|||||||
constructor(bean: ChatBean) {
|
constructor(bean: ChatBean) {
|
||||||
this.bean = bean
|
this.bean = bean
|
||||||
}
|
}
|
||||||
private setAttr(key: string, value: unknown): void {
|
private setAttr(key: string, value: SQLInputValue): void {
|
||||||
User.database.prepare(`UPDATE ${Chat.table_name} SET ${key} = ? WHERE id = ?`).run(value, this.bean.id)
|
Chat.database.prepare(`UPDATE ${Chat.table_name} SET ${key} = ? WHERE id = ?`).run(value, this.bean.id)
|
||||||
this.bean[key] = value
|
this.bean[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
getSettings(): Chat.Settings {
|
|
||||||
return new Chat.Settings(this, JSON.parse(this.bean.settings))
|
|
||||||
}
|
|
||||||
|
|
||||||
static Settings = class {
|
|
||||||
declare bean: Chat.SettingsBean
|
|
||||||
declare chat: Chat
|
|
||||||
constructor(chat: Chat, bean: Chat.SettingsBean) {
|
|
||||||
this.bean = bean
|
|
||||||
this.chat = chat
|
|
||||||
for (const i of [
|
|
||||||
|
|
||||||
]) {
|
|
||||||
this["set" + i.substring(0, 1).toUpperCase() + i.substring(1)] = (v: unknown) => {
|
|
||||||
this.set(i, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key: string, value: unknown) {
|
|
||||||
this.bean[key] = value
|
|
||||||
}
|
|
||||||
get(key: string) {
|
|
||||||
return this.bean[key]
|
|
||||||
}
|
|
||||||
apply() {
|
|
||||||
this.chat.setAttr("settings", JSON.stringify(this.bean))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static SettingsBean = class {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export default class ChatBean {
|
export default class ChatBean {
|
||||||
declare id: string
|
declare id: string
|
||||||
declare settings: string
|
declare settings: string
|
||||||
|
|
||||||
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ class File {
|
|||||||
getLastUsedTime() {
|
getLastUsedTime() {
|
||||||
return this.bean.last_used_time
|
return this.bean.last_used_time
|
||||||
}
|
}
|
||||||
|
getHash() {
|
||||||
|
return this.bean.hash
|
||||||
|
}
|
||||||
readSync() {
|
readSync() {
|
||||||
this.setAttr("last_used_time", Date.now())
|
this.setAttr("last_used_time", Date.now())
|
||||||
return fs_sync.readFileSync(this.getFilePath())
|
return fs_sync.readFileSync(this.getFilePath())
|
||||||
|
|||||||
7
server/data/Message.ts
Normal file
7
server/data/Message.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default class Message {
|
||||||
|
declare id: number
|
||||||
|
declare text: string
|
||||||
|
declare user_id: string
|
||||||
|
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@ import FileManager from './FileManager.ts'
|
|||||||
import { SQLInputValue } from "node:sqlite";
|
import { SQLInputValue } from "node:sqlite";
|
||||||
import DataWrongError from "../api/DataWrongError.ts";
|
import DataWrongError from "../api/DataWrongError.ts";
|
||||||
|
|
||||||
|
type UserBeanKey = keyof UserBean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User.ts - Wrapper and manager
|
* User.ts - Wrapper and manager
|
||||||
* Wrap with UserBean to directly update database
|
* Wrap with UserBean to directly update database
|
||||||
@@ -100,7 +102,7 @@ export default class User {
|
|||||||
this.bean = bean
|
this.bean = bean
|
||||||
}
|
}
|
||||||
/* 一切的基础都是 count ID */
|
/* 一切的基础都是 count ID */
|
||||||
private setAttr(key: string, value: unknown) {
|
private setAttr(key: string, value: SQLInputValue) {
|
||||||
User.database.prepare(`UPDATE ${User.table_name} SET ${key} = ? WHERE count = ?`).run(value, this.bean.count)
|
User.database.prepare(`UPDATE ${User.table_name} SET ${key} = ? WHERE count = ?`).run(value, this.bean.count)
|
||||||
this.bean[key] = value
|
this.bean[key] = value
|
||||||
}
|
}
|
||||||
@@ -123,42 +125,9 @@ export default class User {
|
|||||||
this.setAttr("password", password)
|
this.setAttr("password", password)
|
||||||
}
|
}
|
||||||
getAvatar(): Buffer | null {
|
getAvatar(): Buffer | null {
|
||||||
return FileManager.findByHash(this.bean.avatar_file_hash)?.readSync()
|
return this.bean.avatar_file_hash != null ? FileManager.findByHash(this.bean.avatar_file_hash)?.readSync() : null
|
||||||
}
|
}
|
||||||
setAvatar(avatar: Buffer) {
|
async setAvatar(avatar: Buffer) {
|
||||||
this.setAttr("avatar_file_hash", FileManager.uploadFile(`avatar_user_${this.bean.count}`, avatar).getHash())
|
this.setAttr("avatar_file_hash", (await FileManager.uploadFile(`avatar_user_${this.bean.count}`, avatar)).getHash())
|
||||||
}
|
|
||||||
|
|
||||||
getSettings() {
|
|
||||||
return new User.Settings(this, JSON.parse(this.bean.settings))
|
|
||||||
}
|
|
||||||
|
|
||||||
static Settings = class {
|
|
||||||
declare bean: User.SettingsBean
|
|
||||||
declare user: User
|
|
||||||
constructor(user: User, bean: User.SettingsBean) {
|
|
||||||
this.bean = bean
|
|
||||||
this.user = user
|
|
||||||
for (const i of [
|
|
||||||
|
|
||||||
]) {
|
|
||||||
this["set" + i.substring(0, 1).toUpperCase() + i.substring(1)] = (v: unknown) => {
|
|
||||||
this.set(i, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key: string, value: unknown) {
|
|
||||||
this.bean[key] = value
|
|
||||||
}
|
|
||||||
get(key: string) {
|
|
||||||
return this.bean[key]
|
|
||||||
}
|
|
||||||
apply() {
|
|
||||||
this.user.setAttr("settings", JSON.stringify(this.bean))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static SettingsBean = class {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ export default class UserBean {
|
|||||||
declare nickname: string
|
declare nickname: string
|
||||||
declare avatar_file_hash: string | null
|
declare avatar_file_hash: string | null
|
||||||
declare settings: string
|
declare settings: string
|
||||||
|
|
||||||
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user