@pcontext/api 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -0
- package/dist/app.d.mts +44779 -0
- package/dist/app.d.mts.map +1 -0
- package/dist/app.mjs +37 -0
- package/dist/app.mjs.map +1 -0
- package/dist/client.d.mts +22428 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +15 -0
- package/dist/client.mjs.map +1 -0
- package/dist/dev.d.mts +11 -0
- package/dist/dev.d.mts.map +1 -0
- package/dist/dev.mjs +16 -0
- package/dist/dev.mjs.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -0
- package/dist/modules/doc/chat.route.d.mts +18 -0
- package/dist/modules/doc/chat.route.d.mts.map +1 -0
- package/dist/modules/doc/chat.route.mjs +20 -0
- package/dist/modules/doc/chat.route.mjs.map +1 -0
- package/dist/modules/doc/chat.service.d.mts +10 -0
- package/dist/modules/doc/chat.service.d.mts.map +1 -0
- package/dist/modules/doc/chat.service.mjs +29 -0
- package/dist/modules/doc/chat.service.mjs.map +1 -0
- package/dist/modules/doc/doc.dto.d.mts +67 -0
- package/dist/modules/doc/doc.dto.d.mts.map +1 -0
- package/dist/modules/doc/doc.dto.mjs +43 -0
- package/dist/modules/doc/doc.dto.mjs.map +1 -0
- package/dist/modules/doc/doc.entity.d.mts +19 -0
- package/dist/modules/doc/doc.entity.d.mts.map +1 -0
- package/dist/modules/doc/doc.entity.mjs +1 -0
- package/dist/modules/doc/doc.repo.interface.d.mts +28 -0
- package/dist/modules/doc/doc.repo.interface.d.mts.map +1 -0
- package/dist/modules/doc/doc.repo.interface.mjs +1 -0
- package/dist/modules/doc/doc.route.d.mts +14193 -0
- package/dist/modules/doc/doc.route.d.mts.map +1 -0
- package/dist/modules/doc/doc.route.mjs +100 -0
- package/dist/modules/doc/doc.route.mjs.map +1 -0
- package/dist/modules/doc/doc.service.d.mts +38 -0
- package/dist/modules/doc/doc.service.d.mts.map +1 -0
- package/dist/modules/doc/doc.service.mjs +134 -0
- package/dist/modules/doc/doc.service.mjs.map +1 -0
- package/dist/modules/doc/doc.vo.d.mts +17 -0
- package/dist/modules/doc/doc.vo.d.mts.map +1 -0
- package/dist/modules/doc/doc.vo.mjs +1 -0
- package/dist/modules/doc/infrastructure/agent/engine/chat.d.mts +21 -0
- package/dist/modules/doc/infrastructure/agent/engine/chat.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/engine/chat.mjs +36 -0
- package/dist/modules/doc/infrastructure/agent/engine/chat.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/agent/engine/generate.d.mts +28 -0
- package/dist/modules/doc/infrastructure/agent/engine/generate.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/engine/generate.mjs +135 -0
- package/dist/modules/doc/infrastructure/agent/engine/generate.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/agent/engine/query-filter.d.mts +7 -0
- package/dist/modules/doc/infrastructure/agent/engine/query-filter.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/engine/query-filter.mjs +12 -0
- package/dist/modules/doc/infrastructure/agent/engine/query-filter.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/agent/loaders/git-repository-reader.d.mts +54 -0
- package/dist/modules/doc/infrastructure/agent/loaders/git-repository-reader.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/loaders/git-repository-reader.mjs +142 -0
- package/dist/modules/doc/infrastructure/agent/loaders/git-repository-reader.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/agent/loaders/index.d.mts +3 -0
- package/dist/modules/doc/infrastructure/agent/loaders/index.mjs +4 -0
- package/dist/modules/doc/infrastructure/agent/loaders/website-crawl-reader.d.mts +42 -0
- package/dist/modules/doc/infrastructure/agent/loaders/website-crawl-reader.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/loaders/website-crawl-reader.mjs +104 -0
- package/dist/modules/doc/infrastructure/agent/loaders/website-crawl-reader.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/agent/settings.d.mts +5 -0
- package/dist/modules/doc/infrastructure/agent/settings.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/settings.mjs +72 -0
- package/dist/modules/doc/infrastructure/agent/settings.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/agent/storage/index.d.mts +7 -0
- package/dist/modules/doc/infrastructure/agent/storage/index.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/storage/index.mjs +12 -0
- package/dist/modules/doc/infrastructure/agent/storage/index.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/agent/storage/vector-store.d.mts +14 -0
- package/dist/modules/doc/infrastructure/agent/storage/vector-store.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/agent/storage/vector-store.mjs +35 -0
- package/dist/modules/doc/infrastructure/agent/storage/vector-store.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/doc.po.d.mts +570 -0
- package/dist/modules/doc/infrastructure/doc.po.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/doc.po.mjs +54 -0
- package/dist/modules/doc/infrastructure/doc.po.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/doc.repo.pg.d.mts +32 -0
- package/dist/modules/doc/infrastructure/doc.repo.pg.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/doc.repo.pg.mjs +157 -0
- package/dist/modules/doc/infrastructure/doc.repo.pg.mjs.map +1 -0
- package/dist/modules/doc/infrastructure/doc.repo.sqlite.d.mts +32 -0
- package/dist/modules/doc/infrastructure/doc.repo.sqlite.d.mts.map +1 -0
- package/dist/modules/doc/infrastructure/doc.repo.sqlite.mjs +153 -0
- package/dist/modules/doc/infrastructure/doc.repo.sqlite.mjs.map +1 -0
- package/dist/modules/doc/mcp.route.d.mts +18 -0
- package/dist/modules/doc/mcp.route.d.mts.map +1 -0
- package/dist/modules/doc/mcp.route.mjs +39 -0
- package/dist/modules/doc/mcp.route.mjs.map +1 -0
- package/dist/modules/doc/mcp.service.d.mts +22 -0
- package/dist/modules/doc/mcp.service.d.mts.map +1 -0
- package/dist/modules/doc/mcp.service.mjs +119 -0
- package/dist/modules/doc/mcp.service.mjs.map +1 -0
- package/dist/modules/rank/rank.dto.d.mts +10 -0
- package/dist/modules/rank/rank.dto.d.mts.map +1 -0
- package/dist/modules/rank/rank.dto.mjs +8 -0
- package/dist/modules/rank/rank.dto.mjs.map +1 -0
- package/dist/modules/rank/rank.route.d.mts +2033 -0
- package/dist/modules/rank/rank.route.d.mts.map +1 -0
- package/dist/modules/rank/rank.route.mjs +20 -0
- package/dist/modules/rank/rank.route.mjs.map +1 -0
- package/dist/modules/rank/rank.service.d.mts +32 -0
- package/dist/modules/rank/rank.service.d.mts.map +1 -0
- package/dist/modules/rank/rank.service.mjs +3 -0
- package/dist/modules/rank/rank.vo.d.mts +9 -0
- package/dist/modules/rank/rank.vo.d.mts.map +1 -0
- package/dist/modules/rank/rank.vo.mjs +1 -0
- package/dist/modules/task/infrastructure/mq/task-context.d.mts +29 -0
- package/dist/modules/task/infrastructure/mq/task-context.d.mts.map +1 -0
- package/dist/modules/task/infrastructure/mq/task-context.mjs +70 -0
- package/dist/modules/task/infrastructure/mq/task-context.mjs.map +1 -0
- package/dist/modules/task/infrastructure/mq/task-queue.d.mts +10 -0
- package/dist/modules/task/infrastructure/mq/task-queue.d.mts.map +1 -0
- package/dist/modules/task/infrastructure/mq/task-queue.mjs +12 -0
- package/dist/modules/task/infrastructure/mq/task-queue.mjs.map +1 -0
- package/dist/modules/task/infrastructure/mq/task-worker.d.mts +13 -0
- package/dist/modules/task/infrastructure/mq/task-worker.d.mts.map +1 -0
- package/dist/modules/task/infrastructure/mq/task-worker.mjs +35 -0
- package/dist/modules/task/infrastructure/mq/task-worker.mjs.map +1 -0
- package/dist/modules/task/infrastructure/task.po.d.mts +1147 -0
- package/dist/modules/task/infrastructure/task.po.d.mts.map +1 -0
- package/dist/modules/task/infrastructure/task.po.mjs +55 -0
- package/dist/modules/task/infrastructure/task.po.mjs.map +1 -0
- package/dist/modules/task/infrastructure/task.repo.pg.d.mts +21 -0
- package/dist/modules/task/infrastructure/task.repo.pg.d.mts.map +1 -0
- package/dist/modules/task/infrastructure/task.repo.pg.mjs +86 -0
- package/dist/modules/task/infrastructure/task.repo.pg.mjs.map +1 -0
- package/dist/modules/task/infrastructure/task.repo.sqlite.d.mts +21 -0
- package/dist/modules/task/infrastructure/task.repo.sqlite.d.mts.map +1 -0
- package/dist/modules/task/infrastructure/task.repo.sqlite.mjs +89 -0
- package/dist/modules/task/infrastructure/task.repo.sqlite.mjs.map +1 -0
- package/dist/modules/task/task.dto.d.mts +26 -0
- package/dist/modules/task/task.dto.d.mts.map +1 -0
- package/dist/modules/task/task.dto.mjs +25 -0
- package/dist/modules/task/task.dto.mjs.map +1 -0
- package/dist/modules/task/task.entity.d.mts +43 -0
- package/dist/modules/task/task.entity.d.mts.map +1 -0
- package/dist/modules/task/task.entity.mjs +1 -0
- package/dist/modules/task/task.repo.interface.d.mts +17 -0
- package/dist/modules/task/task.repo.interface.d.mts.map +1 -0
- package/dist/modules/task/task.repo.interface.mjs +1 -0
- package/dist/modules/task/task.route.d.mts +4087 -0
- package/dist/modules/task/task.route.d.mts.map +1 -0
- package/dist/modules/task/task.route.mjs +63 -0
- package/dist/modules/task/task.route.mjs.map +1 -0
- package/dist/modules/task/task.service.d.mts +28 -0
- package/dist/modules/task/task.service.d.mts.map +1 -0
- package/dist/modules/task/task.service.mjs +202 -0
- package/dist/modules/task/task.service.mjs.map +1 -0
- package/dist/modules/task/task.vo.d.mts +18 -0
- package/dist/modules/task/task.vo.d.mts.map +1 -0
- package/dist/modules/task/task.vo.mjs +1 -0
- package/dist/modules/user/application/user.service.d.mts +24 -0
- package/dist/modules/user/application/user.service.d.mts.map +1 -0
- package/dist/modules/user/application/user.service.mjs +153 -0
- package/dist/modules/user/application/user.service.mjs.map +1 -0
- package/dist/modules/user/domain/user.entity.d.mts +24 -0
- package/dist/modules/user/domain/user.entity.d.mts.map +1 -0
- package/dist/modules/user/domain/user.entity.mjs +35 -0
- package/dist/modules/user/domain/user.entity.mjs.map +1 -0
- package/dist/modules/user/domain/user.repo.interface.d.mts +27 -0
- package/dist/modules/user/domain/user.repo.interface.d.mts.map +1 -0
- package/dist/modules/user/domain/user.repo.interface.mjs +1 -0
- package/dist/modules/user/infrastructure/casbin/adapter.d.mts +7 -0
- package/dist/modules/user/infrastructure/casbin/adapter.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/casbin/adapter.mjs +13 -0
- package/dist/modules/user/infrastructure/casbin/adapter.mjs.map +1 -0
- package/dist/modules/user/infrastructure/casbin/adapter.pg.d.mts +19 -0
- package/dist/modules/user/infrastructure/casbin/adapter.pg.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/casbin/adapter.pg.mjs +129 -0
- package/dist/modules/user/infrastructure/casbin/adapter.pg.mjs.map +1 -0
- package/dist/modules/user/infrastructure/casbin/adapter.sqlite.d.mts +19 -0
- package/dist/modules/user/infrastructure/casbin/adapter.sqlite.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/casbin/adapter.sqlite.mjs +129 -0
- package/dist/modules/user/infrastructure/casbin/adapter.sqlite.mjs.map +1 -0
- package/dist/modules/user/infrastructure/casbin/enforcer.d.mts +9 -0
- package/dist/modules/user/infrastructure/casbin/enforcer.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/casbin/enforcer.mjs +150 -0
- package/dist/modules/user/infrastructure/casbin/enforcer.mjs.map +1 -0
- package/dist/modules/user/infrastructure/casbin-rule.po.d.mts +643 -0
- package/dist/modules/user/infrastructure/casbin-rule.po.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/casbin-rule.po.mjs +30 -0
- package/dist/modules/user/infrastructure/casbin-rule.po.mjs.map +1 -0
- package/dist/modules/user/infrastructure/user.po.d.mts +392 -0
- package/dist/modules/user/infrastructure/user.po.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/user.po.mjs +34 -0
- package/dist/modules/user/infrastructure/user.po.mjs.map +1 -0
- package/dist/modules/user/infrastructure/user.repo.pg.d.mts +24 -0
- package/dist/modules/user/infrastructure/user.repo.pg.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/user.repo.pg.mjs +90 -0
- package/dist/modules/user/infrastructure/user.repo.pg.mjs.map +1 -0
- package/dist/modules/user/infrastructure/user.repo.sqlite.d.mts +24 -0
- package/dist/modules/user/infrastructure/user.repo.sqlite.d.mts.map +1 -0
- package/dist/modules/user/infrastructure/user.repo.sqlite.mjs +88 -0
- package/dist/modules/user/infrastructure/user.repo.sqlite.mjs.map +1 -0
- package/dist/modules/user/interfaces/admin.route.d.mts +8270 -0
- package/dist/modules/user/interfaces/admin.route.d.mts.map +1 -0
- package/dist/modules/user/interfaces/admin.route.mjs +74 -0
- package/dist/modules/user/interfaces/admin.route.mjs.map +1 -0
- package/dist/modules/user/interfaces/role.route.d.mts +102 -0
- package/dist/modules/user/interfaces/role.route.d.mts.map +1 -0
- package/dist/modules/user/interfaces/role.route.mjs +60 -0
- package/dist/modules/user/interfaces/role.route.mjs.map +1 -0
- package/dist/modules/user/interfaces/user.dto.d.mts +106 -0
- package/dist/modules/user/interfaces/user.dto.d.mts.map +1 -0
- package/dist/modules/user/interfaces/user.dto.mjs +32 -0
- package/dist/modules/user/interfaces/user.dto.mjs.map +1 -0
- package/dist/modules/user/interfaces/user.route.d.mts +2093 -0
- package/dist/modules/user/interfaces/user.route.d.mts.map +1 -0
- package/dist/modules/user/interfaces/user.route.mjs +69 -0
- package/dist/modules/user/interfaces/user.route.mjs.map +1 -0
- package/dist/modules/user/interfaces/user.vo.d.mts +28 -0
- package/dist/modules/user/interfaces/user.vo.d.mts.map +1 -0
- package/dist/modules/user/interfaces/user.vo.mjs +18 -0
- package/dist/modules/user/interfaces/user.vo.mjs.map +1 -0
- package/dist/rank.service-D2h-2iJA.mjs +109 -0
- package/dist/rank.service-D2h-2iJA.mjs.map +1 -0
- package/dist/settings.d.mts +12 -0
- package/dist/settings.d.mts.map +1 -0
- package/dist/settings.mjs +27 -0
- package/dist/settings.mjs.map +1 -0
- package/dist/shared/create-app.d.mts +13 -0
- package/dist/shared/create-app.d.mts.map +1 -0
- package/dist/shared/create-app.mjs +45 -0
- package/dist/shared/create-app.mjs.map +1 -0
- package/dist/shared/db/bootstrap.d.mts +5 -0
- package/dist/shared/db/bootstrap.d.mts.map +1 -0
- package/dist/shared/db/bootstrap.mjs +51 -0
- package/dist/shared/db/bootstrap.mjs.map +1 -0
- package/dist/shared/db/connection.d.mts +1567 -0
- package/dist/shared/db/connection.d.mts.map +1 -0
- package/dist/shared/db/connection.mjs +59 -0
- package/dist/shared/db/connection.mjs.map +1 -0
- package/dist/shared/db/migrations/pg/0000_init.sql +74 -0
- package/dist/shared/db/migrations/pg/0001_snippets & tokens.sql +6 -0
- package/dist/shared/db/migrations/pg/0002_change_task_id_to_uuid_v7.sql +3 -0
- package/dist/shared/db/migrations/pg/meta/0000_snapshot.json +498 -0
- package/dist/shared/db/migrations/pg/meta/0001_snapshot.json +513 -0
- package/dist/shared/db/migrations/pg/meta/0002_snapshot.json +514 -0
- package/dist/shared/db/migrations/pg/meta/_journal.json +27 -0
- package/dist/shared/db/migrations/sqlite/0000_init.sql +72 -0
- package/dist/shared/db/migrations/sqlite/0001_snippets & tokens.sql +2 -0
- package/dist/shared/db/migrations/sqlite/0002_change_task_id_to_uuid_v7.sql +35 -0
- package/dist/shared/db/migrations/sqlite/meta/0000_snapshot.json +493 -0
- package/dist/shared/db/migrations/sqlite/meta/0001_snapshot.json +509 -0
- package/dist/shared/db/migrations/sqlite/meta/0002_snapshot.json +509 -0
- package/dist/shared/db/migrations/sqlite/meta/_journal.json +27 -0
- package/dist/shared/db/seed.d.mts +5 -0
- package/dist/shared/db/seed.d.mts.map +1 -0
- package/dist/shared/db/seed.mjs +42 -0
- package/dist/shared/db/seed.mjs.map +1 -0
- package/dist/shared/deps.d.mts +28 -0
- package/dist/shared/deps.d.mts.map +1 -0
- package/dist/shared/deps.mjs +69 -0
- package/dist/shared/deps.mjs.map +1 -0
- package/dist/shared/dto/index.d.mts +11 -0
- package/dist/shared/dto/index.d.mts.map +1 -0
- package/dist/shared/dto/index.mjs +11 -0
- package/dist/shared/dto/index.mjs.map +1 -0
- package/dist/shared/logger.d.mts +14 -0
- package/dist/shared/logger.d.mts.map +1 -0
- package/dist/shared/logger.mjs +37 -0
- package/dist/shared/logger.mjs.map +1 -0
- package/dist/shared/mcp/createTool.d.mts +15 -0
- package/dist/shared/mcp/createTool.d.mts.map +1 -0
- package/dist/shared/mcp/createTool.mjs +8 -0
- package/dist/shared/mcp/createTool.mjs.map +1 -0
- package/dist/shared/middleware/authorization.d.mts +15 -0
- package/dist/shared/middleware/authorization.d.mts.map +1 -0
- package/dist/shared/middleware/authorization.mjs +46 -0
- package/dist/shared/middleware/authorization.mjs.map +1 -0
- package/dist/shared/middleware/http-logger.d.mts +7 -0
- package/dist/shared/middleware/http-logger.d.mts.map +1 -0
- package/dist/shared/middleware/http-logger.mjs +40 -0
- package/dist/shared/middleware/http-logger.mjs.map +1 -0
- package/dist/shared/middleware/jwt.d.mts +7 -0
- package/dist/shared/middleware/jwt.d.mts.map +1 -0
- package/dist/shared/middleware/jwt.mjs +34 -0
- package/dist/shared/middleware/jwt.mjs.map +1 -0
- package/dist/shared/middleware/limiter.d.mts +7 -0
- package/dist/shared/middleware/limiter.d.mts.map +1 -0
- package/dist/shared/middleware/limiter.mjs +10 -0
- package/dist/shared/middleware/limiter.mjs.map +1 -0
- package/dist/shared/middleware/services.d.mts +8 -0
- package/dist/shared/middleware/services.d.mts.map +1 -0
- package/dist/shared/middleware/services.mjs +19 -0
- package/dist/shared/middleware/services.mjs.map +1 -0
- package/dist/shared/redis/decorator.d.mts +113 -0
- package/dist/shared/redis/decorator.d.mts.map +1 -0
- package/dist/shared/redis/decorator.mjs +203 -0
- package/dist/shared/redis/decorator.mjs.map +1 -0
- package/dist/shared/redis/factory.d.mts +18 -0
- package/dist/shared/redis/factory.d.mts.map +1 -0
- package/dist/shared/redis/factory.mjs +41 -0
- package/dist/shared/redis/factory.mjs.map +1 -0
- package/dist/shared/redis/index.d.mts +12 -0
- package/dist/shared/redis/index.d.mts.map +1 -0
- package/dist/shared/redis/index.mjs +14 -0
- package/dist/shared/redis/index.mjs.map +1 -0
- package/dist/shared/types.d.mts +36 -0
- package/dist/shared/types.d.mts.map +1 -0
- package/dist/shared/types.mjs +1 -0
- package/dist/shared/utils/date.d.mts +1 -0
- package/dist/shared/utils/date.mjs +1 -0
- package/dist/shared/utils/error-handler.d.mts +7 -0
- package/dist/shared/utils/error-handler.d.mts.map +1 -0
- package/dist/shared/utils/error-handler.mjs +22 -0
- package/dist/shared/utils/error-handler.mjs.map +1 -0
- package/dist/shared/utils/format.d.mts +8 -0
- package/dist/shared/utils/format.d.mts.map +1 -0
- package/dist/shared/utils/format.mjs +24 -0
- package/dist/shared/utils/format.mjs.map +1 -0
- package/dist/shared/utils/pagination.d.mts +9 -0
- package/dist/shared/utils/pagination.d.mts.map +1 -0
- package/dist/shared/utils/pagination.mjs +12 -0
- package/dist/shared/utils/pagination.mjs.map +1 -0
- package/dist/shared/utils/response-template.d.mts +69 -0
- package/dist/shared/utils/response-template.d.mts.map +1 -0
- package/dist/shared/utils/response-template.mjs +96 -0
- package/dist/shared/utils/response-template.mjs.map +1 -0
- package/dist/shared/utils/url.d.mts +18 -0
- package/dist/shared/utils/url.d.mts.map +1 -0
- package/dist/shared/utils/url.mjs +39 -0
- package/dist/shared/utils/url.mjs.map +1 -0
- package/dist/shared/utils/user.d.mts +8 -0
- package/dist/shared/utils/user.d.mts.map +1 -0
- package/dist/shared/utils/user.mjs +11 -0
- package/dist/shared/utils/user.mjs.map +1 -0
- package/dist/shared/utils/validator.d.mts +5984 -0
- package/dist/shared/utils/validator.d.mts.map +1 -0
- package/dist/shared/utils/validator.mjs +34 -0
- package/dist/shared/utils/validator.mjs.map +1 -0
- package/dist/shared/vo/index.d.mts +11 -0
- package/dist/shared/vo/index.d.mts.map +1 -0
- package/dist/shared/vo/index.mjs +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.route.d.mts","names":[],"sources":["../../../src/modules/task/task.route.ts"],"mappings":";;;;;;;;;;cAmBM,MAAA,kBAAM,QAAA,CAmER,WAAA;;;;;;;;;;;;;;;;;;;oBAnEQ,OAAA,CAAA,aAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { logger } from "../../shared/logger.mjs";
|
|
2
|
+
import "../../rank.service-D2h-2iJA.mjs";
|
|
3
|
+
import { createRouter } from "../../shared/create-app.mjs";
|
|
4
|
+
import { Res200 } from "../../shared/utils/response-template.mjs";
|
|
5
|
+
import { paramValidator, queryValidator } from "../../shared/utils/validator.mjs";
|
|
6
|
+
import z from "zod";
|
|
7
|
+
import { streamSSE } from "hono/streaming";
|
|
8
|
+
|
|
9
|
+
//#region src/modules/task/task.route.ts
|
|
10
|
+
const idSchema = z.object({ id: z.string() });
|
|
11
|
+
const listQuerySchema = z.object({ limit: z.number().int().min(1).max(100).default(10) });
|
|
12
|
+
const router = createRouter().get("/", queryValidator(listQuerySchema), async (c) => {
|
|
13
|
+
const { limit } = c.req.valid("query");
|
|
14
|
+
const tasks = await c.var.taskService.listRecentTasks(limit);
|
|
15
|
+
return c.json(Res200({ tasks }), 200);
|
|
16
|
+
}).get("/:id", paramValidator(idSchema), async (c) => {
|
|
17
|
+
const { id } = c.req.valid("param");
|
|
18
|
+
const task = await c.var.taskService.getTaskDetail(id);
|
|
19
|
+
return c.json(Res200({ task }), 200);
|
|
20
|
+
}).get("/:id/progress", (c) => {
|
|
21
|
+
const { id } = c.req.param();
|
|
22
|
+
return streamSSE(c, async (stream) => {
|
|
23
|
+
const send = async (event) => {
|
|
24
|
+
if (event.type === "log") {
|
|
25
|
+
try {
|
|
26
|
+
await stream.writeSSE({ data: JSON.stringify([event.entry]) });
|
|
27
|
+
} catch {
|
|
28
|
+
logger.error(`sending task progress by SSE, taskId is ${id}`);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (event.type === "end") try {
|
|
33
|
+
await stream.writeSSE({
|
|
34
|
+
data: event.status,
|
|
35
|
+
event: "end"
|
|
36
|
+
});
|
|
37
|
+
} catch {
|
|
38
|
+
logger.error(`sending task end by SSE, taskId is ${id}`);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
if (!id) {
|
|
42
|
+
await stream.writeSSE({
|
|
43
|
+
data: "error: TASK_ID",
|
|
44
|
+
event: "error"
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const logStream = await c.var.taskService.createLogStream(id);
|
|
50
|
+
for await (const event of logStream) await send(event);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error(`Error streaming logs for task ${id}: ${error.message}`);
|
|
53
|
+
await stream.writeSSE({
|
|
54
|
+
data: "error: INTERNAL_ERROR",
|
|
55
|
+
event: "error"
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { router as default };
|
|
63
|
+
//# sourceMappingURL=task.route.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.route.mjs","names":[],"sources":["../../../src/modules/task/task.route.ts"],"sourcesContent":["import type { TaskLogEvent } from './task.entity'\nimport type { TaskVO } from './task.vo'\nimport type { TaskDocDTO } from '@/modules/doc/doc.dto'\nimport type { ApiError, ApiSuccess } from '@/shared/types'\nimport { streamSSE } from 'hono/streaming'\nimport z from 'zod'\nimport { createRouter } from '@/shared/create-app'\nimport { logger } from '@/shared/logger'\nimport { Res200, Res400, Res404 } from '@/shared/utils/response-template'\nimport { paramValidator, queryValidator } from '@/shared/utils/validator'\n\nconst idSchema = z.object({\n id: z.string(),\n})\n\nconst listQuerySchema = z.object({\n limit: z.number().int().min(1).max(100).default(10),\n})\n\nconst router = createRouter()\n .get('/', queryValidator(listQuerySchema), async (c) => {\n const { limit } = c.req.valid('query')\n\n const tasks = await c.var.taskService.listRecentTasks(limit)\n return c.json(Res200({ tasks }) as ApiSuccess<{\n tasks: TaskVO<TaskDocDTO>[]\n }>, 200)\n })\n .get('/:id', paramValidator(idSchema), async (c) => {\n const { id } = c.req.valid('param')\n const task = await c.var.taskService.getTaskDetail(id)\n return c.json(Res200({ task }) as ApiSuccess<{ task: TaskVO<TaskDocDTO> | null }>, 200)\n })\n .get('/:id/progress', (c) => {\n const { id } = c.req.param()\n\n return streamSSE(c, async (stream) => {\n const send = async (event: TaskLogEvent) => {\n if (event.type === 'log') {\n try {\n await stream.writeSSE({\n data: JSON.stringify([event.entry]),\n })\n }\n catch {\n logger.error(`sending task progress by SSE, taskId is ${id}`)\n }\n return\n }\n\n if (event.type === 'end') {\n try {\n await stream.writeSSE({\n data: event.status,\n event: 'end',\n })\n }\n catch {\n logger.error(`sending task end by SSE, taskId is ${id}`)\n }\n }\n }\n\n if (!id) {\n await stream.writeSSE({\n data: 'error: TASK_ID',\n event: 'error',\n })\n return\n }\n\n try {\n const logStream = await c.var.taskService.createLogStream(id)\n\n for await (const event of logStream) {\n await send(event as TaskLogEvent)\n }\n }\n catch (error: any) {\n logger.error(`Error streaming logs for task ${id}: ${error.message}`)\n await stream.writeSSE({\n data: 'error: INTERNAL_ERROR',\n event: 'error',\n })\n }\n })\n })\n\nexport default router\n"],"mappings":";;;;;;;;;AAWA,MAAM,WAAW,EAAE,OAAO,EACxB,IAAI,EAAE,QAAQ,EACf,CAAC;AAEF,MAAM,kBAAkB,EAAE,OAAO,EAC/B,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,EACpD,CAAC;AAEF,MAAM,SAAS,cAAc,CAC1B,IAAI,KAAK,eAAe,gBAAgB,EAAE,OAAO,MAAM;CACtD,MAAM,EAAE,UAAU,EAAE,IAAI,MAAM,QAAQ;CAEtC,MAAM,QAAQ,MAAM,EAAE,IAAI,YAAY,gBAAgB,MAAM;AAC5D,QAAO,EAAE,KAAK,OAAO,EAAE,OAAO,CAAC,EAE3B,IAAI;EACR,CACD,IAAI,QAAQ,eAAe,SAAS,EAAE,OAAO,MAAM;CAClD,MAAM,EAAE,OAAO,EAAE,IAAI,MAAM,QAAQ;CACnC,MAAM,OAAO,MAAM,EAAE,IAAI,YAAY,cAAc,GAAG;AACtD,QAAO,EAAE,KAAK,OAAO,EAAE,MAAM,CAAC,EAAqD,IAAI;EACvF,CACD,IAAI,kBAAkB,MAAM;CAC3B,MAAM,EAAE,OAAO,EAAE,IAAI,OAAO;AAE5B,QAAO,UAAU,GAAG,OAAO,WAAW;EACpC,MAAM,OAAO,OAAO,UAAwB;AAC1C,OAAI,MAAM,SAAS,OAAO;AACxB,QAAI;AACF,WAAM,OAAO,SAAS,EACpB,MAAM,KAAK,UAAU,CAAC,MAAM,MAAM,CAAC,EACpC,CAAC;YAEE;AACJ,YAAO,MAAM,2CAA2C,KAAK;;AAE/D;;AAGF,OAAI,MAAM,SAAS,MACjB,KAAI;AACF,UAAM,OAAO,SAAS;KACpB,MAAM,MAAM;KACZ,OAAO;KACR,CAAC;WAEE;AACJ,WAAO,MAAM,sCAAsC,KAAK;;;AAK9D,MAAI,CAAC,IAAI;AACP,SAAM,OAAO,SAAS;IACpB,MAAM;IACN,OAAO;IACR,CAAC;AACF;;AAGF,MAAI;GACF,MAAM,YAAY,MAAM,EAAE,IAAI,YAAY,gBAAgB,GAAG;AAE7D,cAAW,MAAM,SAAS,UACxB,OAAM,KAAK,MAAsB;WAG9B,OAAY;AACjB,UAAO,MAAM,iCAAiC,GAAG,IAAI,MAAM,UAAU;AACrE,SAAM,OAAO,SAAS;IACpB,MAAM;IACN,OAAO;IACR,CAAC;;GAEJ;EACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DocSourceEnumDTO, TaskDocDTO } from "../doc/doc.dto.mjs";
|
|
2
|
+
import { TaskVO } from "./task.vo.mjs";
|
|
3
|
+
import { DocEntity } from "../doc/doc.entity.mjs";
|
|
4
|
+
import { TaskContext } from "./infrastructure/mq/task-context.mjs";
|
|
5
|
+
import { TaskWorker } from "./infrastructure/mq/task-worker.mjs";
|
|
6
|
+
import { Readable } from "node:stream";
|
|
7
|
+
import * as bullmq from "bullmq";
|
|
8
|
+
|
|
9
|
+
//#region src/modules/task/task.service.d.ts
|
|
10
|
+
declare class TaskService {
|
|
11
|
+
private worker;
|
|
12
|
+
private queue;
|
|
13
|
+
private cache;
|
|
14
|
+
private get taskRepo();
|
|
15
|
+
private get docRepo();
|
|
16
|
+
constructor();
|
|
17
|
+
initWorker(): TaskWorker;
|
|
18
|
+
submitTask(type: string, data: Omit<TaskDocDTO, 'id'>): Promise<bullmq.Job<any, any, string>>;
|
|
19
|
+
listRecentTasks(limit?: number): Promise<TaskVO<TaskDocDTO>[]>;
|
|
20
|
+
getTaskDetail(taskId: string, logLimit?: number): Promise<TaskVO<TaskDocDTO> | null>;
|
|
21
|
+
private getTaskLogs;
|
|
22
|
+
indexWebsiteDoc(task: TaskContext<TaskDocDTO>): Promise<DocEntity<Date>>;
|
|
23
|
+
indexGitDoc(task: TaskContext<TaskDocDTO>, source: DocSourceEnumDTO): Promise<DocEntity<Date>>;
|
|
24
|
+
createLogStream(taskId: string): Promise<Readable>;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { TaskService };
|
|
28
|
+
//# sourceMappingURL=task.service.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.service.d.mts","names":[],"sources":["../../../src/modules/task/task.service.ts"],"mappings":";;;;;;;;;cAgBa,WAAA;EAAA,QACH,MAAA;EAAA,QACA,KAAA;EAAA,QACA,KAAA;EAAA,YAEI,QAAA,CAAA;EAAA,YAIA,OAAA,CAAA;;EAUZ,UAAA,CAAA,GAAU,UAAA;EAkCJ,UAAA,CAAW,IAAA,UAAc,IAAA,EAAM,IAAA,CAAK,UAAA,UAAiB,OAAA,CAAlB,MAAA,CAAkB,GAAA;EAWrD,eAAA,CAAgB,KAAA,YAAqB,OAAA,CAAQ,MAAA,CAAO,UAAA;EAoBpD,aAAA,CAAc,MAAA,UAAgB,QAAA,YAAyB,OAAA,CAAQ,MAAA,CAAO,UAAA;EAAA,QAwB9D,WAAA;EAYR,eAAA,CAAgB,IAAA,EAAM,WAAA,CAAY,UAAA,IAAW,OAAA,CAAA,SAAA,CAAA,IAAA;EAa7C,WAAA,CAAY,IAAA,EAAM,WAAA,CAAY,UAAA,GAAa,MAAA,EAAQ,gBAAA,GAAgB,OAAA,CAAA,SAAA,CAAA,IAAA;EAanE,eAAA,CAAgB,MAAA,WAAiB,OAAA,CAAQ,QAAA;AAAA"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { logger } from "../../shared/logger.mjs";
|
|
2
|
+
import { createRedisClient } from "../../shared/redis/factory.mjs";
|
|
3
|
+
import { Cache, CacheableService } from "../../shared/redis/decorator.mjs";
|
|
4
|
+
import { n as __decorate, r as __decorateMetadata } from "../../rank.service-D2h-2iJA.mjs";
|
|
5
|
+
import "../../shared/redis/index.mjs";
|
|
6
|
+
import { generateGitRepositoryData, generateWebsiteData } from "../doc/infrastructure/agent/engine/generate.mjs";
|
|
7
|
+
import { TaskContext } from "./infrastructure/mq/task-context.mjs";
|
|
8
|
+
import { TaskQueue } from "./infrastructure/mq/task-queue.mjs";
|
|
9
|
+
import { TaskWorker } from "./infrastructure/mq/task-worker.mjs";
|
|
10
|
+
import { getRepoDeps } from "../../shared/deps.mjs";
|
|
11
|
+
import { PassThrough } from "node:stream";
|
|
12
|
+
|
|
13
|
+
//#region src/modules/task/task.service.ts
|
|
14
|
+
let TaskService = class TaskService {
|
|
15
|
+
worker = null;
|
|
16
|
+
queue;
|
|
17
|
+
cache;
|
|
18
|
+
get taskRepo() {
|
|
19
|
+
return getRepoDeps().taskRepo;
|
|
20
|
+
}
|
|
21
|
+
get docRepo() {
|
|
22
|
+
return getRepoDeps().docRepo;
|
|
23
|
+
}
|
|
24
|
+
constructor() {
|
|
25
|
+
this.cache = createRedisClient();
|
|
26
|
+
this.queue = new TaskQueue("task-queue", this.cache);
|
|
27
|
+
this.initWorker();
|
|
28
|
+
}
|
|
29
|
+
initWorker() {
|
|
30
|
+
if (this.worker) return this.worker;
|
|
31
|
+
this.worker = new TaskWorker("task-queue", async (job) => {
|
|
32
|
+
const ctx = new TaskContext(job, logger);
|
|
33
|
+
const taskId = job.data.id;
|
|
34
|
+
const source = job.data?.source;
|
|
35
|
+
ctx.logInfo("Task started");
|
|
36
|
+
try {
|
|
37
|
+
if (source === "website") await this.indexWebsiteDoc(ctx);
|
|
38
|
+
else if (source === "github" || source === "gitee") await this.indexGitDoc(ctx, source);
|
|
39
|
+
else throw new Error(`Unknown source: ${source}`);
|
|
40
|
+
await this.taskRepo.updateStatus(taskId, "completed");
|
|
41
|
+
ctx.logInfo("Task updated in database with status completed");
|
|
42
|
+
} catch (error) {
|
|
43
|
+
await this.taskRepo.updateStatus(taskId, "failed", error.message);
|
|
44
|
+
ctx.logError(`Task updated in database with status failed: ${error.message}`);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}, this.cache);
|
|
48
|
+
return this.worker;
|
|
49
|
+
}
|
|
50
|
+
async submitTask(type, data) {
|
|
51
|
+
const taskEntity = await this.taskRepo.create({
|
|
52
|
+
type,
|
|
53
|
+
status: "running",
|
|
54
|
+
extraData: data
|
|
55
|
+
});
|
|
56
|
+
return await this.queue.add(type, {
|
|
57
|
+
...data,
|
|
58
|
+
id: taskEntity.id
|
|
59
|
+
}, { jobId: taskEntity.id });
|
|
60
|
+
}
|
|
61
|
+
async listRecentTasks(limit = 20) {
|
|
62
|
+
return (await this.taskRepo.findRecentTasks(limit)).map((task) => {
|
|
63
|
+
const extraData = task.extraData ? {
|
|
64
|
+
id: task.id,
|
|
65
|
+
...task.extraData
|
|
66
|
+
} : void 0;
|
|
67
|
+
return {
|
|
68
|
+
id: task.id,
|
|
69
|
+
status: task.status,
|
|
70
|
+
createdAt: task.createdAt,
|
|
71
|
+
updatedAt: task.updatedAt,
|
|
72
|
+
extraData,
|
|
73
|
+
logs: [],
|
|
74
|
+
logsLength: task.logsLength || 0
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async getTaskDetail(taskId, logLimit = 100) {
|
|
79
|
+
const task = await this.taskRepo.findById(taskId);
|
|
80
|
+
if (!task) return null;
|
|
81
|
+
const status = task.status;
|
|
82
|
+
const logs = status === "running" ? [] : await this.getTaskLogs(taskId, logLimit);
|
|
83
|
+
const extraData = task.extraData ? {
|
|
84
|
+
id: taskId,
|
|
85
|
+
...task.extraData
|
|
86
|
+
} : void 0;
|
|
87
|
+
return {
|
|
88
|
+
id: taskId,
|
|
89
|
+
status,
|
|
90
|
+
createdAt: task.createdAt,
|
|
91
|
+
updatedAt: task.updatedAt,
|
|
92
|
+
extraData,
|
|
93
|
+
logs,
|
|
94
|
+
logsLength: task.logsLength || logs.length
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async getTaskLogs(taskId, limit) {
|
|
98
|
+
const rows = await this.taskRepo.findRecentLogsByTaskId(taskId, limit);
|
|
99
|
+
console.log("rows === ", rows);
|
|
100
|
+
return rows.map((row) => ({
|
|
101
|
+
timestamp: row.createdAt,
|
|
102
|
+
level: row.logLevel ?? "info",
|
|
103
|
+
message: row.content ?? "",
|
|
104
|
+
data: row.extraData ?? void 0,
|
|
105
|
+
traceId: row.traceId ?? taskId
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
async indexWebsiteDoc(task) {
|
|
109
|
+
if (!task.data || task.data.id == null) throw new Error("task 必须包含 id");
|
|
110
|
+
const { slug, id: taskId, name: docName, url } = task.data;
|
|
111
|
+
const { tokens, snippets } = await generateWebsiteData({
|
|
112
|
+
url,
|
|
113
|
+
bizDocId: slug
|
|
114
|
+
}, task);
|
|
115
|
+
task.logInfo(`Indexed website ${slug} successfully`);
|
|
116
|
+
const record = await this.docRepo.create({
|
|
117
|
+
slug,
|
|
118
|
+
name: docName,
|
|
119
|
+
source: "website",
|
|
120
|
+
url,
|
|
121
|
+
taskId,
|
|
122
|
+
tokens,
|
|
123
|
+
snippets
|
|
124
|
+
});
|
|
125
|
+
task.logInfo(`Add document ${slug} with slug ${record.slug} successfully`);
|
|
126
|
+
return record;
|
|
127
|
+
}
|
|
128
|
+
async indexGitDoc(task, source) {
|
|
129
|
+
if (!task.data || task.data.id == null) throw new Error("task 必须包含 id");
|
|
130
|
+
const { slug, id: taskId, name: docName, url } = task.data;
|
|
131
|
+
const { tokens, snippets } = await generateGitRepositoryData({
|
|
132
|
+
url,
|
|
133
|
+
bizDocId: slug
|
|
134
|
+
}, task);
|
|
135
|
+
task.logInfo(`Indexed git repository ${slug} successfully`);
|
|
136
|
+
const record = await this.docRepo.create({
|
|
137
|
+
slug,
|
|
138
|
+
name: docName,
|
|
139
|
+
source,
|
|
140
|
+
url,
|
|
141
|
+
taskId,
|
|
142
|
+
tokens,
|
|
143
|
+
snippets
|
|
144
|
+
});
|
|
145
|
+
task.logInfo(`Add document ${slug} with slug ${record.slug} successfully`);
|
|
146
|
+
return record;
|
|
147
|
+
}
|
|
148
|
+
async createLogStream(taskId) {
|
|
149
|
+
const stream = new PassThrough({ objectMode: true });
|
|
150
|
+
const channel = `task:${taskId}:events`;
|
|
151
|
+
const listKey = `task:${taskId}:logs`;
|
|
152
|
+
if (!await this.queue.getJob(taskId)) {
|
|
153
|
+
stream.end();
|
|
154
|
+
return stream;
|
|
155
|
+
}
|
|
156
|
+
const existingLogs = await this.cache.lrange(listKey, 0, -1);
|
|
157
|
+
for (const logStr of existingLogs) try {
|
|
158
|
+
const entry = JSON.parse(logStr);
|
|
159
|
+
stream.write({
|
|
160
|
+
type: "log",
|
|
161
|
+
entry
|
|
162
|
+
});
|
|
163
|
+
} catch {
|
|
164
|
+
logger.error(`Failed to parse log entry: ${logStr}`);
|
|
165
|
+
}
|
|
166
|
+
const subRedis = this.cache.duplicate();
|
|
167
|
+
await subRedis.subscribe(channel);
|
|
168
|
+
subRedis.on("message", (ch, message) => {
|
|
169
|
+
if (ch !== channel) return;
|
|
170
|
+
try {
|
|
171
|
+
const event = JSON.parse(message);
|
|
172
|
+
stream.write(event);
|
|
173
|
+
if (event.type === "end") {
|
|
174
|
+
subRedis.unsubscribe();
|
|
175
|
+
subRedis.quit();
|
|
176
|
+
stream.end();
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
logger.error(`Failed to parse event: ${message}`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
stream.on("close", () => {
|
|
183
|
+
subRedis.unsubscribe();
|
|
184
|
+
subRedis.quit();
|
|
185
|
+
});
|
|
186
|
+
return stream;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
__decorate([
|
|
190
|
+
Cache({
|
|
191
|
+
tags: () => ["task:list"],
|
|
192
|
+
ttl: 300
|
|
193
|
+
}),
|
|
194
|
+
__decorateMetadata("design:type", Function),
|
|
195
|
+
__decorateMetadata("design:paramtypes", [String, Number]),
|
|
196
|
+
__decorateMetadata("design:returntype", Promise)
|
|
197
|
+
], TaskService.prototype, "getTaskLogs", null);
|
|
198
|
+
TaskService = __decorate([CacheableService("task"), __decorateMetadata("design:paramtypes", [])], TaskService);
|
|
199
|
+
|
|
200
|
+
//#endregion
|
|
201
|
+
export { TaskService };
|
|
202
|
+
//# sourceMappingURL=task.service.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.service.mjs","names":[],"sources":["../../../src/modules/task/task.service.ts"],"sourcesContent":["import type { Redis } from 'ioredis'\nimport type { Readable } from 'node:stream'\nimport type { DocSourceEnumDTO, TaskDocDTO } from '@/modules/doc/doc.dto'\nimport type { TaskLogEntry, TaskStatus } from '@/modules/task/task.entity'\nimport type { TaskVO } from '@/modules/task/task.vo'\nimport { PassThrough } from 'node:stream'\nimport { generateGitRepositoryData, generateWebsiteData } from '@/modules/doc/infrastructure/agent/engine/generate'\nimport { TaskContext } from '@/modules/task/infrastructure/mq/task-context'\nimport { TaskQueue } from '@/modules/task/infrastructure/mq/task-queue'\nimport { TaskWorker } from '@/modules/task/infrastructure/mq/task-worker'\nimport { getRepoDeps } from '@/shared/deps'\nimport { logger } from '@/shared/logger'\nimport { createRedisClient } from '@/shared/redis'\nimport { Cache, CacheableService } from '@/shared/redis/decorator'\n\n@CacheableService('task')\nexport class TaskService {\n private worker: TaskWorker | null = null\n private queue: TaskQueue\n private cache: Redis\n\n private get taskRepo() {\n return getRepoDeps().taskRepo\n }\n\n private get docRepo() {\n return getRepoDeps().docRepo\n }\n\n constructor() {\n this.cache = createRedisClient()\n this.queue = new TaskQueue('task-queue', this.cache)\n this.initWorker()\n }\n\n initWorker() {\n if (this.worker)\n return this.worker\n\n this.worker = new TaskWorker('task-queue', async (job) => {\n const ctx = new TaskContext(job, logger)\n const taskId = job.data.id\n const source = job.data?.source\n\n ctx.logInfo('Task started')\n try {\n if (source === 'website') {\n await this.indexWebsiteDoc(ctx)\n }\n else if (source === 'github' || source === 'gitee') {\n await this.indexGitDoc(ctx, source)\n }\n else {\n throw new Error(`Unknown source: ${source}`)\n }\n\n await this.taskRepo.updateStatus(taskId, 'completed')\n ctx.logInfo('Task updated in database with status completed')\n }\n catch (error: any) {\n await this.taskRepo.updateStatus(taskId, 'failed', error.message)\n ctx.logError(`Task updated in database with status failed: ${error.message}`)\n throw error\n }\n }, this.cache)\n\n return this.worker\n }\n\n async submitTask(type: string, data: Omit<TaskDocDTO, 'id'>) {\n const taskEntity = await this.taskRepo.create({\n type,\n status: 'running',\n extraData: data,\n })\n\n const job = await this.queue.add(type, { ...data, id: taskEntity.id }, { jobId: taskEntity.id })\n return job\n }\n\n async listRecentTasks(limit: number = 20): Promise<TaskVO<TaskDocDTO>[]> {\n const tasks = await this.taskRepo.findRecentTasks(limit)\n\n return tasks.map((task) => {\n const extraData = task.extraData\n ? ({ id: task.id, ...(task.extraData as any) } as TaskDocDTO)\n : undefined\n\n return {\n id: task.id,\n status: task.status,\n createdAt: task.createdAt,\n updatedAt: task.updatedAt,\n extraData,\n logs: [],\n logsLength: task.logsLength || 0,\n }\n })\n }\n\n async getTaskDetail(taskId: string, logLimit: number = 100): Promise<TaskVO<TaskDocDTO> | null> {\n const task = await this.taskRepo.findById(taskId)\n if (!task)\n return null\n\n const status = task.status as TaskStatus\n const logs = status === 'running' ? [] : await this.getTaskLogs(taskId, logLimit)\n\n const extraData = task.extraData\n ? ({ id: taskId, ...(task.extraData as any) } as TaskDocDTO)\n : undefined\n\n return {\n id: taskId,\n status,\n createdAt: task.createdAt,\n updatedAt: task.updatedAt,\n extraData,\n logs,\n logsLength: task.logsLength || logs.length,\n }\n }\n\n @Cache({ tags: () => ['task:list'], ttl: 300 })\n private async getTaskLogs(taskId: string, limit: number): Promise<TaskLogEntry[]> {\n const rows = await this.taskRepo.findRecentLogsByTaskId(taskId, limit)\n console.log('rows === ', rows)\n return rows.map(row => ({\n timestamp: row.createdAt,\n level: (row.logLevel ?? 'info') as any,\n message: row.content ?? '',\n data: row.extraData ?? undefined,\n traceId: row.traceId ?? taskId,\n })) satisfies TaskLogEntry[]\n }\n\n async indexWebsiteDoc(task: TaskContext<TaskDocDTO>) {\n if (!task.data || task.data.id == null) {\n throw new Error('task 必须包含 id')\n }\n\n const { slug, id: taskId, name: docName, url } = task.data\n const { tokens, snippets } = await generateWebsiteData({ url, bizDocId: slug }, task)\n task.logInfo(`Indexed website ${slug} successfully`)\n const record = await this.docRepo.create({ slug, name: docName, source: 'website', url, taskId, tokens, snippets })\n task.logInfo(`Add document ${slug} with slug ${record.slug} successfully`)\n return record\n }\n\n async indexGitDoc(task: TaskContext<TaskDocDTO>, source: DocSourceEnumDTO) {\n if (!task.data || task.data.id == null) {\n throw new Error('task 必须包含 id')\n }\n\n const { slug, id: taskId, name: docName, url } = task.data\n const { tokens, snippets } = await generateGitRepositoryData({ url, bizDocId: slug }, task)\n task.logInfo(`Indexed git repository ${slug} successfully`)\n const record = await this.docRepo.create({ slug, name: docName, source, url, taskId, tokens, snippets })\n task.logInfo(`Add document ${slug} with slug ${record.slug} successfully`)\n return record\n }\n\n async createLogStream(taskId: string): Promise<Readable> {\n const stream = new PassThrough({ objectMode: true })\n const channel = `task:${taskId}:events`\n const listKey = `task:${taskId}:logs`\n\n // 判断是否存在job,不存在则结束流;存在说明job未结束,监听job的log;\n const job = await this.queue.getJob(taskId)\n if (!job) {\n stream.end()\n return stream\n }\n\n const existingLogs = await this.cache.lrange(listKey, 0, -1)\n for (const logStr of existingLogs) {\n try {\n const entry = JSON.parse(logStr) as TaskLogEntry\n stream.write({ type: 'log', entry })\n }\n catch {\n logger.error(`Failed to parse log entry: ${logStr}`)\n }\n }\n\n const subRedis = this.cache.duplicate()\n await subRedis.subscribe(channel)\n\n subRedis.on('message', (ch, message) => {\n if (ch !== channel)\n return\n try {\n const event = JSON.parse(message)\n stream.write(event)\n\n if (event.type === 'end') {\n subRedis.unsubscribe()\n subRedis.quit()\n stream.end()\n }\n }\n catch {\n logger.error(`Failed to parse event: ${message}`)\n }\n })\n\n stream.on('close', () => {\n subRedis.unsubscribe()\n subRedis.quit()\n })\n\n return stream\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TaskLogEntry, TaskStatus } from "./task.entity.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/modules/task/task.vo.d.ts
|
|
4
|
+
interface TaskVO<TExtraData = unknown> {
|
|
5
|
+
id: string;
|
|
6
|
+
status: TaskStatus;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
updatedAt: number;
|
|
9
|
+
extraData?: TExtraData;
|
|
10
|
+
logs: TaskLogEntry[];
|
|
11
|
+
logsLength: number;
|
|
12
|
+
}
|
|
13
|
+
interface TaskListVO {
|
|
14
|
+
tasks: TaskVO[];
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { TaskListVO, TaskStatus, TaskVO };
|
|
18
|
+
//# sourceMappingURL=task.vo.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.vo.d.mts","names":[],"sources":["../../../src/modules/task/task.vo.ts"],"mappings":";;;UAIiB,MAAA;EACf,EAAA;EACA,MAAA,EAAQ,UAAA;EACR,SAAA;EACA,SAAA;EACA,SAAA,GAAY,UAAA;EACZ,IAAA,EAAM,YAAA;EACN,UAAA;AAAA;AAAA,UAGe,UAAA;EACf,KAAA,EAAO,MAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PaginationVO } from "../../../shared/vo/index.mjs";
|
|
2
|
+
import { CreateUserDTO, UpdateSelfDTO, UpdateUserDTO } from "../interfaces/user.dto.mjs";
|
|
3
|
+
import { LoginResultVO, UserVO } from "../interfaces/user.vo.mjs";
|
|
4
|
+
import { UserRole } from "../domain/user.entity.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/modules/user/application/user.service.d.ts
|
|
7
|
+
declare function login(input: Pick<CreateUserDTO, 'username' | 'password'>): Promise<LoginResultVO>;
|
|
8
|
+
declare function getUserById(id: number): Promise<UserVO | null>;
|
|
9
|
+
declare function listUsers(page: number, pageSize: number, filters?: {
|
|
10
|
+
name?: string;
|
|
11
|
+
}): Promise<PaginationVO<UserVO>>;
|
|
12
|
+
declare function createUser(input: CreateUserDTO): Promise<UserVO>;
|
|
13
|
+
declare function updateUserById(id: number, input: UpdateUserDTO): Promise<UserVO | null>;
|
|
14
|
+
declare function updateUserWithValidation(id: number, input: UpdateUserDTO): Promise<UserVO | null>;
|
|
15
|
+
declare function updateSelf(id: number, input: UpdateSelfDTO): Promise<UserVO | null>;
|
|
16
|
+
declare function softDeleteUser(id: number): Promise<UserVO | null>;
|
|
17
|
+
declare function getUserRoles(id: number): Promise<UserRole[] | null>;
|
|
18
|
+
declare function updateUserRoles(id: number, roles: UserRole[]): Promise<{
|
|
19
|
+
user: UserVO | null;
|
|
20
|
+
roles: UserRole[];
|
|
21
|
+
}>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { createUser, getUserById, getUserRoles, listUsers, login, softDeleteUser, updateSelf, updateUserById, updateUserRoles, updateUserWithValidation };
|
|
24
|
+
//# sourceMappingURL=user.service.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.service.d.mts","names":[],"sources":["../../../../src/modules/user/application/user.service.ts"],"mappings":";;;;;;iBAasB,KAAA,CAAM,KAAA,EAAO,IAAA,CAAK,aAAA,6BAA0C,OAAA,CAAQ,aAAA;AAAA,iBA6BpE,WAAA,CAAY,EAAA,WAAa,OAAA,CAAQ,MAAA;AAAA,iBAMjC,SAAA,CACpB,IAAA,UACA,QAAA,UACA,OAAA;EAAY,IAAA;AAAA,IACX,OAAA,CAAQ,YAAA,CAAa,MAAA;AAAA,iBASF,UAAA,CAAW,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA,iBA6B1C,cAAA,CAAe,EAAA,UAAY,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA,iBAU1D,wBAAA,CAAyB,EAAA,UAAY,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA,iBA4CpE,UAAA,CAAW,EAAA,UAAY,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,MAAA;AAAA,iBAUtD,cAAA,CAAe,EAAA,WAAa,OAAA,CAAQ,MAAA;AAAA,iBAiBpC,YAAA,CAAa,EAAA,WAAa,OAAA,CAAQ,QAAA;AAAA,iBAclC,eAAA,CAAgB,EAAA,UAAY,KAAA,EAAO,QAAA,KAAa,OAAA;EAAU,IAAA,EAAM,MAAA;EAAe,KAAA,EAAO,QAAA;AAAA"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import AppSettings from "../../../settings.mjs";
|
|
2
|
+
import { logger } from "../../../shared/logger.mjs";
|
|
3
|
+
import { getEnforcer } from "../infrastructure/casbin/enforcer.mjs";
|
|
4
|
+
import "../../../rank.service-D2h-2iJA.mjs";
|
|
5
|
+
import { UserEntity } from "../domain/user.entity.mjs";
|
|
6
|
+
import { getRepoDeps } from "../../../shared/deps.mjs";
|
|
7
|
+
import { userSchema } from "../interfaces/user.vo.mjs";
|
|
8
|
+
import { sign } from "hono/jwt";
|
|
9
|
+
import argon2 from "argon2";
|
|
10
|
+
|
|
11
|
+
//#region src/modules/user/application/user.service.ts
|
|
12
|
+
async function login(input) {
|
|
13
|
+
const { userRepo } = getRepoDeps();
|
|
14
|
+
const authUser = await userRepo.findByUsername(input.username);
|
|
15
|
+
if (!authUser) throw new Error("用户不存在");
|
|
16
|
+
if (!await argon2.verify(authUser.password, input.password)) throw new Error("密码错误");
|
|
17
|
+
const user = new UserEntity({
|
|
18
|
+
id: authUser.id,
|
|
19
|
+
username: authUser.username,
|
|
20
|
+
password: authUser.password,
|
|
21
|
+
name: "",
|
|
22
|
+
phone: "",
|
|
23
|
+
email: "",
|
|
24
|
+
role: authUser.role,
|
|
25
|
+
status: authUser.status,
|
|
26
|
+
createdAt: Date.now(),
|
|
27
|
+
updatedAt: Date.now()
|
|
28
|
+
});
|
|
29
|
+
user.ensureActive();
|
|
30
|
+
const token = await sign({
|
|
31
|
+
sub: String(user.id),
|
|
32
|
+
username: user.username,
|
|
33
|
+
role: user.role
|
|
34
|
+
}, AppSettings.config.jwt_secret, "HS256");
|
|
35
|
+
logger.info({
|
|
36
|
+
userId: user.id,
|
|
37
|
+
username: user.username
|
|
38
|
+
}, "user login");
|
|
39
|
+
return {
|
|
40
|
+
user: userSchema.parse(user),
|
|
41
|
+
token
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function getUserById(id) {
|
|
45
|
+
const { userRepo } = getRepoDeps();
|
|
46
|
+
const user = await userRepo.findById(id);
|
|
47
|
+
return user ? userSchema.parse(user) : null;
|
|
48
|
+
}
|
|
49
|
+
async function listUsers(page, pageSize, filters) {
|
|
50
|
+
const { userRepo } = getRepoDeps();
|
|
51
|
+
const result = await userRepo.list(page, pageSize, filters);
|
|
52
|
+
return {
|
|
53
|
+
...result,
|
|
54
|
+
list: result.list.map((item) => userSchema.parse(item))
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function createUser(input) {
|
|
58
|
+
const { userRepo } = getRepoDeps();
|
|
59
|
+
if (await userRepo.findByUsername(input.username)) throw new Error("用户已存在");
|
|
60
|
+
if (await userRepo.findByPhone(input.phone)) throw new Error("手机号已存在");
|
|
61
|
+
if (await userRepo.findByEmail(input.email)) throw new Error("邮箱已存在");
|
|
62
|
+
const hashedPassword = await argon2.hash(input.password);
|
|
63
|
+
const created = await userRepo.create({
|
|
64
|
+
...input,
|
|
65
|
+
password: hashedPassword
|
|
66
|
+
});
|
|
67
|
+
const primaryRole = input.role ?? "user";
|
|
68
|
+
await getEnforcer().addRoleForUser(created.username, primaryRole);
|
|
69
|
+
logger.info({
|
|
70
|
+
userId: created.id,
|
|
71
|
+
username: created.username
|
|
72
|
+
}, "user created");
|
|
73
|
+
return userSchema.parse(created);
|
|
74
|
+
}
|
|
75
|
+
async function updateUserById(id, input) {
|
|
76
|
+
const { userRepo } = getRepoDeps();
|
|
77
|
+
const patch = { ...input };
|
|
78
|
+
if (patch.password !== void 0) patch.password = await argon2.hash(patch.password);
|
|
79
|
+
const updated = await userRepo.updateById(id, patch);
|
|
80
|
+
return updated ? userSchema.parse(updated) : null;
|
|
81
|
+
}
|
|
82
|
+
async function updateUserWithValidation(id, input) {
|
|
83
|
+
const { userRepo } = getRepoDeps();
|
|
84
|
+
const existing = await userRepo.findById(id);
|
|
85
|
+
if (!existing) return null;
|
|
86
|
+
const patch = { ...input };
|
|
87
|
+
if (patch.username !== void 0) {
|
|
88
|
+
const userByName = await userRepo.findByUsername(patch.username);
|
|
89
|
+
if (userByName && userByName.id !== id) throw new Error("用户名已存在");
|
|
90
|
+
}
|
|
91
|
+
if (patch.phone !== void 0) {
|
|
92
|
+
const userByPhone = await userRepo.findByPhone(patch.phone);
|
|
93
|
+
if (userByPhone && userByPhone.id !== id) throw new Error("手机号已存在");
|
|
94
|
+
}
|
|
95
|
+
if (patch.email !== void 0) {
|
|
96
|
+
const userByEmail = await userRepo.findByEmail(patch.email);
|
|
97
|
+
if (userByEmail && userByEmail.id !== id) throw new Error("邮箱已存在");
|
|
98
|
+
}
|
|
99
|
+
const updated = await updateUserById(id, patch);
|
|
100
|
+
if (!updated) return null;
|
|
101
|
+
if (patch.role !== void 0 && patch.role !== existing.role) {
|
|
102
|
+
const enforcer = getEnforcer();
|
|
103
|
+
await enforcer.deleteRolesForUser(existing.username);
|
|
104
|
+
await enforcer.addRoleForUser(existing.username, patch.role);
|
|
105
|
+
}
|
|
106
|
+
return updated;
|
|
107
|
+
}
|
|
108
|
+
async function updateSelf(id, input) {
|
|
109
|
+
const { userRepo } = getRepoDeps();
|
|
110
|
+
const patch = { ...input };
|
|
111
|
+
if (patch.password !== void 0) patch.password = await argon2.hash(patch.password);
|
|
112
|
+
const updated = await userRepo.updateSelf(id, patch);
|
|
113
|
+
return updated ? userSchema.parse(updated) : null;
|
|
114
|
+
}
|
|
115
|
+
async function softDeleteUser(id) {
|
|
116
|
+
const existing = await getUserById(id);
|
|
117
|
+
if (!existing) return null;
|
|
118
|
+
const updated = await updateUserById(id, { status: "inactive" });
|
|
119
|
+
if (!updated) return null;
|
|
120
|
+
await getEnforcer().deleteUser(existing.username);
|
|
121
|
+
return updated;
|
|
122
|
+
}
|
|
123
|
+
async function getUserRoles(id) {
|
|
124
|
+
const user = await getUserById(id);
|
|
125
|
+
if (!user) return null;
|
|
126
|
+
const casbinRoles = (await getEnforcer().getRolesForUser(user.username)).filter((r) => r === "admin" || r === "user");
|
|
127
|
+
if (casbinRoles.length === 0) return [user.role];
|
|
128
|
+
return casbinRoles;
|
|
129
|
+
}
|
|
130
|
+
async function updateUserRoles(id, roles) {
|
|
131
|
+
const primaryRole = roles[0];
|
|
132
|
+
const user = await getUserById(id);
|
|
133
|
+
if (!user) return {
|
|
134
|
+
user: null,
|
|
135
|
+
roles: []
|
|
136
|
+
};
|
|
137
|
+
const updated = await updateUserById(id, { role: primaryRole });
|
|
138
|
+
if (!updated) return {
|
|
139
|
+
user: null,
|
|
140
|
+
roles: []
|
|
141
|
+
};
|
|
142
|
+
const enforcer = getEnforcer();
|
|
143
|
+
await enforcer.deleteRolesForUser(user.username);
|
|
144
|
+
for (const role of roles) await enforcer.addRoleForUser(user.username, role);
|
|
145
|
+
return {
|
|
146
|
+
user: updated,
|
|
147
|
+
roles
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
export { createUser, getUserById, getUserRoles, listUsers, login, softDeleteUser, updateSelf, updateUserById, updateUserRoles, updateUserWithValidation };
|
|
153
|
+
//# sourceMappingURL=user.service.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.service.mjs","names":[],"sources":["../../../../src/modules/user/application/user.service.ts"],"sourcesContent":["import type { UserRole } from '@/modules/user/domain/user.entity'\nimport type { CreateUserDTO, UpdateSelfDTO, UpdateUserDTO } from '@/modules/user/interfaces/user.dto'\nimport type { LoginResultVO, UserVO } from '@/modules/user/interfaces/user.vo'\nimport type { PaginationVO } from '@/shared/vo'\nimport argon2 from 'argon2'\nimport { sign } from 'hono/jwt'\nimport { UserEntity } from '@/modules/user/domain/user.entity'\nimport { getEnforcer } from '@/modules/user/infrastructure/casbin/enforcer'\nimport { userSchema } from '@/modules/user/interfaces/user.vo'\nimport AppSettings from '@/settings'\nimport { getRepoDeps } from '@/shared/deps'\nimport { logger } from '@/shared/logger'\n\nexport async function login(input: Pick<CreateUserDTO, 'username' | 'password'>): Promise<LoginResultVO> {\n const { userRepo } = getRepoDeps()\n const authUser = await userRepo.findByUsername(input.username)\n if (!authUser) {\n throw new Error('用户不存在')\n }\n\n const valid = await argon2.verify(authUser.password, input.password)\n if (!valid) {\n throw new Error('密码错误')\n }\n const user = new UserEntity({\n id: authUser.id,\n username: authUser.username,\n password: authUser.password,\n name: '',\n phone: '',\n email: '',\n role: authUser.role,\n status: authUser.status,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n })\n user.ensureActive()\n const token = await sign({ sub: String(user.id), username: user.username, role: user.role }, AppSettings.config.jwt_secret, 'HS256')\n logger.info({ userId: user.id, username: user.username }, 'user login')\n return { user: userSchema.parse(user), token }\n}\n\nexport async function getUserById(id: number): Promise<UserVO | null> {\n const { userRepo } = getRepoDeps()\n const user = await userRepo.findById(id)\n return user ? userSchema.parse(user) : null\n}\n\nexport async function listUsers(\n page: number,\n pageSize: number,\n filters?: { name?: string },\n): Promise<PaginationVO<UserVO>> {\n const { userRepo } = getRepoDeps()\n const result = await userRepo.list(page, pageSize, filters)\n return {\n ...result,\n list: result.list.map(item => userSchema.parse(item)),\n }\n}\n\nexport async function createUser(input: CreateUserDTO): Promise<UserVO> {\n const { userRepo } = getRepoDeps()\n\n const hasUser = await userRepo.findByUsername(input.username)\n if (hasUser) {\n throw new Error('用户已存在')\n }\n\n const hasPhone = await userRepo.findByPhone(input.phone)\n if (hasPhone) {\n throw new Error('手机号已存在')\n }\n\n const hasEmail = await userRepo.findByEmail(input.email)\n if (hasEmail) {\n throw new Error('邮箱已存在')\n }\n\n const hashedPassword = await argon2.hash(input.password)\n const created = await userRepo.create({ ...input, password: hashedPassword })\n\n const primaryRole: UserRole = input.role ?? 'user'\n const enforcer = getEnforcer()\n await enforcer.addRoleForUser(created.username, primaryRole)\n\n logger.info({ userId: created.id, username: created.username }, 'user created')\n return userSchema.parse(created)\n}\n\nexport async function updateUserById(id: number, input: UpdateUserDTO): Promise<UserVO | null> {\n const { userRepo } = getRepoDeps()\n const patch: UpdateUserDTO = { ...input }\n if (patch.password !== undefined) {\n patch.password = await argon2.hash(patch.password)\n }\n const updated = await userRepo.updateById(id, patch)\n return updated ? userSchema.parse(updated) : null\n}\n\nexport async function updateUserWithValidation(id: number, input: UpdateUserDTO): Promise<UserVO | null> {\n const { userRepo } = getRepoDeps()\n const existing = await userRepo.findById(id)\n if (!existing) {\n return null\n }\n\n const patch: UpdateUserDTO = { ...input }\n\n if (patch.username !== undefined) {\n const userByName = await userRepo.findByUsername(patch.username)\n if (userByName && userByName.id !== id) {\n throw new Error('用户名已存在')\n }\n }\n\n if (patch.phone !== undefined) {\n const userByPhone = await userRepo.findByPhone(patch.phone)\n if (userByPhone && userByPhone.id !== id) {\n throw new Error('手机号已存在')\n }\n }\n\n if (patch.email !== undefined) {\n const userByEmail = await userRepo.findByEmail(patch.email)\n if (userByEmail && userByEmail.id !== id) {\n throw new Error('邮箱已存在')\n }\n }\n\n const updated = await updateUserById(id, patch)\n if (!updated) {\n return null\n }\n\n if (patch.role !== undefined && patch.role !== existing.role) {\n const enforcer = getEnforcer()\n await enforcer.deleteRolesForUser(existing.username)\n await enforcer.addRoleForUser(existing.username, patch.role)\n }\n\n return updated\n}\n\nexport async function updateSelf(id: number, input: UpdateSelfDTO): Promise<UserVO | null> {\n const { userRepo } = getRepoDeps()\n const patch: UpdateSelfDTO = { ...input }\n if (patch.password !== undefined) {\n patch.password = await argon2.hash(patch.password)\n }\n const updated = await userRepo.updateSelf(id, patch)\n return updated ? userSchema.parse(updated) : null\n}\n\nexport async function softDeleteUser(id: number): Promise<UserVO | null> {\n const existing = await getUserById(id)\n if (!existing) {\n return null\n }\n\n const updated = await updateUserById(id, { status: 'inactive' })\n if (!updated) {\n return null\n }\n\n const enforcer = getEnforcer()\n await enforcer.deleteUser(existing.username)\n\n return updated\n}\n\nexport async function getUserRoles(id: number): Promise<UserRole[] | null> {\n const user = await getUserById(id)\n if (!user) {\n return null\n }\n const enforcer = getEnforcer()\n const roles = await enforcer.getRolesForUser(user.username)\n const casbinRoles = roles.filter((r): r is UserRole => r === 'admin' || r === 'user')\n if (casbinRoles.length === 0) {\n return [user.role]\n }\n return casbinRoles\n}\n\nexport async function updateUserRoles(id: number, roles: UserRole[]): Promise<{ user: UserVO | null, roles: UserRole[] }> {\n const primaryRole = roles[0]\n const user = await getUserById(id)\n if (!user) {\n return { user: null, roles: [] }\n }\n\n const updated = await updateUserById(id, { role: primaryRole })\n if (!updated) {\n return { user: null, roles: [] }\n }\n\n const enforcer = getEnforcer()\n await enforcer.deleteRolesForUser(user.username)\n for (const role of roles) {\n await enforcer.addRoleForUser(user.username, role)\n }\n\n return { user: updated, roles }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { UserPgPO, UserSqlitePO } from "../infrastructure/user.po.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/modules/user/domain/user.entity.d.ts
|
|
4
|
+
type UserPO = UserPgPO | UserSqlitePO;
|
|
5
|
+
type UserRole = 'admin' | 'user';
|
|
6
|
+
type UserStatus = 'active' | 'inactive';
|
|
7
|
+
declare class UserEntity {
|
|
8
|
+
id: number;
|
|
9
|
+
username: string;
|
|
10
|
+
password: string;
|
|
11
|
+
name: string;
|
|
12
|
+
phone: string;
|
|
13
|
+
email: string;
|
|
14
|
+
role: UserRole;
|
|
15
|
+
status: UserStatus;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
updatedAt: number;
|
|
18
|
+
constructor(data: UserPO);
|
|
19
|
+
isActive(): boolean;
|
|
20
|
+
ensureActive(): void;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { UserEntity, UserRole, UserStatus };
|
|
24
|
+
//# sourceMappingURL=user.entity.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.entity.d.mts","names":[],"sources":["../../../../src/modules/user/domain/user.entity.ts"],"mappings":";;;KAEK,MAAA,GAAS,QAAA,GAAW,YAAA;AAAA,KACb,QAAA;AAAA,KACA,UAAA;AAAA,cAEC,UAAA;EACX,EAAA;EACA,QAAA;EACA,QAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA;EACA,IAAA,EAAM,QAAA;EACN,MAAA,EAAQ,UAAA;EACR,SAAA;EACA,SAAA;cAEY,IAAA,EAAM,MAAA;EAalB,QAAA,CAAA;EAIA,YAAA,CAAA;AAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/modules/user/domain/user.entity.ts
|
|
2
|
+
var UserEntity = class {
|
|
3
|
+
id;
|
|
4
|
+
username;
|
|
5
|
+
password;
|
|
6
|
+
name;
|
|
7
|
+
phone;
|
|
8
|
+
email;
|
|
9
|
+
role;
|
|
10
|
+
status;
|
|
11
|
+
createdAt;
|
|
12
|
+
updatedAt;
|
|
13
|
+
constructor(data) {
|
|
14
|
+
this.id = data.id ?? 0;
|
|
15
|
+
this.username = data.username ?? "";
|
|
16
|
+
this.name = data.name ?? "";
|
|
17
|
+
this.phone = data.phone ?? "";
|
|
18
|
+
this.email = data.email ?? "";
|
|
19
|
+
this.role = data.role ?? "user";
|
|
20
|
+
this.status = data.status ?? "active";
|
|
21
|
+
this.password = data.password ?? "";
|
|
22
|
+
this.createdAt = data.createdAt;
|
|
23
|
+
this.updatedAt = data.updatedAt;
|
|
24
|
+
}
|
|
25
|
+
isActive() {
|
|
26
|
+
return this.status === "active";
|
|
27
|
+
}
|
|
28
|
+
ensureActive() {
|
|
29
|
+
if (!this.isActive()) throw new Error("用户已被禁用");
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { UserEntity };
|
|
35
|
+
//# sourceMappingURL=user.entity.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.entity.mjs","names":[],"sources":["../../../../src/modules/user/domain/user.entity.ts"],"sourcesContent":["import type { UserPgPO, UserSqlitePO } from '../infrastructure/user.po'\n\ntype UserPO = UserPgPO | UserSqlitePO\nexport type UserRole = 'admin' | 'user'\nexport type UserStatus = 'active' | 'inactive'\n\nexport class UserEntity {\n id: number\n username: string\n password: string\n name: string\n phone: string\n email: string\n role: UserRole\n status: UserStatus\n createdAt: number\n updatedAt: number\n\n constructor(data: UserPO) {\n this.id = data.id ?? 0\n this.username = data.username ?? ''\n this.name = data.name ?? ''\n this.phone = data.phone ?? ''\n this.email = data.email ?? ''\n this.role = (data.role ?? 'user') as UserRole\n this.status = (data.status ?? 'active') as UserStatus\n this.password = data.password ?? ''\n this.createdAt = data.createdAt\n this.updatedAt = data.updatedAt\n }\n\n isActive(): boolean {\n return this.status === 'active'\n }\n\n ensureActive(): void {\n if (!this.isActive()) {\n throw new Error('用户已被禁用')\n }\n }\n}\n"],"mappings":";AAMA,IAAa,aAAb,MAAwB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,MAAc;AACxB,OAAK,KAAK,KAAK,MAAM;AACrB,OAAK,WAAW,KAAK,YAAY;AACjC,OAAK,OAAO,KAAK,QAAQ;AACzB,OAAK,QAAQ,KAAK,SAAS;AAC3B,OAAK,QAAQ,KAAK,SAAS;AAC3B,OAAK,OAAQ,KAAK,QAAQ;AAC1B,OAAK,SAAU,KAAK,UAAU;AAC9B,OAAK,WAAW,KAAK,YAAY;AACjC,OAAK,YAAY,KAAK;AACtB,OAAK,YAAY,KAAK;;CAGxB,WAAoB;AAClB,SAAO,KAAK,WAAW;;CAGzB,eAAqB;AACnB,MAAI,CAAC,KAAK,UAAU,CAClB,OAAM,IAAI,MAAM,SAAS"}
|