teamshare-bridge 0.22.7 → 0.22.8
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 +383 -383
- package/dist/bridge/daemon.js +43 -20
- package/dist/bridge/daemon.js.map +1 -1
- package/dist/bridge/install-verify.js +8 -8
- package/dist/bridge/spawn.d.ts +14 -0
- package/dist/bridge/spawn.js +7 -1
- package/dist/bridge/spawn.js.map +1 -1
- package/dist/bridge/wake-handler.d.ts +8 -15
- package/dist/bridge/wake-handler.js +109 -14
- package/dist/bridge/wake-handler.js.map +1 -1
- package/dist/bridge/wake-outbox.js +7 -0
- package/dist/bridge/wake-outbox.js.map +1 -1
- package/dist/cli/commands/plan.js +4 -0
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/lib/browser/human-input-guard.js +98 -98
- package/dist/lib/browser/screen-change.js +60 -60
- package/dist/lib/llm/usage-reader.js +4 -4
- package/dist/lib/workspace/classify.d.ts +29 -0
- package/dist/lib/workspace/classify.js +62 -0
- package/dist/lib/workspace/classify.js.map +1 -1
- package/dist/lib/workspace/presence.d.ts +31 -0
- package/dist/lib/workspace/presence.js +35 -1
- package/dist/lib/workspace/presence.js.map +1 -1
- package/dist/lib/workspace/shim-runner.js +23 -0
- package/dist/lib/workspace/shim-runner.js.map +1 -1
- package/package.json +70 -70
- package/skills/THIRD_PARTY_NOTICES.md +26 -26
- package/skills/architect/LICENSE +21 -21
- package/skills/codebase-design/LICENSE +21 -21
- package/skills/diagnosing-bugs/LICENSE +21 -21
- package/skills/domain-modeling/LICENSE +21 -21
- package/skills/improve-codebase-architecture/LICENSE +21 -21
- package/skills/recover/LICENSE +21 -21
- package/skills/review/LICENSE +21 -21
- package/skills/tdd/LICENSE +21 -21
- package/skills/teamshare-auth-rbac/SKILL.md +78 -78
- package/skills/teamshare-cdd/SKILL.md +205 -205
- package/skills/teamshare-modularity/SKILL.md +146 -146
- package/skills/teamshare-orchestrator-ask/SKILL.md +77 -77
- package/skills/teamshare-scaffold/SKILL.md +116 -116
- package/templates/backend/.env.example.tpl +13 -13
- package/templates/backend/nest-cli.json.tpl +8 -8
- package/templates/backend/package.json.tpl +41 -41
- package/templates/backend/prisma/schema.prisma.tpl +39 -39
- package/templates/backend/src/app.module.ts.tpl +24 -24
- package/templates/backend/src/common/filters/all-exceptions.filter.ts.tpl +59 -59
- package/templates/backend/src/common/guards/jwt-auth.guard.ts.tpl +27 -27
- package/templates/backend/src/common/guards/rbac.guard.ts.tpl +48 -48
- package/templates/backend/src/common/interceptors/transform.interceptor.ts.tpl +44 -44
- package/templates/backend/src/common/pipes/zod-validation.pipe.ts.tpl +29 -29
- package/templates/backend/src/common/swagger/envelope.schemas.ts.tpl +53 -53
- package/templates/backend/src/common/swagger/error.schemas.ts.tpl +62 -62
- package/templates/backend/src/main.ts.tpl +27 -27
- package/templates/backend/src/modules/example/example.controller.ts.tpl +102 -102
- package/templates/backend/src/modules/example/example.dto.ts.tpl +14 -14
- package/templates/backend/src/modules/example/example.module.ts.tpl +10 -10
- package/templates/backend/src/modules/example/example.service.ts.tpl +45 -45
- package/templates/backend/tsconfig.json.tpl +26 -26
- package/templates/common/.gitignore.tpl +42 -42
- package/templates/common/AGENTS.md.tpl +58 -58
- package/templates/common/docs/adr/README.md +25 -25
- package/templates/common/docs/api-contract.md.tpl +72 -72
- package/templates/web/next.config.ts.tpl +7 -7
- package/templates/web/package.json.tpl +37 -37
- package/templates/web/src/app/globals.css.tpl +48 -48
- package/templates/web/src/app/layout.tsx.tpl +25 -25
- package/templates/web/src/components/shared/app-button.tsx.tpl +59 -59
- package/templates/web/src/components/shared/app-empty-state.tsx.tpl +39 -39
- package/templates/web/src/components/shared/app-list-toolbar.tsx.tpl +63 -63
- package/templates/web/src/components/shared/app-skeleton.tsx.tpl +14 -14
- package/templates/web/src/components/shared/app-table.tsx.tpl +128 -128
- package/templates/web/src/components/shared/index.ts.tpl +5 -5
- package/templates/web/src/hooks/use-list-state.ts.tpl +72 -72
- package/templates/web/src/hooks/use-url-tab.ts.tpl +25 -25
- package/templates/web/src/lib/api/client.ts.tpl +123 -123
- package/templates/web/src/lib/providers.tsx.tpl +35 -35
- package/templates/web/src/lib/query-keys.ts.tpl +21 -21
- package/templates/web/src/lib/utils.ts.tpl +7 -7
- package/templates/web/src/lib/validation/schemas.ts.tpl +15 -15
- package/templates/web/src/modules/example/views/example-list-view.tsx.tpl +97 -97
- package/templates/web/tsconfig.json.tpl +23 -23
- package/tools/workspace-arbitration-check.mjs +389 -389
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
|
|
2
|
-
import { Reflector } from "@nestjs/core";
|
|
3
|
-
import { AuthGuard as PassportAuthGuard } from "@nestjs/passport";
|
|
4
|
-
import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
|
|
5
|
-
|
|
6
|
-
@Injectable()
|
|
7
|
-
export class AuthGuard extends PassportAuthGuard("jwt") {
|
|
8
|
-
constructor(private reflector: Reflector) {
|
|
9
|
-
super();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
canActivate(context: ExecutionContext) {
|
|
13
|
-
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
14
|
-
context.getHandler(),
|
|
15
|
-
context.getClass(),
|
|
16
|
-
]);
|
|
17
|
-
if (isPublic) return true;
|
|
18
|
-
return super.canActivate(context);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
handleRequest(err: unknown, user: unknown) {
|
|
22
|
-
if (err || !user) {
|
|
23
|
-
throw err ?? new UnauthorizedException("Invalid or missing token");
|
|
24
|
-
}
|
|
25
|
-
return user;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
1
|
+
import { ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
|
|
2
|
+
import { Reflector } from "@nestjs/core";
|
|
3
|
+
import { AuthGuard as PassportAuthGuard } from "@nestjs/passport";
|
|
4
|
+
import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class AuthGuard extends PassportAuthGuard("jwt") {
|
|
8
|
+
constructor(private reflector: Reflector) {
|
|
9
|
+
super();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
canActivate(context: ExecutionContext) {
|
|
13
|
+
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
14
|
+
context.getHandler(),
|
|
15
|
+
context.getClass(),
|
|
16
|
+
]);
|
|
17
|
+
if (isPublic) return true;
|
|
18
|
+
return super.canActivate(context);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
handleRequest(err: unknown, user: unknown) {
|
|
22
|
+
if (err || !user) {
|
|
23
|
+
throw err ?? new UnauthorizedException("Invalid or missing token");
|
|
24
|
+
}
|
|
25
|
+
return user;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CanActivate,
|
|
3
|
-
ExecutionContext,
|
|
4
|
-
ForbiddenException,
|
|
5
|
-
Injectable,
|
|
6
|
-
} from "@nestjs/common";
|
|
7
|
-
import { Reflector } from "@nestjs/core";
|
|
8
|
-
import { PERMISSIONS_KEY } from "../decorators/permissions.decorator";
|
|
9
|
-
import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
|
|
10
|
-
|
|
11
|
-
@Injectable()
|
|
12
|
-
export class RbacGuard implements CanActivate {
|
|
13
|
-
constructor(private reflector: Reflector) {}
|
|
14
|
-
|
|
15
|
-
canActivate(context: ExecutionContext): boolean {
|
|
16
|
-
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
17
|
-
context.getHandler(),
|
|
18
|
-
context.getClass(),
|
|
19
|
-
]);
|
|
20
|
-
if (isPublic) return true;
|
|
21
|
-
|
|
22
|
-
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
|
|
23
|
-
PERMISSIONS_KEY,
|
|
24
|
-
[context.getHandler(), context.getClass()]
|
|
25
|
-
);
|
|
26
|
-
if (!requiredPermissions || requiredPermissions.length === 0) return true;
|
|
27
|
-
|
|
28
|
-
const request = context.switchToHttp().getRequest();
|
|
29
|
-
const user = request.user;
|
|
30
|
-
|
|
31
|
-
if (!user) {
|
|
32
|
-
throw new ForbiddenException("Authentication required");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const userPermissions: string[] = user.permissions ?? [];
|
|
36
|
-
const hasPermission = requiredPermissions.every((p) =>
|
|
37
|
-
userPermissions.includes(p)
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
if (!hasPermission) {
|
|
41
|
-
throw new ForbiddenException(
|
|
42
|
-
`Missing required permissions: ${requiredPermissions.join(", ")}`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
CanActivate,
|
|
3
|
+
ExecutionContext,
|
|
4
|
+
ForbiddenException,
|
|
5
|
+
Injectable,
|
|
6
|
+
} from "@nestjs/common";
|
|
7
|
+
import { Reflector } from "@nestjs/core";
|
|
8
|
+
import { PERMISSIONS_KEY } from "../decorators/permissions.decorator";
|
|
9
|
+
import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
|
|
10
|
+
|
|
11
|
+
@Injectable()
|
|
12
|
+
export class RbacGuard implements CanActivate {
|
|
13
|
+
constructor(private reflector: Reflector) {}
|
|
14
|
+
|
|
15
|
+
canActivate(context: ExecutionContext): boolean {
|
|
16
|
+
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
17
|
+
context.getHandler(),
|
|
18
|
+
context.getClass(),
|
|
19
|
+
]);
|
|
20
|
+
if (isPublic) return true;
|
|
21
|
+
|
|
22
|
+
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
|
|
23
|
+
PERMISSIONS_KEY,
|
|
24
|
+
[context.getHandler(), context.getClass()]
|
|
25
|
+
);
|
|
26
|
+
if (!requiredPermissions || requiredPermissions.length === 0) return true;
|
|
27
|
+
|
|
28
|
+
const request = context.switchToHttp().getRequest();
|
|
29
|
+
const user = request.user;
|
|
30
|
+
|
|
31
|
+
if (!user) {
|
|
32
|
+
throw new ForbiddenException("Authentication required");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const userPermissions: string[] = user.permissions ?? [];
|
|
36
|
+
const hasPermission = requiredPermissions.every((p) =>
|
|
37
|
+
userPermissions.includes(p)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (!hasPermission) {
|
|
41
|
+
throw new ForbiddenException(
|
|
42
|
+
`Missing required permissions: ${requiredPermissions.join(", ")}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Injectable,
|
|
3
|
-
NestInterceptor,
|
|
4
|
-
ExecutionContext,
|
|
5
|
-
CallHandler,
|
|
6
|
-
} from "@nestjs/common";
|
|
7
|
-
import { Observable, map } from "rxjs";
|
|
8
|
-
|
|
9
|
-
export interface SuccessResponse<T> {
|
|
10
|
-
success: true;
|
|
11
|
-
data: T;
|
|
12
|
-
pagination?: {
|
|
13
|
-
page: number;
|
|
14
|
-
limit: number;
|
|
15
|
-
total: number;
|
|
16
|
-
totalPages: number;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
@Injectable()
|
|
21
|
-
export class TransformInterceptor<T>
|
|
22
|
-
implements NestInterceptor<T, SuccessResponse<T>>
|
|
23
|
-
{
|
|
24
|
-
intercept(
|
|
25
|
-
context: ExecutionContext,
|
|
26
|
-
next: CallHandler
|
|
27
|
-
): Observable<SuccessResponse<T>> {
|
|
28
|
-
return next.handle().pipe(
|
|
29
|
-
map((data) => {
|
|
30
|
-
if (data && typeof data === "object" && "data" in data && "pagination" in data) {
|
|
31
|
-
return {
|
|
32
|
-
success: true as const,
|
|
33
|
-
data: data.data,
|
|
34
|
-
pagination: data.pagination,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
success: true as const,
|
|
39
|
-
data,
|
|
40
|
-
};
|
|
41
|
-
})
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
Injectable,
|
|
3
|
+
NestInterceptor,
|
|
4
|
+
ExecutionContext,
|
|
5
|
+
CallHandler,
|
|
6
|
+
} from "@nestjs/common";
|
|
7
|
+
import { Observable, map } from "rxjs";
|
|
8
|
+
|
|
9
|
+
export interface SuccessResponse<T> {
|
|
10
|
+
success: true;
|
|
11
|
+
data: T;
|
|
12
|
+
pagination?: {
|
|
13
|
+
page: number;
|
|
14
|
+
limit: number;
|
|
15
|
+
total: number;
|
|
16
|
+
totalPages: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Injectable()
|
|
21
|
+
export class TransformInterceptor<T>
|
|
22
|
+
implements NestInterceptor<T, SuccessResponse<T>>
|
|
23
|
+
{
|
|
24
|
+
intercept(
|
|
25
|
+
context: ExecutionContext,
|
|
26
|
+
next: CallHandler
|
|
27
|
+
): Observable<SuccessResponse<T>> {
|
|
28
|
+
return next.handle().pipe(
|
|
29
|
+
map((data) => {
|
|
30
|
+
if (data && typeof data === "object" && "data" in data && "pagination" in data) {
|
|
31
|
+
return {
|
|
32
|
+
success: true as const,
|
|
33
|
+
data: data.data,
|
|
34
|
+
pagination: data.pagination,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
success: true as const,
|
|
39
|
+
data,
|
|
40
|
+
};
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PipeTransform,
|
|
3
|
-
Injectable,
|
|
4
|
-
BadRequestException,
|
|
5
|
-
} from "@nestjs/common";
|
|
6
|
-
import { ZodSchema } from "zod";
|
|
7
|
-
|
|
8
|
-
@Injectable()
|
|
9
|
-
export class ZodValidationPipe implements PipeTransform {
|
|
10
|
-
constructor(
|
|
11
|
-
private schema: ZodSchema,
|
|
12
|
-
private source: "body" | "query" | "params" = "body"
|
|
13
|
-
) {}
|
|
14
|
-
|
|
15
|
-
transform(value: unknown) {
|
|
16
|
-
const result = this.schema.safeParse(value);
|
|
17
|
-
if (!result.success) {
|
|
18
|
-
const issues = result.error.issues.map((i) => ({
|
|
19
|
-
path: i.path.join("."),
|
|
20
|
-
message: i.message,
|
|
21
|
-
}));
|
|
22
|
-
throw new BadRequestException({
|
|
23
|
-
message: "Validation failed",
|
|
24
|
-
details: issues,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
return result.data;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
PipeTransform,
|
|
3
|
+
Injectable,
|
|
4
|
+
BadRequestException,
|
|
5
|
+
} from "@nestjs/common";
|
|
6
|
+
import { ZodSchema } from "zod";
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class ZodValidationPipe implements PipeTransform {
|
|
10
|
+
constructor(
|
|
11
|
+
private schema: ZodSchema,
|
|
12
|
+
private source: "body" | "query" | "params" = "body"
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
transform(value: unknown) {
|
|
16
|
+
const result = this.schema.safeParse(value);
|
|
17
|
+
if (!result.success) {
|
|
18
|
+
const issues = result.error.issues.map((i) => ({
|
|
19
|
+
path: i.path.join("."),
|
|
20
|
+
message: i.message,
|
|
21
|
+
}));
|
|
22
|
+
throw new BadRequestException({
|
|
23
|
+
message: "Validation failed",
|
|
24
|
+
details: issues,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return result.data;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
export function successSchema(dataSchema: Record<string, unknown>) {
|
|
3
|
-
return {
|
|
4
|
-
type: "object",
|
|
5
|
-
properties: {
|
|
6
|
-
success: { type: "boolean", example: true },
|
|
7
|
-
data: dataSchema,
|
|
8
|
-
},
|
|
9
|
-
required: ["success", "data"],
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function paginatedSchema(dataSchema: Record<string, unknown>) {
|
|
14
|
-
return {
|
|
15
|
-
type: "object",
|
|
16
|
-
properties: {
|
|
17
|
-
success: { type: "boolean", example: true },
|
|
18
|
-
data: {
|
|
19
|
-
type: "array",
|
|
20
|
-
items: dataSchema,
|
|
21
|
-
},
|
|
22
|
-
pagination: {
|
|
23
|
-
type: "object",
|
|
24
|
-
properties: {
|
|
25
|
-
page: { type: "number", example: 1 },
|
|
26
|
-
limit: { type: "number", example: 20 },
|
|
27
|
-
total: { type: "number", example: 100 },
|
|
28
|
-
totalPages: { type: "number", example: 5 },
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
required: ["success", "data", "pagination"],
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function errorSchema() {
|
|
37
|
-
return {
|
|
38
|
-
type: "object",
|
|
39
|
-
properties: {
|
|
40
|
-
success: { type: "boolean", example: false },
|
|
41
|
-
error: {
|
|
42
|
-
type: "object",
|
|
43
|
-
properties: {
|
|
44
|
-
code: { type: "string", example: "NOT_FOUND" },
|
|
45
|
-
message: { type: "string", example: "Resource not found" },
|
|
46
|
-
details: {},
|
|
47
|
-
},
|
|
48
|
-
required: ["code", "message"],
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
required: ["success", "error"],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
1
|
+
|
|
2
|
+
export function successSchema(dataSchema: Record<string, unknown>) {
|
|
3
|
+
return {
|
|
4
|
+
type: "object",
|
|
5
|
+
properties: {
|
|
6
|
+
success: { type: "boolean", example: true },
|
|
7
|
+
data: dataSchema,
|
|
8
|
+
},
|
|
9
|
+
required: ["success", "data"],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function paginatedSchema(dataSchema: Record<string, unknown>) {
|
|
14
|
+
return {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
success: { type: "boolean", example: true },
|
|
18
|
+
data: {
|
|
19
|
+
type: "array",
|
|
20
|
+
items: dataSchema,
|
|
21
|
+
},
|
|
22
|
+
pagination: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
page: { type: "number", example: 1 },
|
|
26
|
+
limit: { type: "number", example: 20 },
|
|
27
|
+
total: { type: "number", example: 100 },
|
|
28
|
+
totalPages: { type: "number", example: 5 },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ["success", "data", "pagination"],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function errorSchema() {
|
|
37
|
+
return {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
success: { type: "boolean", example: false },
|
|
41
|
+
error: {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
code: { type: "string", example: "NOT_FOUND" },
|
|
45
|
+
message: { type: "string", example: "Resource not found" },
|
|
46
|
+
details: {},
|
|
47
|
+
},
|
|
48
|
+
required: ["code", "message"],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ["success", "error"],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import { applyDecorators } from "@nestjs/common";
|
|
2
|
-
import { ApiResponse } from "@nestjs/swagger";
|
|
3
|
-
|
|
4
|
-
export const INTERNAL_ERROR_CODES = [
|
|
5
|
-
"INTERNAL_ERROR",
|
|
6
|
-
"BAD_REQUEST",
|
|
7
|
-
"UNAUTHORIZED",
|
|
8
|
-
"FORBIDDEN",
|
|
9
|
-
"NOT_FOUND",
|
|
10
|
-
"CONFLICT",
|
|
11
|
-
"UNPROCESSABLE_ENTITY",
|
|
12
|
-
"TOO_MANY_REQUESTS",
|
|
13
|
-
] as const;
|
|
14
|
-
|
|
15
|
-
export function errs(...codes: (typeof INTERNAL_ERROR_CODES)[number][]) {
|
|
16
|
-
const descriptions: Record<string, string> = {
|
|
17
|
-
INTERNAL_ERROR: "Internal server error",
|
|
18
|
-
BAD_REQUEST: "Invalid request parameters",
|
|
19
|
-
UNAUTHORIZED: "Authentication required",
|
|
20
|
-
FORBIDDEN: "Insufficient permissions",
|
|
21
|
-
NOT_FOUND: "Resource not found",
|
|
22
|
-
CONFLICT: "Resource already exists",
|
|
23
|
-
UNPROCESSABLE_ENTITY: "Validation failed",
|
|
24
|
-
TOO_MANY_REQUESTS: "Rate limit exceeded",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
return applyDecorators(
|
|
28
|
-
...codes.map((code) =>
|
|
29
|
-
ApiResponse({
|
|
30
|
-
status: codeToStatus(code),
|
|
31
|
-
description: descriptions[code] ?? code,
|
|
32
|
-
schema: {
|
|
33
|
-
type: "object",
|
|
34
|
-
properties: {
|
|
35
|
-
success: { type: "boolean", example: false },
|
|
36
|
-
error: {
|
|
37
|
-
type: "object",
|
|
38
|
-
properties: {
|
|
39
|
-
code: { type: "string", example: code },
|
|
40
|
-
message: { type: "string", example: descriptions[code] },
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
})
|
|
46
|
-
)
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function codeToStatus(code: string): number {
|
|
51
|
-
const map: Record<string, number> = {
|
|
52
|
-
BAD_REQUEST: 400,
|
|
53
|
-
UNAUTHORIZED: 401,
|
|
54
|
-
FORBIDDEN: 403,
|
|
55
|
-
NOT_FOUND: 404,
|
|
56
|
-
CONFLICT: 409,
|
|
57
|
-
UNPROCESSABLE_ENTITY: 422,
|
|
58
|
-
TOO_MANY_REQUESTS: 429,
|
|
59
|
-
INTERNAL_ERROR: 500,
|
|
60
|
-
};
|
|
61
|
-
return map[code] ?? 500;
|
|
62
|
-
}
|
|
1
|
+
import { applyDecorators } from "@nestjs/common";
|
|
2
|
+
import { ApiResponse } from "@nestjs/swagger";
|
|
3
|
+
|
|
4
|
+
export const INTERNAL_ERROR_CODES = [
|
|
5
|
+
"INTERNAL_ERROR",
|
|
6
|
+
"BAD_REQUEST",
|
|
7
|
+
"UNAUTHORIZED",
|
|
8
|
+
"FORBIDDEN",
|
|
9
|
+
"NOT_FOUND",
|
|
10
|
+
"CONFLICT",
|
|
11
|
+
"UNPROCESSABLE_ENTITY",
|
|
12
|
+
"TOO_MANY_REQUESTS",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export function errs(...codes: (typeof INTERNAL_ERROR_CODES)[number][]) {
|
|
16
|
+
const descriptions: Record<string, string> = {
|
|
17
|
+
INTERNAL_ERROR: "Internal server error",
|
|
18
|
+
BAD_REQUEST: "Invalid request parameters",
|
|
19
|
+
UNAUTHORIZED: "Authentication required",
|
|
20
|
+
FORBIDDEN: "Insufficient permissions",
|
|
21
|
+
NOT_FOUND: "Resource not found",
|
|
22
|
+
CONFLICT: "Resource already exists",
|
|
23
|
+
UNPROCESSABLE_ENTITY: "Validation failed",
|
|
24
|
+
TOO_MANY_REQUESTS: "Rate limit exceeded",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return applyDecorators(
|
|
28
|
+
...codes.map((code) =>
|
|
29
|
+
ApiResponse({
|
|
30
|
+
status: codeToStatus(code),
|
|
31
|
+
description: descriptions[code] ?? code,
|
|
32
|
+
schema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
success: { type: "boolean", example: false },
|
|
36
|
+
error: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
code: { type: "string", example: code },
|
|
40
|
+
message: { type: "string", example: descriptions[code] },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function codeToStatus(code: string): number {
|
|
51
|
+
const map: Record<string, number> = {
|
|
52
|
+
BAD_REQUEST: 400,
|
|
53
|
+
UNAUTHORIZED: 401,
|
|
54
|
+
FORBIDDEN: 403,
|
|
55
|
+
NOT_FOUND: 404,
|
|
56
|
+
CONFLICT: 409,
|
|
57
|
+
UNPROCESSABLE_ENTITY: 422,
|
|
58
|
+
TOO_MANY_REQUESTS: 429,
|
|
59
|
+
INTERNAL_ERROR: 500,
|
|
60
|
+
};
|
|
61
|
+
return map[code] ?? 500;
|
|
62
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { NestFactory } from "@nestjs/core";
|
|
2
|
-
import { ValidationPipe } from "@nestjs/common";
|
|
3
|
-
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
|
|
4
|
-
import { AppModule } from "./app.module";
|
|
5
|
-
|
|
6
|
-
async function bootstrap() {
|
|
7
|
-
const app = await NestFactory.create(AppModule);
|
|
8
|
-
|
|
9
|
-
app.setGlobalPrefix("api");
|
|
10
|
-
app.enableCors();
|
|
11
|
-
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
|
|
12
|
-
|
|
13
|
-
const config = new DocumentBuilder()
|
|
14
|
-
.setTitle("{{projectName}} API")
|
|
15
|
-
.setDescription("{{description}}")
|
|
16
|
-
.setVersion("0.1.0")
|
|
17
|
-
.addBearerAuth()
|
|
18
|
-
.build();
|
|
19
|
-
|
|
20
|
-
const document = SwaggerModule.createDocument(app, config);
|
|
21
|
-
SwaggerModule.setup("docs", app, document);
|
|
22
|
-
|
|
23
|
-
await app.listen({{apiPort}});
|
|
24
|
-
console.log(`Application running on: http://localhost:{{apiPort}}`);
|
|
25
|
-
console.log(`Swagger docs: http://localhost:{{apiPort}}/docs`);
|
|
26
|
-
}
|
|
27
|
-
bootstrap();
|
|
1
|
+
import { NestFactory } from "@nestjs/core";
|
|
2
|
+
import { ValidationPipe } from "@nestjs/common";
|
|
3
|
+
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
|
|
4
|
+
import { AppModule } from "./app.module";
|
|
5
|
+
|
|
6
|
+
async function bootstrap() {
|
|
7
|
+
const app = await NestFactory.create(AppModule);
|
|
8
|
+
|
|
9
|
+
app.setGlobalPrefix("api");
|
|
10
|
+
app.enableCors();
|
|
11
|
+
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
|
|
12
|
+
|
|
13
|
+
const config = new DocumentBuilder()
|
|
14
|
+
.setTitle("{{projectName}} API")
|
|
15
|
+
.setDescription("{{description}}")
|
|
16
|
+
.setVersion("0.1.0")
|
|
17
|
+
.addBearerAuth()
|
|
18
|
+
.build();
|
|
19
|
+
|
|
20
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
21
|
+
SwaggerModule.setup("docs", app, document);
|
|
22
|
+
|
|
23
|
+
await app.listen({{apiPort}});
|
|
24
|
+
console.log(`Application running on: http://localhost:{{apiPort}}`);
|
|
25
|
+
console.log(`Swagger docs: http://localhost:{{apiPort}}/docs`);
|
|
26
|
+
}
|
|
27
|
+
bootstrap();
|