@aepstore-dev/contracts 1.1.0 → 1.3.0
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/gen/auth.ts +70 -79
- package/gen/upload.ts +147 -28
- package/package.json +1 -1
- package/proto/auth.proto +69 -63
- package/proto/upload.proto +112 -26
package/gen/auth.ts
CHANGED
|
@@ -34,32 +34,16 @@ export interface RegisterRequest {
|
|
|
34
34
|
ip: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
export interface AuthResponse {
|
|
39
|
-
accessToken: string;
|
|
40
|
-
refreshToken: string;
|
|
41
|
-
user: AuthUser | undefined;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface AuthUser {
|
|
45
|
-
id: string;
|
|
46
|
-
email: string;
|
|
47
|
-
username: string;
|
|
48
|
-
/** primary role for backwards compat ("User" / "Administrator" / "Moderator") */
|
|
49
|
-
role: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Code types: '2fa', 'reset', 'confirm', 'totp' (legacy strings preserved as-is). */
|
|
37
|
+
/** type ∈ { twofa, reset, totp_generate, totp_activate, confirm } (legacy strings) */
|
|
53
38
|
export interface CreateCodeRequest {
|
|
54
39
|
email: string;
|
|
55
40
|
type: string;
|
|
56
41
|
}
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/** `new_password` is only used by the 'reset' flow. */
|
|
43
|
+
/**
|
|
44
|
+
* type ∈ { twofa, twofa_deactivate, reset, totp, totp_deactivate, backup, confirm }
|
|
45
|
+
* new_password is only used by the 'reset' flow.
|
|
46
|
+
*/
|
|
63
47
|
export interface VerifyCodeRequest {
|
|
64
48
|
email: string;
|
|
65
49
|
code: string;
|
|
@@ -68,29 +52,10 @@ export interface VerifyCodeRequest {
|
|
|
68
52
|
newPassword: string;
|
|
69
53
|
}
|
|
70
54
|
|
|
71
|
-
/**
|
|
72
|
-
* Some verification flows (e.g. confirm-on-register) issue tokens immediately;
|
|
73
|
-
* others just return ok=true.
|
|
74
|
-
*/
|
|
75
|
-
export interface VerifyCodeResponse {
|
|
76
|
-
ok: boolean;
|
|
77
|
-
/** optional */
|
|
78
|
-
accessToken: string;
|
|
79
|
-
/** optional */
|
|
80
|
-
refreshToken: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
55
|
export interface CheckTokenRequest {
|
|
84
56
|
token: string;
|
|
85
57
|
}
|
|
86
58
|
|
|
87
|
-
export interface CheckTokenResponse {
|
|
88
|
-
valid: boolean;
|
|
89
|
-
userId: string;
|
|
90
|
-
/** populated when valid=false */
|
|
91
|
-
reason: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
59
|
export interface RefreshTokenRequest {
|
|
95
60
|
refreshToken: string;
|
|
96
61
|
/** optional */
|
|
@@ -103,24 +68,54 @@ export interface Get2faStatusRequest {
|
|
|
103
68
|
username: string;
|
|
104
69
|
}
|
|
105
70
|
|
|
106
|
-
export interface Get2faStatusResponse {
|
|
107
|
-
enabled: boolean;
|
|
108
|
-
type: TwoFactorType;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
71
|
export interface GetProfileRequest {
|
|
112
72
|
userId: string;
|
|
113
73
|
}
|
|
114
74
|
|
|
115
|
-
export interface
|
|
75
|
+
export interface AuthUser {
|
|
116
76
|
id: string;
|
|
117
|
-
email: string;
|
|
118
77
|
username: string;
|
|
119
|
-
|
|
78
|
+
email: string;
|
|
79
|
+
avatarUrl: string;
|
|
80
|
+
bannerUrl: string;
|
|
81
|
+
roles: string[];
|
|
82
|
+
/** primary role (roles[0]) */
|
|
83
|
+
role: string;
|
|
120
84
|
isVerified: boolean;
|
|
85
|
+
twoFactorType: TwoFactorType;
|
|
121
86
|
twoFactorEnabled: boolean;
|
|
87
|
+
/** Decimal as string (D7) */
|
|
88
|
+
balance: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Unified result for login/register/createCode/verifyCode/refresh. Which fields
|
|
93
|
+
* are populated depends on the flow:
|
|
94
|
+
* - fully authenticated → access_token + refresh_token + expires_in + user
|
|
95
|
+
* - 2FA required / pending confirm → message + requires_2fa + two_factor_type + user (no tokens)
|
|
96
|
+
* - totp_generate → secret + otpauth + qr (+ user)
|
|
97
|
+
* - totp_activate → backup_codes (+ tokens/user)
|
|
98
|
+
*/
|
|
99
|
+
export interface AuthResult {
|
|
100
|
+
accessToken: string;
|
|
101
|
+
refreshToken: string;
|
|
102
|
+
expiresIn: number;
|
|
103
|
+
user: AuthUser | undefined;
|
|
104
|
+
message: string;
|
|
105
|
+
requires2fa: boolean;
|
|
122
106
|
twoFactorType: TwoFactorType;
|
|
123
|
-
|
|
107
|
+
/** TOTP setup extras */
|
|
108
|
+
secret: string;
|
|
109
|
+
otpauth: string;
|
|
110
|
+
/** data URL (PNG) */
|
|
111
|
+
qr: string;
|
|
112
|
+
backupCodes: string[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface TwoFactorStatusResponse {
|
|
116
|
+
twoFactorType: TwoFactorType;
|
|
117
|
+
has2fa: boolean;
|
|
118
|
+
isVerified: boolean;
|
|
124
119
|
}
|
|
125
120
|
|
|
126
121
|
export interface Empty {
|
|
@@ -130,64 +125,60 @@ export const AUTH_V1_PACKAGE_NAME = "auth.v1";
|
|
|
130
125
|
|
|
131
126
|
/**
|
|
132
127
|
* Ported from legacy auth-service (@MessagePattern → @GrpcMethod).
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
128
|
+
* Tokens are HMAC primitives from @aepstore-dev/passport:
|
|
129
|
+
* - access token: short TTL, carries userId; the gateway validates it
|
|
130
|
+
* locally via PassportAuthGuard (no round-trip here).
|
|
131
|
+
* - refresh token: long TTL HMAC + a RefreshToken DB row (device list +
|
|
132
|
+
* revocation). Refresh verifies the HMAC AND that the row still exists.
|
|
136
133
|
*/
|
|
137
134
|
|
|
138
135
|
export interface AuthServiceClient {
|
|
139
|
-
login(request: LoginRequest): Observable<
|
|
136
|
+
login(request: LoginRequest): Observable<AuthResult>;
|
|
140
137
|
|
|
141
|
-
register(request: RegisterRequest): Observable<
|
|
138
|
+
register(request: RegisterRequest): Observable<AuthResult>;
|
|
142
139
|
|
|
143
|
-
createCode(request: CreateCodeRequest): Observable<
|
|
140
|
+
createCode(request: CreateCodeRequest): Observable<AuthResult>;
|
|
144
141
|
|
|
145
|
-
verifyCode(request: VerifyCodeRequest): Observable<
|
|
142
|
+
verifyCode(request: VerifyCodeRequest): Observable<AuthResult>;
|
|
146
143
|
|
|
147
|
-
checkToken(request: CheckTokenRequest): Observable<
|
|
144
|
+
checkToken(request: CheckTokenRequest): Observable<AuthUser>;
|
|
148
145
|
|
|
149
|
-
refreshToken(request: RefreshTokenRequest): Observable<
|
|
146
|
+
refreshToken(request: RefreshTokenRequest): Observable<AuthResult>;
|
|
150
147
|
|
|
151
|
-
get2FaStatus(request: Get2faStatusRequest): Observable<
|
|
148
|
+
get2FaStatus(request: Get2faStatusRequest): Observable<TwoFactorStatusResponse>;
|
|
152
149
|
|
|
153
|
-
getProfile(request: GetProfileRequest): Observable<
|
|
150
|
+
getProfile(request: GetProfileRequest): Observable<AuthUser>;
|
|
154
151
|
|
|
155
152
|
cleanupExpiredTotpSetups(request: Empty): Observable<Empty>;
|
|
156
153
|
}
|
|
157
154
|
|
|
158
155
|
/**
|
|
159
156
|
* Ported from legacy auth-service (@MessagePattern → @GrpcMethod).
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
157
|
+
* Tokens are HMAC primitives from @aepstore-dev/passport:
|
|
158
|
+
* - access token: short TTL, carries userId; the gateway validates it
|
|
159
|
+
* locally via PassportAuthGuard (no round-trip here).
|
|
160
|
+
* - refresh token: long TTL HMAC + a RefreshToken DB row (device list +
|
|
161
|
+
* revocation). Refresh verifies the HMAC AND that the row still exists.
|
|
163
162
|
*/
|
|
164
163
|
|
|
165
164
|
export interface AuthServiceController {
|
|
166
|
-
login(request: LoginRequest): Promise<
|
|
165
|
+
login(request: LoginRequest): Promise<AuthResult> | Observable<AuthResult> | AuthResult;
|
|
167
166
|
|
|
168
|
-
register(request: RegisterRequest): Promise<
|
|
167
|
+
register(request: RegisterRequest): Promise<AuthResult> | Observable<AuthResult> | AuthResult;
|
|
169
168
|
|
|
170
|
-
createCode(
|
|
171
|
-
request: CreateCodeRequest,
|
|
172
|
-
): Promise<CreateCodeResponse> | Observable<CreateCodeResponse> | CreateCodeResponse;
|
|
169
|
+
createCode(request: CreateCodeRequest): Promise<AuthResult> | Observable<AuthResult> | AuthResult;
|
|
173
170
|
|
|
174
|
-
verifyCode(
|
|
175
|
-
request: VerifyCodeRequest,
|
|
176
|
-
): Promise<VerifyCodeResponse> | Observable<VerifyCodeResponse> | VerifyCodeResponse;
|
|
171
|
+
verifyCode(request: VerifyCodeRequest): Promise<AuthResult> | Observable<AuthResult> | AuthResult;
|
|
177
172
|
|
|
178
|
-
checkToken(
|
|
179
|
-
request: CheckTokenRequest,
|
|
180
|
-
): Promise<CheckTokenResponse> | Observable<CheckTokenResponse> | CheckTokenResponse;
|
|
173
|
+
checkToken(request: CheckTokenRequest): Promise<AuthUser> | Observable<AuthUser> | AuthUser;
|
|
181
174
|
|
|
182
|
-
refreshToken(request: RefreshTokenRequest): Promise<
|
|
175
|
+
refreshToken(request: RefreshTokenRequest): Promise<AuthResult> | Observable<AuthResult> | AuthResult;
|
|
183
176
|
|
|
184
177
|
get2FaStatus(
|
|
185
178
|
request: Get2faStatusRequest,
|
|
186
|
-
): Promise<
|
|
179
|
+
): Promise<TwoFactorStatusResponse> | Observable<TwoFactorStatusResponse> | TwoFactorStatusResponse;
|
|
187
180
|
|
|
188
|
-
getProfile(
|
|
189
|
-
request: GetProfileRequest,
|
|
190
|
-
): Promise<UserProfileResponse> | Observable<UserProfileResponse> | UserProfileResponse;
|
|
181
|
+
getProfile(request: GetProfileRequest): Promise<AuthUser> | Observable<AuthUser> | AuthUser;
|
|
191
182
|
|
|
192
183
|
cleanupExpiredTotpSetups(request: Empty): Promise<Empty> | Observable<Empty> | Empty;
|
|
193
184
|
}
|
package/gen/upload.ts
CHANGED
|
@@ -10,84 +10,203 @@ import { Observable } from "rxjs";
|
|
|
10
10
|
|
|
11
11
|
export const protobufPackage = "upload.v1";
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Determines the object key prefix (and future ACL/privacy policy).
|
|
15
|
+
* ATTACHMENT = paid product deliverable (private; presigned GET only after purchase).
|
|
16
|
+
* MEDIA / AVATAR / BANNER = preview assets.
|
|
17
|
+
*/
|
|
18
|
+
export enum UploadKind {
|
|
19
|
+
UPLOAD_KIND_UNSPECIFIED = 0,
|
|
20
|
+
UPLOAD_KIND_ATTACHMENT = 1,
|
|
21
|
+
UPLOAD_KIND_MEDIA = 2,
|
|
22
|
+
UPLOAD_KIND_AVATAR = 3,
|
|
23
|
+
UPLOAD_KIND_BANNER = 4,
|
|
24
|
+
UNRECOGNIZED = -1,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Ok {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CreateMultipartUploadRequest {
|
|
32
|
+
fileName: string;
|
|
16
33
|
mimeType: string;
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
fileSize: number;
|
|
35
|
+
kind: UploadKind;
|
|
36
|
+
/** user initiating, for key namespacing + audit */
|
|
37
|
+
ownerId: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CreateMultipartUploadResponse {
|
|
41
|
+
/** S3 multipart UploadId */
|
|
19
42
|
uploadId: string;
|
|
43
|
+
/** object key this service generated */
|
|
44
|
+
fileKey: string;
|
|
45
|
+
/** recommended part size (bytes) the client should slice with */
|
|
46
|
+
partSize: number;
|
|
47
|
+
/** number of parts for file_size at part_size */
|
|
48
|
+
partCount: number;
|
|
20
49
|
}
|
|
21
50
|
|
|
22
|
-
export interface
|
|
23
|
-
|
|
24
|
-
success: boolean;
|
|
25
|
-
message: string;
|
|
51
|
+
export interface SignPartRequest {
|
|
52
|
+
fileKey: string;
|
|
26
53
|
uploadId: string;
|
|
54
|
+
/** 1-based */
|
|
55
|
+
partNumber: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface SignPartResponse {
|
|
59
|
+
/** presigned PUT URL for this single part */
|
|
60
|
+
url: string;
|
|
61
|
+
expiresIn: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface CompletedPart {
|
|
65
|
+
partNumber: number;
|
|
66
|
+
/** ETag the client received in the S3 PUT response header for this part */
|
|
67
|
+
etag: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface CompleteMultipartUploadRequest {
|
|
71
|
+
fileKey: string;
|
|
72
|
+
uploadId: string;
|
|
73
|
+
parts: CompletedPart[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface CompleteMultipartUploadResponse {
|
|
77
|
+
fileKey: string;
|
|
78
|
+
/** final object location (internal reference) */
|
|
79
|
+
location: string;
|
|
27
80
|
fileSize: number;
|
|
28
81
|
}
|
|
29
82
|
|
|
30
|
-
export interface
|
|
83
|
+
export interface AbortMultipartUploadRequest {
|
|
84
|
+
fileKey: string;
|
|
31
85
|
uploadId: string;
|
|
32
86
|
}
|
|
33
87
|
|
|
34
|
-
export interface
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
88
|
+
export interface CreateUploadUrlRequest {
|
|
89
|
+
fileName: string;
|
|
90
|
+
mimeType: string;
|
|
91
|
+
kind: UploadKind;
|
|
92
|
+
ownerId: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface CreateUploadUrlResponse {
|
|
96
|
+
/** presigned PUT URL (client PUTs the whole file) */
|
|
97
|
+
url: string;
|
|
98
|
+
fileKey: string;
|
|
99
|
+
expiresIn: number;
|
|
38
100
|
}
|
|
39
101
|
|
|
40
102
|
export interface SignedUrlRequest {
|
|
41
103
|
fileKey: string;
|
|
104
|
+
/** optional, default 3600 */
|
|
42
105
|
expiresIn: number;
|
|
106
|
+
/** optional: forces Content-Disposition attachment filename */
|
|
107
|
+
downloadFileName: string;
|
|
43
108
|
}
|
|
44
109
|
|
|
45
110
|
export interface SignedUrlResponse {
|
|
46
111
|
signedUrl: string;
|
|
47
|
-
|
|
48
|
-
|
|
112
|
+
expiresIn: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface DeleteObjectRequest {
|
|
116
|
+
fileKey: string;
|
|
49
117
|
}
|
|
50
118
|
|
|
51
119
|
export const UPLOAD_V1_PACKAGE_NAME = "upload.v1";
|
|
52
120
|
|
|
53
121
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
122
|
+
* Thin signing service. File bytes NEVER pass through this service — clients
|
|
123
|
+
* upload/download directly to/from S3 (Yandex Object Storage) using presigned
|
|
124
|
+
* URLs this service mints. Memory/bandwidth flat regardless of file size.
|
|
125
|
+
*
|
|
126
|
+
* Large files (product deliverables, up to ~1GB) use the multipart flow:
|
|
127
|
+
* CreateMultipartUpload → (SignPart × N, client PUTs each part to S3) →
|
|
128
|
+
* CompleteMultipartUpload (or AbortMultipartUpload on failure)
|
|
129
|
+
*
|
|
130
|
+
* Small files (preview images, avatars) use the single-PUT flow: CreateUploadUrl.
|
|
131
|
+
*
|
|
132
|
+
* Downloads are gated elsewhere (products-service verifies purchase, then calls
|
|
133
|
+
* GenerateSignedUrl). This service does not know about purchases.
|
|
56
134
|
*/
|
|
57
135
|
|
|
58
136
|
export interface UploadServiceClient {
|
|
59
|
-
|
|
137
|
+
createMultipartUpload(request: CreateMultipartUploadRequest): Observable<CreateMultipartUploadResponse>;
|
|
138
|
+
|
|
139
|
+
signPart(request: SignPartRequest): Observable<SignPartResponse>;
|
|
140
|
+
|
|
141
|
+
completeMultipartUpload(request: CompleteMultipartUploadRequest): Observable<CompleteMultipartUploadResponse>;
|
|
142
|
+
|
|
143
|
+
abortMultipartUpload(request: AbortMultipartUploadRequest): Observable<Ok>;
|
|
60
144
|
|
|
61
|
-
|
|
145
|
+
createUploadUrl(request: CreateUploadUrlRequest): Observable<CreateUploadUrlResponse>;
|
|
62
146
|
|
|
63
147
|
generateSignedUrl(request: SignedUrlRequest): Observable<SignedUrlResponse>;
|
|
148
|
+
|
|
149
|
+
deleteObject(request: DeleteObjectRequest): Observable<Ok>;
|
|
64
150
|
}
|
|
65
151
|
|
|
66
152
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
153
|
+
* Thin signing service. File bytes NEVER pass through this service — clients
|
|
154
|
+
* upload/download directly to/from S3 (Yandex Object Storage) using presigned
|
|
155
|
+
* URLs this service mints. Memory/bandwidth flat regardless of file size.
|
|
156
|
+
*
|
|
157
|
+
* Large files (product deliverables, up to ~1GB) use the multipart flow:
|
|
158
|
+
* CreateMultipartUpload → (SignPart × N, client PUTs each part to S3) →
|
|
159
|
+
* CompleteMultipartUpload (or AbortMultipartUpload on failure)
|
|
160
|
+
*
|
|
161
|
+
* Small files (preview images, avatars) use the single-PUT flow: CreateUploadUrl.
|
|
162
|
+
*
|
|
163
|
+
* Downloads are gated elsewhere (products-service verifies purchase, then calls
|
|
164
|
+
* GenerateSignedUrl). This service does not know about purchases.
|
|
69
165
|
*/
|
|
70
166
|
|
|
71
167
|
export interface UploadServiceController {
|
|
72
|
-
|
|
168
|
+
createMultipartUpload(
|
|
169
|
+
request: CreateMultipartUploadRequest,
|
|
170
|
+
): Promise<CreateMultipartUploadResponse> | Observable<CreateMultipartUploadResponse> | CreateMultipartUploadResponse;
|
|
171
|
+
|
|
172
|
+
signPart(request: SignPartRequest): Promise<SignPartResponse> | Observable<SignPartResponse> | SignPartResponse;
|
|
173
|
+
|
|
174
|
+
completeMultipartUpload(
|
|
175
|
+
request: CompleteMultipartUploadRequest,
|
|
176
|
+
):
|
|
177
|
+
| Promise<CompleteMultipartUploadResponse>
|
|
178
|
+
| Observable<CompleteMultipartUploadResponse>
|
|
179
|
+
| CompleteMultipartUploadResponse;
|
|
73
180
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
181
|
+
abortMultipartUpload(request: AbortMultipartUploadRequest): Promise<Ok> | Observable<Ok> | Ok;
|
|
182
|
+
|
|
183
|
+
createUploadUrl(
|
|
184
|
+
request: CreateUploadUrlRequest,
|
|
185
|
+
): Promise<CreateUploadUrlResponse> | Observable<CreateUploadUrlResponse> | CreateUploadUrlResponse;
|
|
77
186
|
|
|
78
187
|
generateSignedUrl(
|
|
79
188
|
request: SignedUrlRequest,
|
|
80
189
|
): Promise<SignedUrlResponse> | Observable<SignedUrlResponse> | SignedUrlResponse;
|
|
190
|
+
|
|
191
|
+
deleteObject(request: DeleteObjectRequest): Promise<Ok> | Observable<Ok> | Ok;
|
|
81
192
|
}
|
|
82
193
|
|
|
83
194
|
export function UploadServiceControllerMethods() {
|
|
84
195
|
return function (constructor: Function) {
|
|
85
|
-
const grpcMethods: string[] = [
|
|
196
|
+
const grpcMethods: string[] = [
|
|
197
|
+
"createMultipartUpload",
|
|
198
|
+
"signPart",
|
|
199
|
+
"completeMultipartUpload",
|
|
200
|
+
"abortMultipartUpload",
|
|
201
|
+
"createUploadUrl",
|
|
202
|
+
"generateSignedUrl",
|
|
203
|
+
"deleteObject",
|
|
204
|
+
];
|
|
86
205
|
for (const method of grpcMethods) {
|
|
87
206
|
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
|
|
88
207
|
GrpcMethod("UploadService", method)(constructor.prototype[method], method, descriptor);
|
|
89
208
|
}
|
|
90
|
-
const grpcStreamMethods: string[] = [
|
|
209
|
+
const grpcStreamMethods: string[] = [];
|
|
91
210
|
for (const method of grpcStreamMethods) {
|
|
92
211
|
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
|
|
93
212
|
GrpcStreamMethod("UploadService", method)(constructor.prototype[method], method, descriptor);
|
package/package.json
CHANGED
package/proto/auth.proto
CHANGED
|
@@ -3,21 +3,33 @@ syntax = "proto3";
|
|
|
3
3
|
package auth.v1;
|
|
4
4
|
|
|
5
5
|
// Ported from legacy auth-service (@MessagePattern → @GrpcMethod).
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// Tokens are HMAC primitives from @aepstore-dev/passport:
|
|
7
|
+
// - access token: short TTL, carries userId; the gateway validates it
|
|
8
|
+
// locally via PassportAuthGuard (no round-trip here).
|
|
9
|
+
// - refresh token: long TTL HMAC + a RefreshToken DB row (device list +
|
|
10
|
+
// revocation). Refresh verifies the HMAC AND that the row still exists.
|
|
9
11
|
service AuthService {
|
|
10
|
-
rpc Login(LoginRequest) returns (
|
|
11
|
-
rpc Register(RegisterRequest) returns (
|
|
12
|
-
rpc CreateCode(CreateCodeRequest) returns (
|
|
13
|
-
rpc VerifyCode(VerifyCodeRequest) returns (
|
|
14
|
-
rpc CheckToken(CheckTokenRequest) returns (
|
|
15
|
-
rpc RefreshToken(RefreshTokenRequest) returns (
|
|
16
|
-
rpc Get2faStatus(Get2faStatusRequest) returns (
|
|
17
|
-
rpc GetProfile(GetProfileRequest) returns (
|
|
12
|
+
rpc Login(LoginRequest) returns (AuthResult);
|
|
13
|
+
rpc Register(RegisterRequest) returns (AuthResult);
|
|
14
|
+
rpc CreateCode(CreateCodeRequest) returns (AuthResult);
|
|
15
|
+
rpc VerifyCode(VerifyCodeRequest) returns (AuthResult);
|
|
16
|
+
rpc CheckToken(CheckTokenRequest) returns (AuthUser);
|
|
17
|
+
rpc RefreshToken(RefreshTokenRequest) returns (AuthResult);
|
|
18
|
+
rpc Get2faStatus(Get2faStatusRequest) returns (TwoFactorStatusResponse);
|
|
19
|
+
rpc GetProfile(GetProfileRequest) returns (AuthUser);
|
|
18
20
|
rpc CleanupExpiredTotpSetups(Empty) returns (Empty);
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
enum TwoFactorType {
|
|
24
|
+
NONE = 0;
|
|
25
|
+
APP = 1;
|
|
26
|
+
EMAIL = 2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Requests
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
21
33
|
// Login by username OR email (one of the two must be set).
|
|
22
34
|
message LoginRequest {
|
|
23
35
|
string password = 1;
|
|
@@ -32,31 +44,14 @@ message RegisterRequest {
|
|
|
32
44
|
string ip = 4; // optional, attached by gateway from req
|
|
33
45
|
}
|
|
34
46
|
|
|
35
|
-
//
|
|
36
|
-
message AuthResponse {
|
|
37
|
-
string access_token = 1;
|
|
38
|
-
string refresh_token = 2;
|
|
39
|
-
AuthUser user = 3;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
message AuthUser {
|
|
43
|
-
string id = 1;
|
|
44
|
-
string email = 2;
|
|
45
|
-
string username = 3;
|
|
46
|
-
string role = 4; // primary role for backwards compat ("User" / "Administrator" / "Moderator")
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Code types: '2fa', 'reset', 'confirm', 'totp' (legacy strings preserved as-is).
|
|
47
|
+
// type ∈ { twofa, reset, totp_generate, totp_activate, confirm } (legacy strings)
|
|
50
48
|
message CreateCodeRequest {
|
|
51
49
|
string email = 1;
|
|
52
50
|
string type = 2;
|
|
53
51
|
}
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// `new_password` is only used by the 'reset' flow.
|
|
53
|
+
// type ∈ { twofa, twofa_deactivate, reset, totp, totp_deactivate, backup, confirm }
|
|
54
|
+
// new_password is only used by the 'reset' flow.
|
|
60
55
|
message VerifyCodeRequest {
|
|
61
56
|
string email = 1;
|
|
62
57
|
string code = 2;
|
|
@@ -64,24 +59,10 @@ message VerifyCodeRequest {
|
|
|
64
59
|
string new_password = 4; // optional
|
|
65
60
|
}
|
|
66
61
|
|
|
67
|
-
// Some verification flows (e.g. confirm-on-register) issue tokens immediately;
|
|
68
|
-
// others just return ok=true.
|
|
69
|
-
message VerifyCodeResponse {
|
|
70
|
-
bool ok = 1;
|
|
71
|
-
string access_token = 2; // optional
|
|
72
|
-
string refresh_token = 3; // optional
|
|
73
|
-
}
|
|
74
|
-
|
|
75
62
|
message CheckTokenRequest {
|
|
76
63
|
string token = 1;
|
|
77
64
|
}
|
|
78
65
|
|
|
79
|
-
message CheckTokenResponse {
|
|
80
|
-
bool valid = 1;
|
|
81
|
-
string user_id = 2;
|
|
82
|
-
string reason = 3; // populated when valid=false
|
|
83
|
-
}
|
|
84
|
-
|
|
85
66
|
message RefreshTokenRequest {
|
|
86
67
|
string refresh_token = 1;
|
|
87
68
|
string user_agent = 2; // optional
|
|
@@ -92,30 +73,55 @@ message Get2faStatusRequest {
|
|
|
92
73
|
string username = 1;
|
|
93
74
|
}
|
|
94
75
|
|
|
95
|
-
message Get2faStatusResponse {
|
|
96
|
-
bool enabled = 1;
|
|
97
|
-
TwoFactorType type = 2;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
enum TwoFactorType {
|
|
101
|
-
NONE = 0;
|
|
102
|
-
APP = 1;
|
|
103
|
-
EMAIL = 2;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
76
|
message GetProfileRequest {
|
|
107
77
|
string user_id = 1;
|
|
108
78
|
}
|
|
109
79
|
|
|
110
|
-
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Responses
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
message AuthUser {
|
|
111
85
|
string id = 1;
|
|
112
|
-
string
|
|
113
|
-
string
|
|
114
|
-
string
|
|
115
|
-
|
|
116
|
-
|
|
86
|
+
string username = 2;
|
|
87
|
+
string email = 3;
|
|
88
|
+
string avatar_url = 4;
|
|
89
|
+
string banner_url = 5;
|
|
90
|
+
repeated string roles = 6;
|
|
91
|
+
string role = 7; // primary role (roles[0])
|
|
92
|
+
bool is_verified = 8;
|
|
93
|
+
TwoFactorType two_factor_type = 9;
|
|
94
|
+
bool two_factor_enabled = 10;
|
|
95
|
+
string balance = 11; // Decimal as string (D7)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Unified result for login/register/createCode/verifyCode/refresh. Which fields
|
|
99
|
+
// are populated depends on the flow:
|
|
100
|
+
// - fully authenticated → access_token + refresh_token + expires_in + user
|
|
101
|
+
// - 2FA required / pending confirm → message + requires_2fa + two_factor_type + user (no tokens)
|
|
102
|
+
// - totp_generate → secret + otpauth + qr (+ user)
|
|
103
|
+
// - totp_activate → backup_codes (+ tokens/user)
|
|
104
|
+
message AuthResult {
|
|
105
|
+
string access_token = 1;
|
|
106
|
+
string refresh_token = 2;
|
|
107
|
+
int32 expires_in = 3;
|
|
108
|
+
AuthUser user = 4;
|
|
109
|
+
|
|
110
|
+
string message = 5;
|
|
111
|
+
bool requires_2fa = 6;
|
|
117
112
|
TwoFactorType two_factor_type = 7;
|
|
118
|
-
|
|
113
|
+
|
|
114
|
+
// TOTP setup extras
|
|
115
|
+
string secret = 8;
|
|
116
|
+
string otpauth = 9;
|
|
117
|
+
string qr = 10; // data URL (PNG)
|
|
118
|
+
repeated string backup_codes = 11;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
message TwoFactorStatusResponse {
|
|
122
|
+
TwoFactorType two_factor_type = 1;
|
|
123
|
+
bool has_2fa = 2;
|
|
124
|
+
bool is_verified = 3;
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
message Empty {}
|
package/proto/upload.proto
CHANGED
|
@@ -2,48 +2,134 @@ syntax = "proto3";
|
|
|
2
2
|
|
|
3
3
|
package upload.v1;
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
//
|
|
5
|
+
// Thin signing service. File bytes NEVER pass through this service — clients
|
|
6
|
+
// upload/download directly to/from S3 (Yandex Object Storage) using presigned
|
|
7
|
+
// URLs this service mints. Memory/bandwidth flat regardless of file size.
|
|
8
|
+
//
|
|
9
|
+
// Large files (product deliverables, up to ~1GB) use the multipart flow:
|
|
10
|
+
// CreateMultipartUpload → (SignPart × N, client PUTs each part to S3) →
|
|
11
|
+
// CompleteMultipartUpload (or AbortMultipartUpload on failure)
|
|
12
|
+
//
|
|
13
|
+
// Small files (preview images, avatars) use the single-PUT flow: CreateUploadUrl.
|
|
14
|
+
//
|
|
15
|
+
// Downloads are gated elsewhere (products-service verifies purchase, then calls
|
|
16
|
+
// GenerateSignedUrl). This service does not know about purchases.
|
|
7
17
|
service UploadService {
|
|
8
|
-
rpc
|
|
9
|
-
rpc
|
|
18
|
+
rpc CreateMultipartUpload(CreateMultipartUploadRequest) returns (CreateMultipartUploadResponse);
|
|
19
|
+
rpc SignPart(SignPartRequest) returns (SignPartResponse);
|
|
20
|
+
rpc CompleteMultipartUpload(CompleteMultipartUploadRequest) returns (CompleteMultipartUploadResponse);
|
|
21
|
+
rpc AbortMultipartUpload(AbortMultipartUploadRequest) returns (Ok);
|
|
22
|
+
|
|
23
|
+
rpc CreateUploadUrl(CreateUploadUrlRequest) returns (CreateUploadUrlResponse);
|
|
24
|
+
|
|
10
25
|
rpc GenerateSignedUrl(SignedUrlRequest) returns (SignedUrlResponse);
|
|
26
|
+
|
|
27
|
+
rpc DeleteObject(DeleteObjectRequest) returns (Ok);
|
|
11
28
|
}
|
|
12
29
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
// Determines the object key prefix (and future ACL/privacy policy).
|
|
31
|
+
// ATTACHMENT = paid product deliverable (private; presigned GET only after purchase).
|
|
32
|
+
// MEDIA / AVATAR / BANNER = preview assets.
|
|
33
|
+
enum UploadKind {
|
|
34
|
+
UPLOAD_KIND_UNSPECIFIED = 0;
|
|
35
|
+
UPLOAD_KIND_ATTACHMENT = 1;
|
|
36
|
+
UPLOAD_KIND_MEDIA = 2;
|
|
37
|
+
UPLOAD_KIND_AVATAR = 3;
|
|
38
|
+
UPLOAD_KIND_BANNER = 4;
|
|
20
39
|
}
|
|
21
40
|
|
|
22
|
-
message
|
|
23
|
-
|
|
24
|
-
bool success = 2;
|
|
25
|
-
string message = 3;
|
|
26
|
-
string upload_id = 4;
|
|
27
|
-
int64 file_size = 5;
|
|
41
|
+
message Ok {
|
|
42
|
+
bool ok = 1;
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
|
|
31
|
-
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Multipart upload (large files)
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
message CreateMultipartUploadRequest {
|
|
50
|
+
string file_name = 1;
|
|
51
|
+
string mime_type = 2;
|
|
52
|
+
int64 file_size = 3;
|
|
53
|
+
UploadKind kind = 4;
|
|
54
|
+
string owner_id = 5; // user initiating, for key namespacing + audit
|
|
32
55
|
}
|
|
33
56
|
|
|
34
|
-
message
|
|
35
|
-
string
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
message CreateMultipartUploadResponse {
|
|
58
|
+
string upload_id = 1; // S3 multipart UploadId
|
|
59
|
+
string file_key = 2; // object key this service generated
|
|
60
|
+
int64 part_size = 3; // recommended part size (bytes) the client should slice with
|
|
61
|
+
int32 part_count = 4; // number of parts for file_size at part_size
|
|
38
62
|
}
|
|
39
63
|
|
|
40
|
-
message
|
|
64
|
+
message SignPartRequest {
|
|
41
65
|
string file_key = 1;
|
|
66
|
+
string upload_id = 2;
|
|
67
|
+
int32 part_number = 3; // 1-based
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
message SignPartResponse {
|
|
71
|
+
string url = 1; // presigned PUT URL for this single part
|
|
42
72
|
int32 expires_in = 2;
|
|
43
73
|
}
|
|
44
74
|
|
|
75
|
+
message CompletedPart {
|
|
76
|
+
int32 part_number = 1;
|
|
77
|
+
string etag = 2; // ETag the client received in the S3 PUT response header for this part
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
message CompleteMultipartUploadRequest {
|
|
81
|
+
string file_key = 1;
|
|
82
|
+
string upload_id = 2;
|
|
83
|
+
repeated CompletedPart parts = 3;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message CompleteMultipartUploadResponse {
|
|
87
|
+
string file_key = 1;
|
|
88
|
+
string location = 2; // final object location (internal reference)
|
|
89
|
+
int64 file_size = 3;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
message AbortMultipartUploadRequest {
|
|
93
|
+
string file_key = 1;
|
|
94
|
+
string upload_id = 2;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Single-PUT upload (small files)
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
message CreateUploadUrlRequest {
|
|
102
|
+
string file_name = 1;
|
|
103
|
+
string mime_type = 2;
|
|
104
|
+
UploadKind kind = 3;
|
|
105
|
+
string owner_id = 4;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
message CreateUploadUrlResponse {
|
|
109
|
+
string url = 1; // presigned PUT URL (client PUTs the whole file)
|
|
110
|
+
string file_key = 2;
|
|
111
|
+
int32 expires_in = 3;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Download
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
message SignedUrlRequest {
|
|
119
|
+
string file_key = 1;
|
|
120
|
+
int32 expires_in = 2; // optional, default 3600
|
|
121
|
+
string download_file_name = 3; // optional: forces Content-Disposition attachment filename
|
|
122
|
+
}
|
|
123
|
+
|
|
45
124
|
message SignedUrlResponse {
|
|
46
125
|
string signed_url = 1;
|
|
47
|
-
|
|
48
|
-
|
|
126
|
+
int32 expires_in = 2;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Cleanup
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
message DeleteObjectRequest {
|
|
134
|
+
string file_key = 1;
|
|
49
135
|
}
|