{"id":16179320,"url":"https://github.com/1111mp/im_server","last_synced_at":"2025-03-19T01:30:57.586Z","repository":{"id":48471467,"uuid":"264132080","full_name":"1111mp/im_server","owner":"1111mp","description":"IM Application Server.","archived":false,"fork":false,"pushed_at":"2024-11-20T21:56:34.000Z","size":2515,"stargazers_count":4,"open_issues_count":3,"forks_count":4,"subscribers_count":1,"default_branch":"nestjs","last_synced_at":"2025-02-28T13:47:27.899Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1111mp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-15T07:54:21.000Z","updated_at":"2024-12-24T14:50:39.000Z","dependencies_parsed_at":"2024-04-11T11:24:10.106Z","dependency_job_id":"c5603c00-b74e-4b38-862d-f91d46919e1e","html_url":"https://github.com/1111mp/im_server","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1111mp%2Fim_server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1111mp%2Fim_server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1111mp%2Fim_server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1111mp%2Fim_server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1111mp","download_url":"https://codeload.github.com/1111mp/im_server/tar.gz/refs/heads/nestjs","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243960361,"owners_count":20375102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-10T05:27:09.272Z","updated_at":"2025-03-19T01:30:57.217Z","avatar_url":"https://github.com/1111mp.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# im_server\n\n一个 IM 应用的纯后台服务，对应的客户端目前为：[electron_client](https://github.com/1111mp/electron_client)\n\n接口文档可在服务启动之后（[Swagger Ui](https://swagger.io/tools/swagger-ui/)），浏览器访问 `http://localhost:3000/api/docs` 查看\n\n数据库：mariadb\n\n缓存：redis\n\n（本地需要安装数据库和 redis）\n\n数据库 ORM 库：sequelize\n\n配置文件（用到的 key）：\n`.env.dev`:\n\n```\nPORT=3000\nSECRET_KEY=random_str\nUSER_AUTH_KEY=user_auth\n\nDB_USER=root\nDB_PASS=************\nDB_NAME=database_test\nDB_HOST=127.0.0.1\nDB_PORT=3306\n\nREDIS_HOST=127.0.0.1\nREDIS_PORT=6379\nREDIS_USERNAME=root\nREDIS_PWD=root\nREDIS_DB=0\n\nMULTER_DEST=/upload\nCLIENTORIGIN=http://localhost:3000___http://localhost:1212___http://127.0.0.1:1212\n```\n\n`日志系统` 借由 `log4js` 实现\n\n已支持:\n\n[JSON Web Token](https://jwt.io/)\n\n[protocol-buffers](https://github.com/protobufjs/protobuf.js/)\n\n[Api Data Cache](https://github.com/1111mp/im_server/blob/nestjs/src/cache/interceptors/cache.interceptor.ts)\n\n## Installation\n\n```bash\n$ yarn install\n```\n\n## Running the app\n\n```bash\n# development\n$ yarn run start\n\n# watch mode\n$ yarn run start:dev\n\n# production mode\n$ yarn run start:prod\n```\n\nOr `F5` to `Debug` project.\n\n## Test\n\n```bash\n# unit tests\n$ yarn run test\n\n# e2e tests\n$ yarn run test:e2e\n\n# test coverage\n$ yarn run test:cov\n```\n\n## IM Protocol\n\n关于 IM 实现的设计可以查看[掘金文档](https://juejin.cn/post/7202583557751865401)，阅读体验更好\n\n[Socket.io](https://socket.io/zh-CN/)\n\n```\ntype MessageBasic = {\n  id: bigint;\n  msgId: string;\n  type: Common.MsgType;\n  sender: number;\n  senderInfo: Omit\u003cUser.UserAttributes, 'pwd'\u003e;\n  groupId?: number;\n  receiver: number; // userId or groupId\n  content: string;\n  timer: number;\n  ext?: string; // reserved field\n};\n\n```\n\n```\ninterface AckResponse {\n  statusCode: HttpStatus;\n  message?: string;\n}\n\nconst enum MessageEventNames {\n  Message = 'on-message',\n  Notify = 'on-notify',\n  Read = 'on-message:read',\n}\n```\n\nMessage Events:\n\n`on-message`: send a message.\n\nexample:\n\n`Server` to send:\n\n```\nconst message: MessageBasic = {\n  msgId: string;\n  type: Common.MsgType;\n  sender: number;\n  senderInfo: Omit\u003cUser.UserAttributes, 'pwd'\u003e;\n  groupId?: number; // if group message\n  receiver: number; // userId or groupId\n  content: string;\n  timer: number;\n  ext?: string; // reserved field\n}\n\nconst buffer = setMessageToProto(message);\n\nclient.socket.emit(\"on-message\", buffer, (result: AckResponse) =\u003e {\n   console.log(result);\n   // result.statusCode === 200 ===\u003e successed\n})\n```\n\n`Client` listener:\n\n```\n  // on-message:text\n  client.socket.on(MessageEventNames.Message, (msg: Uint8Array, callback: (resp: AckResponse) =\u003e void) =\u003e {\n    const message: MessageText = getMessageFromProto(msg);\n    console.log(message);\n    callback({\n      statusCode: 200,\n      message: \"...\"\n    })\n  })\n```\n\n#### Notify\n\n```\ntype Notify = {\n  id: string;\n  type: Common.Notifys;\n  sender: Omit\u003cUser.UserAttributes, 'pwd'\u003e;\n  receiver: number;\n  status: Common.NotifyStatus;\n  timer: number;\n  remark?: string;\n  ext?: string;\n};\n```\n\nexample:\n\n```\nconst notify: Notify = {\n    id: string;\n    type: Common.Notifys;\n    sender: Omit\u003cUser.UserAttributes, 'pwd'\u003e;\n    receiver: number;\n    status: Common.NotifyStatus;\n    timer: number;\n    remark?: string;\n    ext?: string;\n}\n\nconst buffer = setNotifyToProto(notify);\n\nclient.socket.emit(\"on-notify\", buffer, (result: AckResponse) =\u003e {\n   console.log(result);\n   // result.statusCode === 200 ===\u003e successed\n})\n```\n\nClient listener:\n\n```\n  // on-notify\n  client.socket.on(MessageEventNames.Notify, (msg: Uint8Array, callback: (resp: AckResponse) =\u003e void) =\u003e {\n    const notify: Notify = getNotifyFromProto(msg);\n    console.log(notify);\n    callback({\n      statusCode: 200,\n      message: \"...\"\n    })\n  })\n```\n\n## Notes\n\n1. Authentication \u0026 Authorization has been achieved.\n\n```\nUserContext(req.user), example:\n{\n  regisTime: '2023-01-28 17:36:29',\n  updateTime: '2023-01-28 17:36:30',\n  id: 10009,\n  account: '176xxxxxxxx',\n  roleId: 5,\n  roleName: 'admin',\n  roleDesc: '管理员',\n  permissions: [\n    { id: 1, name: 'userGet', desc: 'Get user info' },\n    { id: 2, name: 'userDel', desc: 'Delete user' }\n  ]\n}\n```\n\n2. `passport-jwt` 库的 `jwtFromRequest` 选项目前不支持异步函数，解决方案参考：[passport-jwt:187](https://github.com/mikenicholson/passport-jwt/pull/187)\n\n3. 关于 pm2 启动 ts 项目的报错可参考：[pm2:2675](https://github.com/Unitech/pm2/issues/2675)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1111mp%2Fim_server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1111mp%2Fim_server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1111mp%2Fim_server/lists"}