diff --git a/package.json b/package.json index 5362f54..688775c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "scripts": { "dev": "node --require ts-node/register src/main.ts", - "compile": "npx rimraf dist && tsc", + "compile": "npx rimraf dist && tsc && cp -R src/sql dist", "run": "node ./dist/main.js", "start:redis": "docker run -d -p 127.0.0.1:6379:6379 redis/redis-stack-server:latest", "start:daemon": "pm2 start --name tgbot ./dist/main.js --watch --no-daemon", @@ -20,8 +20,8 @@ "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.21.1", - "form-data": "^4.0.1", "express-validator": "^7.2.0", + "form-data": "^4.0.1", "js-sha256": "^0.11.0", "lodash": "^4.17.21", "mysql2": "^3.11.3", @@ -31,6 +31,7 @@ "qrcode": "^1.5.1", "redis": "^4.6.15", "sequelize": "^6.37.4", + "uuid": "^10.0.0", "viem": "^2.21.31", "winston": "^3.15.0", "winston-daily-rotate-file": "^5.0.0" @@ -41,6 +42,7 @@ "@types/lodash": "^4.17.12", "@types/node-telegram-bot-api": "^0.64.7", "@types/qrcode": "^1.5.0", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^5.38.1", "@typescript-eslint/parser": "^5.38.1", "eslint": "8.22.0", diff --git a/src/commands-handlers.ts b/src/commands-handlers.ts index 9d77109..ba9e165 100644 --- a/src/commands-handlers.ts +++ b/src/commands-handlers.ts @@ -37,6 +37,7 @@ import { retryPromise } from './utils'; import { getMode } from './ton-connect/storage'; +import { FileModel } from './dao/files'; let newConnectRequestListenersMap = new Map void>(); @@ -322,17 +323,16 @@ async function saveToTonStorage(params: { .toString('base64') } ]); - // 保存记录 @TODO - const url = ''; - const data = { - address: address, // toUserFriendlyAddress( connector.wallet!.account.address, connector.wallet!.account.chain === CHAIN.TESTNET) - from: from, //msg.forward_from?.username || msg.from?.username - fileName: fileName, + await FileModel.createFile({ + chatId: `${chatId}`, + address, + from, + fileName, file: filePath, - fileSize: String(fileSize), + fileSize: BigInt(fileSize), + saveMode: 'crust', bagId: bag_id - }; - await retryPromise(() => axios.post(url, data)); + }); } async function saveToCrust(params: { @@ -351,6 +351,7 @@ async function saveToCrust(params: { const form = new FD(); form.append('file', createReadStream(filePath), { filename: fileName }); + // IPFS add const upRes = await axios .request({ data: form, @@ -362,10 +363,11 @@ async function saveToCrust(params: { }) .then(res => res.data); // pin + const cid = upRes.Hash; await axios.post( `${CONFIGS.common.pinSever}/psa/pins`, { - cid: upRes.Hash, + cid, name: upRes.Name }, { @@ -373,6 +375,16 @@ async function saveToCrust(params: { } ); // saveTo database + await FileModel.createFile({ + chatId: `${chatId}`, + address, + from, + fileName, + file: filePath, + fileSize: BigInt(fileSize), + saveMode: 'crust', + cid + }); } const supportTypes: TelegramBot.MessageType[] = [ diff --git a/src/config/index.ts b/src/config/index.ts index 9910978..74ea2a1 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -21,11 +21,11 @@ export const CONFIGS = { url: getEnvOrExit('REDIS_URL') }, mysql: { - host: getEnvOrExit('MYSQL_HOST', 'localhost'), - port: Number(getEnvOrExit('MYSQL_PORT', '23306')), + host: getEnvOrExit('MYSQL_HOST', 'localhost', true), + port: Number(getEnvOrExit('MYSQL_PORT', '23306', true)), database: getEnvOrExit('MYSQL_DB_NAME', 'bags'), - user: getEnvOrExit('MYSQL_USER', 'root'), - password: getEnvOrExit('MYSQL_PASSWORD', 'root'), + user: getEnvOrExit('MYSQL_USER', 'root', true), + password: getEnvOrExit('MYSQL_PASSWORD', 'root', true), schemaTable: 'data_migration', location: '../sql' }, diff --git a/src/dao/chatModes.ts b/src/dao/chatModes.ts index 04ce675..131e5c5 100644 --- a/src/dao/chatModes.ts +++ b/src/dao/chatModes.ts @@ -1,6 +1,7 @@ import { sequelize } from '../db/mysql'; -import { ChatMode, DBModel, FileSaveMode } from '../type/model'; +import { ChatMode, DBModel } from '../type/model'; import { DataTypes } from 'sequelize'; +import { MODE } from '../ton-connect/storage'; class Model implements DBModel { model = sequelize.define( @@ -19,9 +20,10 @@ class Model implements DBModel { unique: true }, saveMode: { - type: DataTypes.TINYINT, + type: DataTypes.STRING(16), allowNull: false, - field: 'save_mode' + field: 'save_mode', + defaultValue: '' } }, { @@ -30,19 +32,19 @@ class Model implements DBModel { updatedAt: 'update_time' } ); - async upsertMode(chatId: string, saveMode: FileSaveMode) { + async upsertMode(chatId: string, saveMode: MODE) { return this.model.upsert({ chatId, saveMode: saveMode }); } - async getMode(chatId: string): Promise { + async getMode(chatId: string): Promise { const mode = await this.model.findOne({ where: { chatId } }); - return mode == null ? FileSaveMode.CRUST : mode!.saveMode; + return mode == null ? '' : mode!.saveMode; } } diff --git a/src/dao/files.ts b/src/dao/files.ts index 9e60cf0..3b88a1f 100644 --- a/src/dao/files.ts +++ b/src/dao/files.ts @@ -1,6 +1,8 @@ import { DataTypes, Sequelize } from 'sequelize'; -import { DBModel, Files, FileSaveMode } from '../type/model'; +import { DBModel, Files } from '../type/model'; import { sequelize } from '../db/mysql'; +import { v7 as uuidV7 } from 'uuid'; +import { MODE } from '../ton-connect/storage'; class Model implements DBModel { model = sequelize.define( @@ -49,11 +51,11 @@ class Model implements DBModel { field: 'upload_date', defaultValue: Sequelize.fn('NOW') }, - saveType: { - type: DataTypes.TINYINT, + saveMode: { + type: DataTypes.STRING(16), allowNull: false, - field: 'save_type', - defaultValue: FileSaveMode.CRUST + field: 'save_mode', + defaultValue: '' }, cid: { type: DataTypes.STRING(128), @@ -98,9 +100,20 @@ class Model implements DBModel { totalFileSize: Number(result!.get('totalFileSize') ?? 0) }; } - async createFile(file: Files) { + async createFile(file: { + chatId: string; + address: string; + from: string; + fileName: string; + file: string; + fileSize: bigint; + saveMode: MODE; + cid?: string; + bagId?: string; + }) { return await this.model.create({ ...file, + uuid: uuidV7(), uploadDate: new Date(), id: undefined }); diff --git a/src/db/mysql.ts b/src/db/mysql.ts index 2532498..5c6d8b3 100644 --- a/src/db/mysql.ts +++ b/src/db/mysql.ts @@ -1,6 +1,5 @@ import { Sequelize } from 'sequelize'; import { CONFIGS } from '../config'; -import { Env } from '../type/common'; export const sequelize = new Sequelize({ database: CONFIGS.mysql.database, username: CONFIGS.mysql.user, diff --git a/src/main.ts b/src/main.ts index 7149a24..9c5f8d6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -41,7 +41,6 @@ async function main(): Promise { bot.sendMessage(chatId, startMsg); } }; - await initRedisClient(); const callbacks = { ...walletMenuCallbacks, chose_mode: onChooseModeClick diff --git a/src/server/index.ts b/src/server/index.ts index 691a542..0d47ce4 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -7,7 +7,6 @@ import { validate } from '../middleware/validator'; import { query } from 'express-validator'; import { formatFileSize } from '../utils'; import * as _ from 'lodash'; -import { ChatModeModel } from '../dao/chatModes'; export const serverStart = async () => { const app = express(); diff --git a/src/sql/001.do.create-base-tables.sql b/src/sql/001.do.create-base-tables.sql index 2aa8345..50b43ac 100644 --- a/src/sql/001.do.create-base-tables.sql +++ b/src/sql/001.do.create-base-tables.sql @@ -1,17 +1,19 @@ CREATE TABLE IF NOT EXISTS `file` ( `id` bigint NOT NULL AUTO_INCREMENT, `uuid` varchar(64) NOT NULL, + `chat_id` varchar(96) NOT NULL, `address` varchar(96) NOT NULL, `from` varchar(128) NOT NULL, `file_name` text NOT NULL, `file` text NOT NULL, `file_size` bigint NOT NULL, `upload_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `save_type` tinyint NOT NULL DEFAULT 0 COMMENT '0: crust, 1: ton_storage', + `save_mode` varchar(16) NOT NULL DEFAULT '' COMMENT 'ton, crust', `cid` varchar(128) NULL, `bag_id` varchar(128) NULL, PRIMARY KEY (`id`), INDEX `idx_files_address`(`address`) USING BTREE, + INDEX `idx_files_chat_id`(`chat_id`) USING BTREE, INDEX `idx_files_update_date`(`upload_date`) USING BTREE, UNIQUE INDEX `idx_files_uuid`(`uuid`) USING BTREE ); @@ -19,7 +21,7 @@ CREATE TABLE IF NOT EXISTS `file` ( CREATE TABLE IF NOT EXISTS `chat_mode` ( `id` bigint NOT NULL AUTO_INCREMENT, `chat_id` varchar(96) NOT NULL, - `save_mode` tinyint NOT NULL DEFAULT 0 COMMENT '0: crust, 1: ton_storage', + `save_mode` varchar(16) NOT NULL DEFAULT '' COMMENT 'ton, crust', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), diff --git a/src/ton-connect/storage.ts b/src/ton-connect/storage.ts index adea9f8..572d40f 100644 --- a/src/ton-connect/storage.ts +++ b/src/ton-connect/storage.ts @@ -1,6 +1,7 @@ import { IStorage } from '@tonconnect/sdk'; import { createClient } from 'redis'; import { CONFIGS } from '../config'; +import { ChatModeModel } from '../dao/chatModes'; const client = createClient({ url: CONFIGS.redis.url }); @@ -33,10 +34,14 @@ export type MODE = '' | 'ton' | 'crust'; export async function getMode(chatId: number): Promise { let mode = await client.get(`storage_mode_${chatId}`); - if (!mode) mode = ''; + if (!mode) { + mode = await ChatModeModel.getMode(`${chatId}`); + await client.set(`storage_mode_${chatId}`, mode); + } return mode as MODE; } export async function setMode(chatId: number, mode: MODE): Promise { + await ChatModeModel.upsertMode(`${chatId}`, mode); await client.set(`storage_mode_${chatId}`, mode); } diff --git a/src/type/model.ts b/src/type/model.ts index 6bfb31e..534e51a 100644 --- a/src/type/model.ts +++ b/src/type/model.ts @@ -1,24 +1,21 @@ import { Model, ModelStatic } from 'sequelize'; +import { MODE } from '../ton-connect/storage'; export interface DBModel { model: ModelStatic; } -export enum FileSaveMode { - CRUST = 0, - TON_STORAGE = 1 -} - export class Files extends Model { public id!: number; public uuid!: string; + public chatId!: string; public address!: string; public from!: string; public fileName!: string; public file!: string; public fileSize!: bigint; public uploadDate!: Date; - public saveType!: number; + public saveMode!: MODE; public cid?: string; public bagId?: string; } @@ -26,7 +23,7 @@ export class Files extends Model { export class ChatMode extends Model { public id!: number; public chatId!: string; - public saveMode!: number; + public saveMode!: MODE; public createTime?: Date; public updateTime?: Date; } diff --git a/yarn.lock b/yarn.lock index ac10582..ff04e48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -514,6 +514,11 @@ resolved "https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.npmmirror.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + "@types/validator@^13.7.17": version "13.12.2" resolved "https://registry.npmmirror.com/@types/validator/-/validator-13.12.2.tgz#760329e756e18a4aab82fc502b51ebdfebbe49f5" @@ -4265,6 +4270,11 @@ uuid@8.0.0: resolved "https://registry.npmmirror.com/uuid/-/uuid-8.0.0.tgz" integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz"