Compare commits

..

4 Commits

Author SHA1 Message Date
CrescentLeaf
d2007fbdcc 進一步完善對外 API 2025-08-29 23:43:59 +08:00
CrescentLeaf
7bf5921298 chore: MAKE LINIT HAPPT 2025-08-29 23:43:24 +08:00
CrescentLeaf
f79fe55fe5 chore: MAKE LINIT HAPPT 2025-08-29 23:43:09 +08:00
CrescentLeaf
91bfbcb3b3 docs: add readme 2025-08-29 22:51:03 +08:00
5 changed files with 103 additions and 67 deletions

11
readme.md Normal file
View File

@@ -0,0 +1,11 @@
## TheWhiteSilk
Under developing...
### TODO
* [x] 接口完備
* [ ] 對外接口
* [ ] 單元測試
* [ ] 基本可用
* [ ] 負載均衡

View File

@@ -2,6 +2,7 @@ import BaseApi from './BaseApi.ts'
import HttpServerLike from '../types/HttpServerLike.ts' import HttpServerLike from '../types/HttpServerLike.ts'
import UserApi from "./UserApi.ts" import UserApi from "./UserApi.ts"
import SocketIo from "socket.io" import SocketIo from "socket.io"
import UnknownFunction from "../types/UnknownFunction.ts";
export default class ApiManager { export default class ApiManager {
static httpServer: HttpServerLike static httpServer: HttpServerLike
@@ -14,9 +15,22 @@ export default class ApiManager {
static getHttpServer() { static getHttpServer() {
return this.httpServer return this.httpServer
} }
static getSocketIoServer() {
return this.socketIoServer
}
static initAllApis() { static initAllApis() {
this.apis_instance = { this.apis_instance = {
user: new UserApi() user: new UserApi()
} }
} }
static initEvents() {
const io = this.socketIoServer
io.on('connection', (socket) => {
socket.on("The_White_Silk", (name: string, args: {}, callback: UnknownFunction) => {
if (name == null || args == null) return callback({
})
})
})
}
} }

View File

@@ -1,4 +1,5 @@
import UnknownFunction from '../types/UnknownFunction.ts' import UnknownFunction from '../types/UnknownFunction.ts'
import ApiManager from "./ApiManager.ts";
export default abstract class BaseApi { export default abstract class BaseApi {
abstract getName(): string abstract getName(): string
@@ -7,6 +8,7 @@ export default abstract class BaseApi {
} }
abstract onInit(): void abstract onInit(): void
registerEvent(name: string, func: UnknownFunction) { registerEvent(name: string, func: UnknownFunction) {
const io = ApiManager.getSocketIoServer()
} }
} }

View File

