@fivepixels-js/lite-api 0.2.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/LICENSE +93 -0
- package/README.md +537 -0
- package/dist/app.d.ts +10 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +70 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/identity.repository.d.ts +62 -0
- package/dist/auth/identity.repository.d.ts.map +1 -0
- package/dist/auth/identity.repository.js +202 -0
- package/dist/auth/identity.repository.js.map +1 -0
- package/dist/auth/identity.routes.d.ts +5 -0
- package/dist/auth/identity.routes.d.ts.map +1 -0
- package/dist/auth/identity.routes.js +56 -0
- package/dist/auth/identity.routes.js.map +1 -0
- package/dist/auth/identity.schemas.d.ts +2711 -0
- package/dist/auth/identity.schemas.d.ts.map +1 -0
- package/dist/auth/identity.schemas.js +117 -0
- package/dist/auth/identity.schemas.js.map +1 -0
- package/dist/auth/identity.service.d.ts +37 -0
- package/dist/auth/identity.service.d.ts.map +1 -0
- package/dist/auth/identity.service.js +131 -0
- package/dist/auth/identity.service.js.map +1 -0
- package/dist/auth/password.service.d.ts +15 -0
- package/dist/auth/password.service.d.ts.map +1 -0
- package/dist/auth/password.service.js +116 -0
- package/dist/auth/password.service.js.map +1 -0
- package/dist/auth/token.service.d.ts +38 -0
- package/dist/auth/token.service.d.ts.map +1 -0
- package/dist/auth/token.service.js +54 -0
- package/dist/auth/token.service.js.map +1 -0
- package/dist/cli/cli.d.ts +3 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +132 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/commands/backup.d.ts +4 -0
- package/dist/cli/commands/backup.d.ts.map +1 -0
- package/dist/cli/commands/backup.js +24 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/init.d.ts +4 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +23 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +4 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +20 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/start.d.ts +4 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +13 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +4 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +48 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/io.d.ts +6 -0
- package/dist/cli/io.d.ts.map +1 -0
- package/dist/cli/io.js +9 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/community/contracts.d.ts +29 -0
- package/dist/community/contracts.d.ts.map +1 -0
- package/dist/community/contracts.js +41 -0
- package/dist/community/contracts.js.map +1 -0
- package/dist/community/error-handler.d.ts +3 -0
- package/dist/community/error-handler.d.ts.map +1 -0
- package/dist/community/error-handler.js +66 -0
- package/dist/community/error-handler.js.map +1 -0
- package/dist/community/errors.d.ts +10 -0
- package/dist/community/errors.d.ts.map +1 -0
- package/dist/community/errors.js +39 -0
- package/dist/community/errors.js.map +1 -0
- package/dist/community/etag.d.ts +5 -0
- package/dist/community/etag.d.ts.map +1 -0
- package/dist/community/etag.js +31 -0
- package/dist/community/etag.js.map +1 -0
- package/dist/community/input.d.ts +10 -0
- package/dist/community/input.d.ts.map +1 -0
- package/dist/community/input.js +48 -0
- package/dist/community/input.js.map +1 -0
- package/dist/community/mutation-route.d.ts +7 -0
- package/dist/community/mutation-route.d.ts.map +1 -0
- package/dist/community/mutation-route.js +47 -0
- package/dist/community/mutation-route.js.map +1 -0
- package/dist/community/schemas.d.ts +807 -0
- package/dist/community/schemas.d.ts.map +1 -0
- package/dist/community/schemas.js +100 -0
- package/dist/community/schemas.js.map +1 -0
- package/dist/community/system.schemas.d.ts +40 -0
- package/dist/community/system.schemas.d.ts.map +1 -0
- package/dist/community/system.schemas.js +30 -0
- package/dist/community/system.schemas.js.map +1 -0
- package/dist/config/config.d.ts +43 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +208 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/data-path.d.ts +2 -0
- package/dist/config/data-path.d.ts.map +1 -0
- package/dist/config/data-path.js +16 -0
- package/dist/config/data-path.js.map +1 -0
- package/dist/config/jwt-secret.d.ts +3 -0
- package/dist/config/jwt-secret.d.ts.map +1 -0
- package/dist/config/jwt-secret.js +67 -0
- package/dist/config/jwt-secret.js.map +1 -0
- package/dist/db/backup.d.ts +10 -0
- package/dist/db/backup.d.ts.map +1 -0
- package/dist/db/backup.js +51 -0
- package/dist/db/backup.js.map +1 -0
- package/dist/db/connection.d.ts +15 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +82 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/guid-order.d.ts +2 -0
- package/dist/db/guid-order.d.ts.map +1 -0
- package/dist/db/guid-order.js +9 -0
- package/dist/db/guid-order.js.map +1 -0
- package/dist/db/migrate.d.ts +37 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +145 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/migrations/001_schema_migrations.sql +6 -0
- package/dist/db/migrations/002_identity.sql +95 -0
- package/dist/db/migrations/003_projects.sql +62 -0
- package/dist/db/migrations/004_feedback_core.sql +236 -0
- package/dist/db/migrations/005_project_member_management.sql +55 -0
- package/dist/db/migrations/006_align_credentials.sql +33 -0
- package/dist/db/migrations/007_align_storage_values.sql +78 -0
- package/dist/db/storage-migration.d.ts +9 -0
- package/dist/db/storage-migration.d.ts.map +1 -0
- package/dist/db/storage-migration.js +79 -0
- package/dist/db/storage-migration.js.map +1 -0
- package/dist/db/transaction.d.ts +9 -0
- package/dist/db/transaction.d.ts.map +1 -0
- package/dist/db/transaction.js +14 -0
- package/dist/db/transaction.js.map +1 -0
- package/dist/errors/error-handler.d.ts +3 -0
- package/dist/errors/error-handler.d.ts.map +1 -0
- package/dist/errors/error-handler.js +50 -0
- package/dist/errors/error-handler.js.map +1 -0
- package/dist/feedbacks/feedback-workflow.service.d.ts +59 -0
- package/dist/feedbacks/feedback-workflow.service.d.ts.map +1 -0
- package/dist/feedbacks/feedback-workflow.service.js +89 -0
- package/dist/feedbacks/feedback-workflow.service.js.map +1 -0
- package/dist/feedbacks/feedback.input.d.ts +4 -0
- package/dist/feedbacks/feedback.input.d.ts.map +1 -0
- package/dist/feedbacks/feedback.input.js +25 -0
- package/dist/feedbacks/feedback.input.js.map +1 -0
- package/dist/feedbacks/feedback.repository.d.ts +48 -0
- package/dist/feedbacks/feedback.repository.d.ts.map +1 -0
- package/dist/feedbacks/feedback.repository.js +238 -0
- package/dist/feedbacks/feedback.repository.js.map +1 -0
- package/dist/feedbacks/feedback.routes.d.ts +4 -0
- package/dist/feedbacks/feedback.routes.d.ts.map +1 -0
- package/dist/feedbacks/feedback.routes.js +78 -0
- package/dist/feedbacks/feedback.routes.js.map +1 -0
- package/dist/feedbacks/feedback.schemas.d.ts +642 -0
- package/dist/feedbacks/feedback.schemas.d.ts.map +1 -0
- package/dist/feedbacks/feedback.schemas.js +131 -0
- package/dist/feedbacks/feedback.schemas.js.map +1 -0
- package/dist/feedbacks/feedback.service.d.ts +23 -0
- package/dist/feedbacks/feedback.service.d.ts.map +1 -0
- package/dist/feedbacks/feedback.service.js +74 -0
- package/dist/feedbacks/feedback.service.js.map +1 -0
- package/dist/feedbacks/feedback.types.d.ts +64 -0
- package/dist/feedbacks/feedback.types.d.ts.map +1 -0
- package/dist/feedbacks/feedback.types.js +2 -0
- package/dist/feedbacks/feedback.types.js.map +1 -0
- package/dist/page-elements/page-element.input.d.ts +7 -0
- package/dist/page-elements/page-element.input.d.ts.map +1 -0
- package/dist/page-elements/page-element.input.js +59 -0
- package/dist/page-elements/page-element.input.js.map +1 -0
- package/dist/page-elements/page-element.repository.d.ts +21 -0
- package/dist/page-elements/page-element.repository.d.ts.map +1 -0
- package/dist/page-elements/page-element.repository.js +123 -0
- package/dist/page-elements/page-element.repository.js.map +1 -0
- package/dist/page-elements/page-element.schemas.d.ts +97 -0
- package/dist/page-elements/page-element.schemas.d.ts.map +1 -0
- package/dist/page-elements/page-element.schemas.js +40 -0
- package/dist/page-elements/page-element.schemas.js.map +1 -0
- package/dist/page-elements/page-element.types.d.ts +24 -0
- package/dist/page-elements/page-element.types.d.ts.map +1 -0
- package/dist/page-elements/page-element.types.js +2 -0
- package/dist/page-elements/page-element.types.js.map +1 -0
- package/dist/plugins/auth.plugin.d.ts +3 -0
- package/dist/plugins/auth.plugin.d.ts.map +1 -0
- package/dist/plugins/auth.plugin.js +28 -0
- package/dist/plugins/auth.plugin.js.map +1 -0
- package/dist/plugins/community-api.plugin.d.ts +5 -0
- package/dist/plugins/community-api.plugin.d.ts.map +1 -0
- package/dist/plugins/community-api.plugin.js +25 -0
- package/dist/plugins/community-api.plugin.js.map +1 -0
- package/dist/plugins/cors.plugin.d.ts +3 -0
- package/dist/plugins/cors.plugin.d.ts.map +1 -0
- package/dist/plugins/cors.plugin.js +19 -0
- package/dist/plugins/cors.plugin.js.map +1 -0
- package/dist/plugins/swagger.plugin.d.ts +3 -0
- package/dist/plugins/swagger.plugin.d.ts.map +1 -0
- package/dist/plugins/swagger.plugin.js +59 -0
- package/dist/plugins/swagger.plugin.js.map +1 -0
- package/dist/plugins/user-auth.plugin.d.ts +33 -0
- package/dist/plugins/user-auth.plugin.d.ts.map +1 -0
- package/dist/plugins/user-auth.plugin.js +62 -0
- package/dist/plugins/user-auth.plugin.js.map +1 -0
- package/dist/projects/members/project-member.repository.d.ts +49 -0
- package/dist/projects/members/project-member.repository.d.ts.map +1 -0
- package/dist/projects/members/project-member.repository.js +215 -0
- package/dist/projects/members/project-member.repository.js.map +1 -0
- package/dist/projects/members/project-member.routes.d.ts +4 -0
- package/dist/projects/members/project-member.routes.d.ts.map +1 -0
- package/dist/projects/members/project-member.routes.js +82 -0
- package/dist/projects/members/project-member.routes.js.map +1 -0
- package/dist/projects/members/project-member.schemas.d.ts +2735 -0
- package/dist/projects/members/project-member.schemas.d.ts.map +1 -0
- package/dist/projects/members/project-member.schemas.js +138 -0
- package/dist/projects/members/project-member.schemas.js.map +1 -0
- package/dist/projects/members/project-member.service.d.ts +29 -0
- package/dist/projects/members/project-member.service.d.ts.map +1 -0
- package/dist/projects/members/project-member.service.js +92 -0
- package/dist/projects/members/project-member.service.js.map +1 -0
- package/dist/projects/project-access.policy.d.ts +11 -0
- package/dist/projects/project-access.policy.d.ts.map +1 -0
- package/dist/projects/project-access.policy.js +30 -0
- package/dist/projects/project-access.policy.js.map +1 -0
- package/dist/projects/project-access.repository.d.ts +12 -0
- package/dist/projects/project-access.repository.d.ts.map +1 -0
- package/dist/projects/project-access.repository.js +45 -0
- package/dist/projects/project-access.repository.js.map +1 -0
- package/dist/projects/project-access.types.d.ts +9 -0
- package/dist/projects/project-access.types.d.ts.map +1 -0
- package/dist/projects/project-access.types.js +2 -0
- package/dist/projects/project-access.types.js.map +1 -0
- package/dist/projects/project-mutation.service.d.ts +15 -0
- package/dist/projects/project-mutation.service.d.ts.map +1 -0
- package/dist/projects/project-mutation.service.js +39 -0
- package/dist/projects/project-mutation.service.js.map +1 -0
- package/dist/projects/project.repository.d.ts +49 -0
- package/dist/projects/project.repository.d.ts.map +1 -0
- package/dist/projects/project.repository.js +155 -0
- package/dist/projects/project.repository.js.map +1 -0
- package/dist/projects/project.routes.d.ts +4 -0
- package/dist/projects/project.routes.d.ts.map +1 -0
- package/dist/projects/project.routes.js +45 -0
- package/dist/projects/project.routes.js.map +1 -0
- package/dist/projects/project.schemas.d.ts +2044 -0
- package/dist/projects/project.schemas.d.ts.map +1 -0
- package/dist/projects/project.schemas.js +90 -0
- package/dist/projects/project.schemas.js.map +1 -0
- package/dist/projects/project.service.d.ts +40 -0
- package/dist/projects/project.service.d.ts.map +1 -0
- package/dist/projects/project.service.js +128 -0
- package/dist/projects/project.service.js.map +1 -0
- package/dist/replies/reply-mutation.service.d.ts +19 -0
- package/dist/replies/reply-mutation.service.d.ts.map +1 -0
- package/dist/replies/reply-mutation.service.js +51 -0
- package/dist/replies/reply-mutation.service.js.map +1 -0
- package/dist/replies/reply.input.d.ts +4 -0
- package/dist/replies/reply.input.d.ts.map +1 -0
- package/dist/replies/reply.input.js +33 -0
- package/dist/replies/reply.input.js.map +1 -0
- package/dist/replies/reply.repository.d.ts +40 -0
- package/dist/replies/reply.repository.d.ts.map +1 -0
- package/dist/replies/reply.repository.js +173 -0
- package/dist/replies/reply.repository.js.map +1 -0
- package/dist/replies/reply.routes.d.ts +4 -0
- package/dist/replies/reply.routes.d.ts.map +1 -0
- package/dist/replies/reply.routes.js +64 -0
- package/dist/replies/reply.routes.js.map +1 -0
- package/dist/replies/reply.schemas.d.ts +251 -0
- package/dist/replies/reply.schemas.d.ts.map +1 -0
- package/dist/replies/reply.schemas.js +51 -0
- package/dist/replies/reply.schemas.js.map +1 -0
- package/dist/replies/reply.service.d.ts +19 -0
- package/dist/replies/reply.service.d.ts.map +1 -0
- package/dist/replies/reply.service.js +50 -0
- package/dist/replies/reply.service.js.map +1 -0
- package/dist/replies/reply.types.d.ts +31 -0
- package/dist/replies/reply.types.d.ts.map +1 -0
- package/dist/replies/reply.types.js +2 -0
- package/dist/replies/reply.types.js.map +1 -0
- package/dist/report-cases/report-case-workflow.service.d.ts +39 -0
- package/dist/report-cases/report-case-workflow.service.d.ts.map +1 -0
- package/dist/report-cases/report-case-workflow.service.js +103 -0
- package/dist/report-cases/report-case-workflow.service.js.map +1 -0
- package/dist/report-cases/report-case.input.d.ts +4 -0
- package/dist/report-cases/report-case.input.d.ts.map +1 -0
- package/dist/report-cases/report-case.input.js +29 -0
- package/dist/report-cases/report-case.input.js.map +1 -0
- package/dist/report-cases/report-case.repository.d.ts +45 -0
- package/dist/report-cases/report-case.repository.d.ts.map +1 -0
- package/dist/report-cases/report-case.repository.js +185 -0
- package/dist/report-cases/report-case.repository.js.map +1 -0
- package/dist/report-cases/report-case.routes.d.ts +4 -0
- package/dist/report-cases/report-case.routes.d.ts.map +1 -0
- package/dist/report-cases/report-case.routes.js +94 -0
- package/dist/report-cases/report-case.routes.js.map +1 -0
- package/dist/report-cases/report-case.schemas.d.ts +294 -0
- package/dist/report-cases/report-case.schemas.d.ts.map +1 -0
- package/dist/report-cases/report-case.schemas.js +77 -0
- package/dist/report-cases/report-case.schemas.js.map +1 -0
- package/dist/report-cases/report-case.service.d.ts +21 -0
- package/dist/report-cases/report-case.service.d.ts.map +1 -0
- package/dist/report-cases/report-case.service.js +60 -0
- package/dist/report-cases/report-case.service.js.map +1 -0
- package/dist/report-cases/report-case.types.d.ts +39 -0
- package/dist/report-cases/report-case.types.d.ts.map +1 -0
- package/dist/report-cases/report-case.types.js +2 -0
- package/dist/report-cases/report-case.types.js.map +1 -0
- package/dist/routes/health.route.d.ts +4 -0
- package/dist/routes/health.route.d.ts.map +1 -0
- package/dist/routes/health.route.js +39 -0
- package/dist/routes/health.route.js.map +1 -0
- package/dist/routes/version.route.d.ts +3 -0
- package/dist/routes/version.route.d.ts.map +1 -0
- package/dist/routes/version.route.js +22 -0
- package/dist/routes/version.route.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +11 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +66 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/values.d.ts +3 -0
- package/dist/storage/values.d.ts.map +1 -0
- package/dist/storage/values.js +30 -0
- package/dist/storage/values.js.map +1 -0
- package/dist/users/current-user.routes.d.ts +3 -0
- package/dist/users/current-user.routes.d.ts.map +1 -0
- package/dist/users/current-user.routes.js +9 -0
- package/dist/users/current-user.routes.js.map +1 -0
- package/dist/users/current-user.schemas.d.ts +641 -0
- package/dist/users/current-user.schemas.d.ts.map +1 -0
- package/dist/users/current-user.schemas.js +21 -0
- package/dist/users/current-user.schemas.js.map +1 -0
- package/dist/users/user-search.repository.d.ts +19 -0
- package/dist/users/user-search.repository.d.ts.map +1 -0
- package/dist/users/user-search.repository.js +55 -0
- package/dist/users/user-search.repository.js.map +1 -0
- package/dist/users/user-search.routes.d.ts +4 -0
- package/dist/users/user-search.routes.d.ts.map +1 -0
- package/dist/users/user-search.routes.js +19 -0
- package/dist/users/user-search.routes.js.map +1 -0
- package/dist/users/user-search.schemas.d.ts +693 -0
- package/dist/users/user-search.schemas.d.ts.map +1 -0
- package/dist/users/user-search.schemas.js +41 -0
- package/dist/users/user-search.schemas.js.map +1 -0
- package/dist/users/user-search.service.d.ts +20 -0
- package/dist/users/user-search.service.d.ts.map +1 -0
- package/dist/users/user-search.service.js +41 -0
- package/dist/users/user-search.service.js.map +1 -0
- package/dist/users/user.schemas.d.ts +47 -0
- package/dist/users/user.schemas.d.ts.map +1 -0
- package/dist/users/user.schemas.js +13 -0
- package/dist/users/user.schemas.js.map +1 -0
- package/dist/users/user.types.d.ts +6 -0
- package/dist/users/user.types.d.ts.map +1 -0
- package/dist/users/user.types.js +2 -0
- package/dist/users/user.types.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/docs/guides/community-api-quickstart.md +171 -0
- package/docs/guides/feature-structure.md +138 -0
- package/docs/guides/independent-cli.md +73 -0
- package/docs/guides/non-attachment-api-contract.md +138 -0
- package/docs/plans/selfdev-schema-alignment.md +61 -0
- package/package.json +76 -0
- package/sql/fivepixels-lite-init.sql +518 -0
package/dist/app.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import Fastify, { LogController } from 'fastify';
|
|
2
|
+
import { registerErrorHandlers } from './errors/error-handler.js';
|
|
3
|
+
import { registerCommunityApi } from './plugins/community-api.plugin.js';
|
|
4
|
+
import { registerCors } from './plugins/cors.plugin.js';
|
|
5
|
+
import { registerSwagger } from './plugins/swagger.plugin.js';
|
|
6
|
+
import { registerHealthRoute } from './routes/health.route.js';
|
|
7
|
+
import { registerVersionRoute } from './routes/version.route.js';
|
|
8
|
+
export async function buildApp(options) {
|
|
9
|
+
const requestStartedAt = new WeakMap();
|
|
10
|
+
const app = Fastify({
|
|
11
|
+
bodyLimit: 1024 * 1024,
|
|
12
|
+
logController: new LogController({ disableRequestLogging: true }),
|
|
13
|
+
trustProxy: options.config.trustedProxies.length > 0
|
|
14
|
+
? options.config.trustedProxies
|
|
15
|
+
: false,
|
|
16
|
+
logger: options.logger === false ? false : {
|
|
17
|
+
level: options.config.logLevel,
|
|
18
|
+
redact: {
|
|
19
|
+
paths: [
|
|
20
|
+
'req.headers.authorization',
|
|
21
|
+
'request.headers.authorization',
|
|
22
|
+
'req.body.password',
|
|
23
|
+
'request.body.password',
|
|
24
|
+
'req.body.refreshToken',
|
|
25
|
+
'request.body.refreshToken',
|
|
26
|
+
],
|
|
27
|
+
censor: '[REDACTED]',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
app.addHook('onRequest', (request, _reply, done) => {
|
|
32
|
+
requestStartedAt.set(request, process.hrtime.bigint());
|
|
33
|
+
done();
|
|
34
|
+
});
|
|
35
|
+
app.addHook('onResponse', (request, reply, done) => {
|
|
36
|
+
const startedAt = requestStartedAt.get(request);
|
|
37
|
+
const durationMs = startedAt === undefined
|
|
38
|
+
? 0
|
|
39
|
+
: Number(process.hrtime.bigint() - startedAt) / 1_000_000;
|
|
40
|
+
const requestPath = request.url.split('?', 1)[0] ?? request.url;
|
|
41
|
+
const path = request.routeOptions.url || requestPath;
|
|
42
|
+
const logFields = {
|
|
43
|
+
requestId: request.id,
|
|
44
|
+
method: request.method,
|
|
45
|
+
path,
|
|
46
|
+
statusCode: reply.statusCode,
|
|
47
|
+
durationMs: Math.round(durationMs * 100) / 100,
|
|
48
|
+
};
|
|
49
|
+
if (requestPath === '/health') {
|
|
50
|
+
request.log.debug(logFields, 'Request completed');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
request.log.info(logFields, 'Request completed');
|
|
54
|
+
}
|
|
55
|
+
done();
|
|
56
|
+
});
|
|
57
|
+
registerErrorHandlers(app);
|
|
58
|
+
await registerCors(app, options.config.corsOrigins);
|
|
59
|
+
await registerSwagger(app, options.config.docsEnabled);
|
|
60
|
+
await registerCommunityApi(app, options.database, options.config);
|
|
61
|
+
registerHealthRoute(app, options.database);
|
|
62
|
+
registerVersionRoute(app);
|
|
63
|
+
app.addHook('onClose', async () => {
|
|
64
|
+
if (options.database.open)
|
|
65
|
+
options.database.close();
|
|
66
|
+
await Promise.resolve();
|
|
67
|
+
});
|
|
68
|
+
return app;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { SqliteDatabase } from '../db/connection.js';
|
|
2
|
+
export interface IdentityUserRecord {
|
|
3
|
+
createdAt: string;
|
|
4
|
+
id: string;
|
|
5
|
+
profileImageUrl: string | null;
|
|
6
|
+
statusCode: number;
|
|
7
|
+
updatedAt: string;
|
|
8
|
+
userName: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AuthenticationUserRecord {
|
|
11
|
+
id: string;
|
|
12
|
+
loginId: string;
|
|
13
|
+
profileImageUrl: string | null;
|
|
14
|
+
userName: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CredentialWithUserRecord extends AuthenticationUserRecord {
|
|
17
|
+
failedLoginCount: number;
|
|
18
|
+
lockedUntil: string | null;
|
|
19
|
+
passwordHash: string;
|
|
20
|
+
statusCode: number;
|
|
21
|
+
version: number;
|
|
22
|
+
}
|
|
23
|
+
export interface RegisterAccountInput {
|
|
24
|
+
createdAt: string;
|
|
25
|
+
email: string;
|
|
26
|
+
id: string;
|
|
27
|
+
loginId: string;
|
|
28
|
+
passwordHash: string;
|
|
29
|
+
userName: string;
|
|
30
|
+
}
|
|
31
|
+
export interface RefreshTokenInput {
|
|
32
|
+
createdAt: string;
|
|
33
|
+
expiresAt: string;
|
|
34
|
+
id: string;
|
|
35
|
+
tokenHash: Buffer;
|
|
36
|
+
userId: string;
|
|
37
|
+
}
|
|
38
|
+
export type NewRefreshTokenInput = Omit<RefreshTokenInput, 'userId'>;
|
|
39
|
+
export type RefreshRotationResult = {
|
|
40
|
+
kind: 'invalid';
|
|
41
|
+
reuseDetected: boolean;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'user-blocked';
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'rotated';
|
|
46
|
+
user: AuthenticationUserRecord;
|
|
47
|
+
};
|
|
48
|
+
export declare class IdentityRepository {
|
|
49
|
+
private readonly database;
|
|
50
|
+
constructor(database: SqliteDatabase);
|
|
51
|
+
registerAccount(input: RegisterAccountInput): void;
|
|
52
|
+
findCredentialByLoginId(loginId: string): CredentialWithUserRecord | undefined;
|
|
53
|
+
recordFailedLogin(userId: string, now: string, lockedUntil: string, maxFailedLoginAttempts: number): void;
|
|
54
|
+
completeLogin(credential: CredentialWithUserRecord, now: string, refreshToken: NewRefreshTokenInput, replacementHash?: string): boolean;
|
|
55
|
+
rotateRefreshToken(tokenHash: Buffer, replacement: NewRefreshTokenInput, now: string): RefreshRotationResult;
|
|
56
|
+
logout(tokenHash: Buffer, now: string): void;
|
|
57
|
+
findUserById(userId: string): IdentityUserRecord | undefined;
|
|
58
|
+
private insertRefreshToken;
|
|
59
|
+
private revokeRefreshToken;
|
|
60
|
+
private revokeActiveRefreshTokens;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=identity.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.repository.d.ts","sourceRoot":"","sources":["../../src/auth/identity.repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,wBAAwB,CAAA;CAAE,CAAC;AA0BxD,qBAAa,kBAAkB;IACV,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,cAAc;IAErD,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI;IAmClD,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS;IAmB9E,iBAAiB,CACtB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,sBAAsB,EAAE,MAAM,GAC7B,IAAI;IAoBA,aAAa,CAClB,UAAU,EAAE,wBAAwB,EACpC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,oBAAoB,EAClC,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO;IAqBH,kBAAkB,CACvB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,oBAAoB,EACjC,GAAG,EAAE,MAAM,GACV,qBAAqB;IAuEjB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAQ5C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAcnE,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,yBAAyB;CAOlC"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
export class IdentityRepository {
|
|
2
|
+
database;
|
|
3
|
+
constructor(database) {
|
|
4
|
+
this.database = database;
|
|
5
|
+
}
|
|
6
|
+
registerAccount(input) {
|
|
7
|
+
this.database.transaction(() => {
|
|
8
|
+
this.database.prepare(`
|
|
9
|
+
INSERT INTO users (
|
|
10
|
+
user_id, status_code, user_name, profile_image_url, created_at, updated_at
|
|
11
|
+
) VALUES (?, 1, ?, NULL, ?, ?)
|
|
12
|
+
`).run(input.id, input.userName, input.createdAt, input.createdAt);
|
|
13
|
+
this.database.prepare(`
|
|
14
|
+
INSERT INTO local_credentials (
|
|
15
|
+
user_id,
|
|
16
|
+
login_id,
|
|
17
|
+
email,
|
|
18
|
+
password_hash,
|
|
19
|
+
failed_login_count,
|
|
20
|
+
locked_until,
|
|
21
|
+
email_verified_at,
|
|
22
|
+
password_changed_at,
|
|
23
|
+
last_login_at,
|
|
24
|
+
lock_version,
|
|
25
|
+
created_at,
|
|
26
|
+
updated_at
|
|
27
|
+
) VALUES (?, ?, ?, ?, 0, NULL, NULL, ?, NULL, 0, ?, ?)
|
|
28
|
+
`).run(input.id, input.loginId, input.email, input.passwordHash, input.createdAt, input.createdAt, input.createdAt);
|
|
29
|
+
}).immediate();
|
|
30
|
+
}
|
|
31
|
+
findCredentialByLoginId(loginId) {
|
|
32
|
+
const row = this.database.prepare(`
|
|
33
|
+
SELECT
|
|
34
|
+
users.user_id AS id,
|
|
35
|
+
users.status_code AS statusCode,
|
|
36
|
+
users.user_name AS userName,
|
|
37
|
+
users.profile_image_url AS profileImageUrl,
|
|
38
|
+
local_credentials.login_id AS loginId,
|
|
39
|
+
local_credentials.password_hash AS passwordHash,
|
|
40
|
+
local_credentials.failed_login_count AS failedLoginCount,
|
|
41
|
+
local_credentials.locked_until AS lockedUntil,
|
|
42
|
+
local_credentials.lock_version AS version
|
|
43
|
+
FROM local_credentials
|
|
44
|
+
INNER JOIN users ON users.user_id = local_credentials.user_id
|
|
45
|
+
WHERE local_credentials.login_id = ? COLLATE NOCASE
|
|
46
|
+
`).get(loginId);
|
|
47
|
+
return row;
|
|
48
|
+
}
|
|
49
|
+
recordFailedLogin(userId, now, lockedUntil, maxFailedLoginAttempts) {
|
|
50
|
+
const nextCount = `
|
|
51
|
+
CASE
|
|
52
|
+
WHEN locked_until IS NOT NULL AND locked_until <= @now THEN 1
|
|
53
|
+
ELSE failed_login_count + 1
|
|
54
|
+
END
|
|
55
|
+
`;
|
|
56
|
+
this.database.prepare(`
|
|
57
|
+
UPDATE local_credentials
|
|
58
|
+
SET failed_login_count = ${nextCount},
|
|
59
|
+
locked_until = CASE
|
|
60
|
+
WHEN (${nextCount}) >= @maxFailedLoginAttempts THEN @lockedUntil
|
|
61
|
+
ELSE NULL
|
|
62
|
+
END,
|
|
63
|
+
updated_at = @now,
|
|
64
|
+
lock_version = lock_version + 1
|
|
65
|
+
WHERE user_id = @userId
|
|
66
|
+
`).run({ userId, now, lockedUntil, maxFailedLoginAttempts });
|
|
67
|
+
}
|
|
68
|
+
completeLogin(credential, now, refreshToken, replacementHash) {
|
|
69
|
+
return this.database.transaction(() => {
|
|
70
|
+
const updated = this.database.prepare(`
|
|
71
|
+
UPDATE local_credentials
|
|
72
|
+
SET password_hash = ?,
|
|
73
|
+
failed_login_count = 0,
|
|
74
|
+
locked_until = NULL,
|
|
75
|
+
last_login_at = ?,
|
|
76
|
+
updated_at = ?,
|
|
77
|
+
lock_version = lock_version + 1
|
|
78
|
+
WHERE user_id = ? AND password_hash = ? AND lock_version = ?
|
|
79
|
+
AND (locked_until IS NULL OR locked_until <= ?)
|
|
80
|
+
AND EXISTS (SELECT 1 FROM users WHERE user_id = local_credentials.user_id AND status_code = 1)
|
|
81
|
+
`).run(replacementHash ?? credential.passwordHash, now, now, credential.id, credential.passwordHash, credential.version, now);
|
|
82
|
+
if (updated.changes !== 1)
|
|
83
|
+
return false;
|
|
84
|
+
this.insertRefreshToken({ ...refreshToken, userId: credential.id });
|
|
85
|
+
return true;
|
|
86
|
+
}).immediate();
|
|
87
|
+
}
|
|
88
|
+
rotateRefreshToken(tokenHash, replacement, now) {
|
|
89
|
+
return this.database.transaction(() => {
|
|
90
|
+
const storedToken = this.database.prepare(`
|
|
91
|
+
SELECT
|
|
92
|
+
token.refresh_token_id AS id,
|
|
93
|
+
token.user_id AS userId,
|
|
94
|
+
token.expires_at AS expiresAt,
|
|
95
|
+
token.revoked_at AS revokedAt,
|
|
96
|
+
token.replaced_by_token_id AS replacedByTokenId,
|
|
97
|
+
users.status_code AS statusCode,
|
|
98
|
+
users.user_name AS userName,
|
|
99
|
+
users.profile_image_url AS profileImageUrl,
|
|
100
|
+
credential.login_id AS credentialLoginId
|
|
101
|
+
FROM auth_refresh_tokens AS token
|
|
102
|
+
INNER JOIN users ON users.user_id = token.user_id
|
|
103
|
+
LEFT JOIN local_credentials AS credential ON credential.user_id = token.user_id
|
|
104
|
+
WHERE token.token_hash = ?
|
|
105
|
+
`).get(tokenHash);
|
|
106
|
+
if (storedToken === undefined)
|
|
107
|
+
return { kind: 'invalid', reuseDetected: false };
|
|
108
|
+
if (storedToken.revokedAt !== null) {
|
|
109
|
+
if (storedToken.replacedByTokenId !== null) {
|
|
110
|
+
this.revokeActiveRefreshTokens(storedToken.userId, now);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
kind: 'invalid',
|
|
114
|
+
reuseDetected: storedToken.replacedByTokenId !== null,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (storedToken.expiresAt <= now) {
|
|
118
|
+
this.revokeRefreshToken(storedToken.id, now);
|
|
119
|
+
return { kind: 'invalid', reuseDetected: false };
|
|
120
|
+
}
|
|
121
|
+
if (storedToken.statusCode !== 1) {
|
|
122
|
+
this.revokeActiveRefreshTokens(storedToken.userId, now);
|
|
123
|
+
return { kind: 'user-blocked' };
|
|
124
|
+
}
|
|
125
|
+
if (storedToken.credentialLoginId === null) {
|
|
126
|
+
this.revokeActiveRefreshTokens(storedToken.userId, now);
|
|
127
|
+
return { kind: 'invalid', reuseDetected: false };
|
|
128
|
+
}
|
|
129
|
+
this.insertRefreshToken({ ...replacement, userId: storedToken.userId });
|
|
130
|
+
const updated = this.database.prepare(`
|
|
131
|
+
UPDATE auth_refresh_tokens
|
|
132
|
+
SET last_used_at = ?,
|
|
133
|
+
revoked_at = ?,
|
|
134
|
+
replaced_by_token_id = ?,
|
|
135
|
+
lock_version = lock_version + 1
|
|
136
|
+
WHERE refresh_token_id = ? AND revoked_at IS NULL
|
|
137
|
+
`).run(now, now, replacement.id, storedToken.id);
|
|
138
|
+
if (updated.changes !== 1) {
|
|
139
|
+
throw new Error('refresh-token-rotation-conflict');
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
kind: 'rotated',
|
|
143
|
+
user: {
|
|
144
|
+
id: storedToken.userId,
|
|
145
|
+
loginId: storedToken.credentialLoginId,
|
|
146
|
+
userName: storedToken.userName,
|
|
147
|
+
profileImageUrl: storedToken.profileImageUrl,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}).immediate();
|
|
151
|
+
}
|
|
152
|
+
logout(tokenHash, now) {
|
|
153
|
+
this.database.prepare(`
|
|
154
|
+
UPDATE auth_refresh_tokens
|
|
155
|
+
SET revoked_at = ?, lock_version = lock_version + 1
|
|
156
|
+
WHERE token_hash = ? AND revoked_at IS NULL
|
|
157
|
+
`).run(now, tokenHash);
|
|
158
|
+
}
|
|
159
|
+
findUserById(userId) {
|
|
160
|
+
return this.database.prepare(`
|
|
161
|
+
SELECT
|
|
162
|
+
user_id AS id,
|
|
163
|
+
status_code AS statusCode,
|
|
164
|
+
user_name AS userName,
|
|
165
|
+
profile_image_url AS profileImageUrl,
|
|
166
|
+
created_at AS createdAt,
|
|
167
|
+
updated_at AS updatedAt
|
|
168
|
+
FROM users
|
|
169
|
+
WHERE user_id = ?
|
|
170
|
+
`).get(userId);
|
|
171
|
+
}
|
|
172
|
+
insertRefreshToken(input) {
|
|
173
|
+
this.database.prepare(`
|
|
174
|
+
INSERT INTO auth_refresh_tokens (
|
|
175
|
+
refresh_token_id,
|
|
176
|
+
user_id,
|
|
177
|
+
token_hash,
|
|
178
|
+
expires_at,
|
|
179
|
+
last_used_at,
|
|
180
|
+
revoked_at,
|
|
181
|
+
replaced_by_token_id,
|
|
182
|
+
lock_version,
|
|
183
|
+
created_at
|
|
184
|
+
) VALUES (?, ?, ?, ?, NULL, NULL, NULL, 0, ?)
|
|
185
|
+
`).run(input.id, input.userId, input.tokenHash, input.expiresAt, input.createdAt);
|
|
186
|
+
}
|
|
187
|
+
revokeRefreshToken(refreshTokenId, now) {
|
|
188
|
+
this.database.prepare(`
|
|
189
|
+
UPDATE auth_refresh_tokens
|
|
190
|
+
SET revoked_at = ?, lock_version = lock_version + 1
|
|
191
|
+
WHERE refresh_token_id = ? AND revoked_at IS NULL
|
|
192
|
+
`).run(now, refreshTokenId);
|
|
193
|
+
}
|
|
194
|
+
revokeActiveRefreshTokens(userId, now) {
|
|
195
|
+
this.database.prepare(`
|
|
196
|
+
UPDATE auth_refresh_tokens
|
|
197
|
+
SET revoked_at = ?, lock_version = lock_version + 1
|
|
198
|
+
WHERE user_id = ? AND revoked_at IS NULL
|
|
199
|
+
`).run(now, userId);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=identity.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.repository.js","sourceRoot":"","sources":["../../src/auth/identity.repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { AppConfig } from '../config/config.js';
|
|
3
|
+
import type { SqliteDatabase } from '../db/connection.js';
|
|
4
|
+
export declare function registerIdentityRoutes(app: FastifyInstance, database: SqliteDatabase, config: AppConfig): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=identity.routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.routes.d.ts","sourceRoot":"","sources":["../../src/auth/identity.routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAgB1D,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,IAAI,CAAC,CAuDf"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import rateLimit from '@fastify/rate-limit';
|
|
2
|
+
import { successResponse } from '../community/contracts.js';
|
|
3
|
+
import { IdentityRepository } from './identity.repository.js';
|
|
4
|
+
import { loginRouteSchema, logoutRouteSchema, refreshRouteSchema, registerRouteSchema, } from './identity.schemas.js';
|
|
5
|
+
import { IdentityService } from './identity.service.js';
|
|
6
|
+
import { PasswordService } from './password.service.js';
|
|
7
|
+
import { TokenService } from './token.service.js';
|
|
8
|
+
export async function registerIdentityRoutes(app, database, config) {
|
|
9
|
+
await app.register(rateLimit, {
|
|
10
|
+
global: false,
|
|
11
|
+
});
|
|
12
|
+
const repository = new IdentityRepository(database);
|
|
13
|
+
const passwordService = new PasswordService();
|
|
14
|
+
const tokenService = new TokenService(config, (payload, options) => app.jwt.sign(payload, options));
|
|
15
|
+
const service = new IdentityService(repository, passwordService, tokenService, config);
|
|
16
|
+
app.post('/auth/register', {
|
|
17
|
+
schema: registerRouteSchema,
|
|
18
|
+
config: {
|
|
19
|
+
rateLimit: { max: 5, timeWindow: '1 hour', groupId: 'auth-register' },
|
|
20
|
+
},
|
|
21
|
+
}, async (request, reply) => {
|
|
22
|
+
const result = await service.register(request.body);
|
|
23
|
+
request.log.info({ userId: result.id }, 'Fivepixels registration succeeded');
|
|
24
|
+
return reply.code(201).send(successResponse(result, undefined, '회원가입했습니다.'));
|
|
25
|
+
});
|
|
26
|
+
app.post('/auth/login', {
|
|
27
|
+
schema: loginRouteSchema,
|
|
28
|
+
config: {
|
|
29
|
+
rateLimit: { max: 10, timeWindow: '1 minute', groupId: 'auth-login' },
|
|
30
|
+
},
|
|
31
|
+
}, async (request) => {
|
|
32
|
+
const result = await service.login(request.body);
|
|
33
|
+
request.log.info({ userId: result.user.id }, 'Fivepixels login succeeded');
|
|
34
|
+
return successResponse(result, undefined, '로그인했습니다.');
|
|
35
|
+
});
|
|
36
|
+
app.post('/auth/refresh', {
|
|
37
|
+
schema: refreshRouteSchema,
|
|
38
|
+
config: {
|
|
39
|
+
rateLimit: { max: 30, timeWindow: '1 minute', groupId: 'auth-refresh' },
|
|
40
|
+
},
|
|
41
|
+
}, (request) => {
|
|
42
|
+
const result = service.refresh(request.body.refreshToken);
|
|
43
|
+
request.log.info({ userId: result.user.id }, 'Fivepixels token refreshed');
|
|
44
|
+
return successResponse(result, undefined, '토큰을 갱신했습니다.');
|
|
45
|
+
});
|
|
46
|
+
app.post('/auth/logout', {
|
|
47
|
+
schema: logoutRouteSchema,
|
|
48
|
+
config: {
|
|
49
|
+
rateLimit: { max: 30, timeWindow: '1 minute', groupId: 'auth-logout' },
|
|
50
|
+
},
|
|
51
|
+
}, (request) => {
|
|
52
|
+
const result = service.logout(request.body.refreshToken);
|
|
53
|
+
return successResponse(result, undefined, '로그아웃했습니다.');
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=identity.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.routes.js","sourceRoot":"","sources":["../../src/auth/identity.routes.ts"],"names":[],"mappings":"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"}
|