@@ -1,38 +1,39 @@
import { DatabaseSync } from "node:sqlite" import { DatabaseSync, SQLInputValue } from "node:sqlite"
import { Buffer } from "node:buffer" import { Buffer } from "node:buffer"
import path from 'node:path' import path from 'node:path'
import crypto from 'node:crypto' import crypto from 'node:crypto'
import fs_sync from 'node:fs' import fs_sync from 'node:fs'
import chalk from "chalk"
import { fileTypeFromBuffer } from 'file-type' import { fileTypeFromBuffer } from 'file-type'
export default class FileManager { import config from "../config.ts"
static FileBean = class {
class FileBean {
declare count: number declare count: number
declare name: string declare name: string
declare hash: string declare hash: string
declare mime: string declare mime: string
declare chatid: string | null declare chatid?: string
declare upload_time: number declare upload_time: number
declare last_used_time: number declare last_used_time: number
} }
static File = class { class File {
declare bean: FileManager.FileBean declare bean: FileBean
constructor(bean: FileManager.FileBean) { constructor(bean: FileBean) {
this.bean = bean this.bean = bean
} }
private setAttr(key: string, value: unknown): void { private setAttr(key: string, value: SQLInputValue) {
FileManager.database.prepare(`UPDATE ${FileManager.table_name} SET ${key} = ? WHERE count = ?`).run(value, this.bean.count) FileManager.database.prepare(`UPDATE ${FileManager.table_name} SET ${key} = ? WHERE count = ?`).run(value, this.bean.count)
this.bean[key] = value this.bean[key] = value
} }
getMime(): string { getMime() {
return this.bean.mime return this.bean.mime
} }
getName(): string { getName() {
return this.bean.name return this.bean.name
} }
getFilePath(): string { getFilePath() {
const hash = this.bean.hash const hash = this.bean.hash
return path.join( return path.join(
config.data_path, config.data_path,
@@ -43,23 +44,27 @@ export default class FileManager {
this.bean.hash this.bean.hash
) )
} }
getChatId(): string | null { getChatId() {
return this.bean.chatid return this.bean.chatid
} }
getUploadTime(): number { getUploadTime() {
return this.bean.upload_time return this.bean.upload_time
} }
getLastUsedTime(): number { getLastUsedTime() {
return this.bean.last_used_time return this.bean.last_used_time
} }
readSync(): Buffer { 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())
} }
} }
export default class FileManager {
static FileBean = FileBean
static File = File
static table_name: string = "FileReferences" static table_name: string = "FileReferences"
private static database: DatabaseSync = FileManager.init() static database: DatabaseSync = FileManager.init()
private static init(): DatabaseSync { private static init(): DatabaseSync {
const db: DatabaseSync = new DatabaseSync(path.join(config.data_path, FileManager.table_name + '.db')) const db: DatabaseSync = new DatabaseSync(path.join(config.data_path, FileManager.table_name + '.db'))
db.exec(` db.exec(`
@@ -76,15 +81,15 @@ export default class FileManager {
return db return db
} }
static uploadFile(fileName: string, data: Buffer, chatId: string | null) { static async uploadFile(fileName: string, data: Buffer, chatId?: string) {
const hash = crypto.createHash('sha256').update(data).digest('hex') const hash = crypto.createHash('sha256').update(data).digest('hex')
try { try {
return FileManager.findByHash(hash) return FileManager.findByHash(hash)
} catch(_e) { } catch (_e) {
// Do nothing... // Do nothing...
} }
const mime = fileTypeFromBuffer(data) const mime = (await fileTypeFromBuffer(data))?.mime || 'application/octet-stream'
fs_sync.writeFileSync( fs_sync.writeFileSync(
path.join( path.join(
config.data_path, config.data_path,
@@ -110,7 +115,7 @@ export default class FileManager {
fileName, fileName,
hash, hash,
mime, mime,
chatId, chatId || null,
Date.now(), Date.now(),
-1 -1
).lastInsertRowid ).lastInsertRowid
@@ -118,15 +123,15 @@ export default class FileManager {
) )
} }
private static findAllBeansByCondition(condition: string, ...args: unknown[]): FileManager.FileBean[] { private static findAllBeansByCondition(condition: string, ...args: SQLInputValue[]): FileBean[] {
return FileManager.database.prepare(`SELECT * FROM ${FileManager.table_name} WHERE ${condition};`).all(...args) return FileManager.database.prepare(`SELECT * FROM ${FileManager.table_name} WHERE ${condition};`).all(...args) as unknown as FileBean[]
} }
static findByHash(hash: string): FileManager.File { static findByHash(hash: string): File {
const beans = FileManager.findAllBeansByCondition('hash = ?', hash) const beans = FileManager.findAllBeansByCondition('hash = ?', hash)
if (beans.length == 0) if (beans.length == 0)
throw new Error(`找不到 hash 为 ${hash} 的文件`) throw new Error(`找不到 hash 为 ${hash} 的文件`)
else if (beans.length > 1) else if (beans.length > 1)
console.error(chalk.red(`警告: 查询 hash = ${id} 时, 查询到多个相同 Hash 的文件`)) console.error(chalk.red(`警告: 查询 hash = ${hash} 时, 查询到多个相同 Hash 的文件`))
return new FileManager.File(beans[0]) return new FileManager.File(beans[0])
} }
} }

View File

@@ -0,0 +1,4 @@
type ApiCallbackMessage = {
msg: string,
code: 200 | 400 | 401 | 403 | 404 | 500 | 501
}