@aepstore-dev/contracts 1.39.0 → 1.41.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/proto/users.proto CHANGED
@@ -1,660 +1,660 @@
1
- syntax = "proto3";
2
-
3
- package users.v1;
4
-
5
- service UsersService {
6
- rpc GetPublicProfile(GetPublicProfileRequest) returns (PublicProfileResponse);
7
- rpc UpdateProfile(UpdateProfileRequest) returns (PublicProfileResponse);
8
- rpc UpdateUser(UpdateUserRequest) returns (UserResponse);
9
- rpc UpdateUserRole(UpdateUserRoleRequest) returns (UserResponse);
10
- rpc GetUsers(GetUsersRequest) returns (GetUsersResponse);
11
- rpc GetActiveDevices(GetActiveDevicesRequest) returns (GetActiveDevicesResponse);
12
- rpc ManageDevices(ManageDevicesRequest) returns (ManageDevicesResponse);
13
- rpc GetUserFields(GetUserFieldsRequest) returns (GetUserFieldsResponse);
14
-
15
- rpc CreateSubscription(SubscriptionRequest) returns (SubscriptionResponse);
16
- rpc DeleteSubscription(SubscriptionRequest) returns (SubscriptionResponse);
17
-
18
- rpc BanUser(BanUserRequest) returns (UserResponse);
19
- rpc UnbanUser(UnbanUserRequest) returns (UserResponse);
20
-
21
- // Premium (paid). State lives in users-service; payments-service handles
22
- // money and calls ActivatePremium (internal) on completion.
23
- rpc GetMyPremium(GetMyPremiumRequest) returns (PremiumStateResponse);
24
- rpc SetPremiumAutoRenew(SetPremiumAutoRenewRequest) returns (PremiumStateResponse);
25
- // Internal — InternalServiceGuard. Called by payments-service after a
26
- // successful premium payment (balance or YooKassa webhook).
27
- rpc ActivatePremium(ActivatePremiumRequest) returns (PremiumStateResponse);
28
- // Internal — fast lookup for the commission engine in payments-service.
29
- rpc IsPremiumActive(IsPremiumActiveRequest) returns (IsPremiumActiveResponse);
30
-
31
- // Avatar borders (catalog + admin CRUD). Catalog read is public; mutations
32
- // are admin only (gateway enforces with RolesGuard).
33
- rpc ListAvatarBorders(ListAvatarBordersRequest) returns (ListAvatarBordersResponse);
34
- rpc UpsertAvatarBorder(UpsertAvatarBorderRequest) returns (AvatarBorder);
35
- rpc DeleteAvatarBorder(DeleteAvatarBorderRequest) returns (DeleteAvatarBorderResponse);
36
-
37
- // Account settings — bundled write surface for the UI.
38
- rpc UpdatePrivacy(UpdatePrivacyRequest) returns (PublicProfileResponse);
39
- rpc UpdateEmailPrefs(UpdateEmailPrefsRequest) returns (UpdateEmailPrefsResponse);
40
- rpc UpdateLocale(UpdateLocaleRequest) returns (UpdateLocaleResponse);
41
-
42
- // Soft-delete + restore. Internal AuthService also queries the soft-delete
43
- // state on login (rejects if deletedAt set).
44
- rpc SoftDeleteAccount(SoftDeleteAccountRequest) returns (SoftDeleteAccountResponse);
45
- rpc RestoreAccount(RestoreAccountRequest) returns (UserResponse);
46
- rpc HardDeletePending(HardDeletePendingRequest) returns (HardDeletePendingResponse); // tasks-service cron
47
-
48
- // Blocks
49
- rpc BlockUser(BlockUserRequest) returns (BlockUserResponse);
50
- rpc UnblockUser(BlockUserRequest) returns (BlockUserResponse);
51
- rpc ListBlockedUsers(ListBlockedUsersRequest) returns (ListBlockedUsersResponse);
52
- rpc IsBlocked(IsBlockedRequest) returns (IsBlockedResponse); // internal lookup
53
-
54
- // Data export
55
- rpc RequestDataExport(RequestDataExportRequest) returns (DataExportRequestRow);
56
- rpc ListDataExports(ListDataExportsRequest) returns (ListDataExportsResponse);
57
- rpc PickPendingDataExport(PickPendingDataExportRequest) returns (DataExportRequestRow); // tasks-service
58
- rpc CompleteDataExport(CompleteDataExportRequest) returns (DataExportRequestRow); // tasks-service
59
- rpc CollectUserExportData(CollectUserExportDataRequest) returns (CollectUserExportDataResponse); // tasks-service
60
-
61
- // Unified read for the settings page
62
- rpc GetAccountSettings(GetAccountSettingsRequest) returns (AccountSettingsResponse);
63
-
64
- // Password — separate from generic createCode/verifyCode reset flow.
65
- rpc ChangePassword(ChangePasswordRequest) returns (Ok);
66
- }
67
-
68
- message Ok { bool ok = 1; }
69
-
70
- // =================================================================
71
- // Common types
72
- // =================================================================
73
-
74
- message User {
75
- string id = 1;
76
- string email = 2;
77
- string username = 3;
78
- string avatar_url = 4;
79
- repeated string roles = 5;
80
- string role = 6; // primary role for backwards compat
81
- string created_at = 7; // ISO 8601
82
- string updated_at = 8;
83
- bool is_banned = 9;
84
- string banned_until = 10; // ISO 8601, empty = permanent/none
85
- string ban_reason = 11;
86
- }
87
-
88
- message BanUserRequest {
89
- string admin_id = 1;
90
- string user_id = 2;
91
- string reason = 3;
92
- string until = 4; // ISO 8601; empty = permanent ban
93
- }
94
-
95
- message UnbanUserRequest {
96
- string admin_id = 1;
97
- string user_id = 2;
98
- }
99
-
100
- message Country {
101
- string id = 1;
102
- string code = 2;
103
- string name = 3;
104
- }
105
-
106
- message SocialMedia {
107
- string id = 1;
108
- string platform = 2;
109
- string url = 3;
110
- }
111
-
112
- message CreateSocialMedia {
113
- string platform = 1;
114
- string url = 2;
115
- }
116
-
117
- message ProductUser {
118
- string id = 1;
119
- string username = 2;
120
- string avatar_url = 3;
121
- }
122
-
123
- message Category {
124
- string id = 1;
125
- string name = 2;
126
- }
127
-
128
- message Product {
129
- string id = 1;
130
- string name = 2;
131
- string description = 3;
132
- string price = 4; // Decimal as string (D7)
133
- string created_at = 5;
134
- string updated_at = 6;
135
- // profile-card preview fields
136
- string preview = 7;
137
- string final_price = 8; // Decimal as string
138
- string average_rating = 9; // number as string
139
- string status = 10;
140
- // Author of the product (same person as the profile owner, but echoed per
141
- // product so cards can render the author independently).
142
- ProductUser user = 11;
143
- repeated Category categories = 12;
144
- }
145
-
146
- message Video {
147
- string id = 1;
148
- string title = 2;
149
- string description = 3;
150
- string url = 4;
151
- string created_at = 5;
152
- string updated_at = 6;
153
- }
154
-
155
- message ActiveDevice {
156
- string id = 1;
157
- string user_agent = 2;
158
- string ip = 3;
159
- string created_at = 4;
160
- string last_used_at = 5;
161
- }
162
-
163
- message ProfileStats {
164
- int32 subscribers_count = 1;
165
- int32 subscriptions_count = 2;
166
- int32 products_count = 3;
167
- int32 videos_count = 4;
168
- }
169
-
170
- // Compact public user card for subscriber/subscription lists — no email/ban data.
171
- message ProfileUser {
172
- string id = 1;
173
- string username = 2;
174
- string avatar_url = 3;
175
- }
176
-
177
- // =================================================================
178
- // GetPublicProfile
179
- // =================================================================
180
-
181
- message GetPublicProfileRequest {
182
- string username = 1;
183
- string viewer_user_id = 2; // optional — present when caller is authenticated
184
- }
185
-
186
- message PublicProfileResponse {
187
- string id = 1;
188
- string username = 2;
189
- string full_name = 3;
190
- string rating = 4; // Decimal as string
191
- string about_me = 5;
192
- repeated SocialMedia social_media = 6;
193
- ProfileStats stats = 7;
194
- repeated Video videos = 8;
195
- repeated Product products = 9;
196
- Country country = 10;
197
- string avatar_url = 11;
198
- string banner_url = 12;
199
- string role = 13;
200
- string created_at = 14;
201
- bool hide_subscriptions = 15;
202
- // Either populated lists or empty depending on hide_subscriptions
203
- repeated ProfileUser subscriptions = 16;
204
- repeated ProfileUser subscribers = 17;
205
- bool is_owner = 18;
206
- bool is_subscribed = 19; // viewer is subscribed to this profile (false when anonymous / self)
207
- // Premium fields — populated only when the profile owner has an active
208
- // PremiumSubscription. Non-premium viewers and non-premium profiles see
209
- // empty strings / false.
210
- bool is_premium = 20;
211
- string profile_color = 21;
212
- AvatarBorder avatar_border = 22;
213
- string animated_avatar_url = 23;
214
- string animated_banner_url = 24;
215
- string premium_expires_at = 25; // ISO 8601, empty when not premium
216
- }
217
-
218
- // =================================================================
219
- // UpdateProfile
220
- // =================================================================
221
-
222
- message UpdateProfileRequest {
223
- string user_id = 1;
224
- string full_name = 2;
225
- string about_me = 3;
226
- bool newsletter_subscription = 4;
227
- bool hide_subscriptions = 5;
228
- string avatar_url = 6;
229
- string banner_url = 7;
230
- string country_id = 8;
231
- string timezone = 9;
232
- repeated CreateSocialMedia social_media = 10;
233
- // Premium-only fields — rejected with FAILED_PRECONDITION when the caller
234
- // has no active premium. Empty string clears the value.
235
- string profile_color = 11;
236
- string avatar_border_id = 12;
237
- string animated_avatar_url = 13;
238
- string animated_banner_url = 14;
239
- }
240
-
241
- // =================================================================
242
- // UpdateUser
243
- // =================================================================
244
-
245
- message UpdateUserRequest {
246
- string user_id = 1;
247
- string username = 2;
248
- string email = 3;
249
- string code = 4; // confirmation code when changing email/username
250
- }
251
-
252
- message UserResponse {
253
- User user = 1;
254
- }
255
-
256
- // =================================================================
257
- // UpdateUserRole (admin)
258
- // =================================================================
259
-
260
- message UpdateUserRoleRequest {
261
- string admin_id = 1;
262
- string user_id = 2;
263
- string role = 3;
264
- string action = 4; // "grant" (default) | "revoke"
265
- }
266
-
267
- // =================================================================
268
- // GetUsers (paginated)
269
- // =================================================================
270
-
271
- message GetUsersRequest {
272
- string page = 1;
273
- string limit = 2;
274
- string search = 3;
275
- string role_name = 4;
276
- }
277
-
278
- message GetUsersResponse {
279
- repeated User items = 1;
280
- int32 total = 2;
281
- int32 page = 3;
282
- int32 limit = 4;
283
- }
284
-
285
- // =================================================================
286
- // Active devices
287
- // =================================================================
288
-
289
- message GetActiveDevicesRequest {
290
- string user_id = 1;
291
- }
292
-
293
- message GetActiveDevicesResponse {
294
- repeated ActiveDevice devices = 1;
295
- }
296
-
297
- message ManageDevicesRequest {
298
- string user_id = 1;
299
- repeated string device_ids_to_remove = 2;
300
- }
301
-
302
- message ManageDevicesResponse {
303
- bool ok = 1;
304
- int32 removed_count = 2;
305
- }
306
-
307
- // =================================================================
308
- // GetUserFields — paginated dynamic field sets
309
- // =================================================================
310
-
311
- enum UserFieldsType {
312
- USER_FIELDS_TYPE_UNSPECIFIED = 0;
313
- USER_FIELDS_TYPE_VIDEOS = 1;
314
- USER_FIELDS_TYPE_PRODUCTS = 2;
315
- USER_FIELDS_TYPE_SUBSCRIPTIONS = 3;
316
- USER_FIELDS_TYPE_SUBSCRIBERS = 4;
317
- }
318
-
319
- message GetUserFieldsRequest {
320
- string username = 1;
321
- UserFieldsType type = 2;
322
- int32 page = 3;
323
- int32 limit = 4;
324
- string viewer_user_id = 5; // optional
325
- }
326
-
327
- message GetUserFieldsResponse {
328
- repeated Video videos = 1;
329
- repeated Product products = 2;
330
- repeated ProfileUser subscriptions = 3;
331
- repeated ProfileUser subscribers = 4;
332
- int32 total = 5;
333
- int32 page = 6;
334
- int32 limit = 7;
335
- }
336
-
337
- // =================================================================
338
- // Subscriptions
339
- // =================================================================
340
-
341
- message SubscriptionRequest {
342
- string subscriber_id = 1; // the authenticated user performing the action
343
- string target_id = 2; // the user being (un)subscribed to/from
344
- }
345
-
346
- message SubscriptionResponse {
347
- bool subscribed = 1; // state after the call
348
- int32 subscribers_count = 2; // target's subscriber count after the call
349
- }
350
-
351
- // =================================================================
352
- // Premium
353
- // =================================================================
354
-
355
- // Periods mirror PremiumPeriod enum in schema.prisma.
356
- enum PremiumPeriod {
357
- PREMIUM_PERIOD_UNSPECIFIED = 0;
358
- PREMIUM_PERIOD_MONTH_1 = 1;
359
- PREMIUM_PERIOD_MONTH_3 = 2;
360
- PREMIUM_PERIOD_MONTH_6 = 3;
361
- PREMIUM_PERIOD_MONTH_12 = 4;
362
- }
363
-
364
- message GetMyPremiumRequest {
365
- string user_id = 1;
366
- }
367
-
368
- message PremiumStateResponse {
369
- bool is_active = 1;
370
- string status = 2; // ACTIVE | EXPIRED | CANCELED | empty (never purchased)
371
- PremiumPeriod current_period = 3;
372
- string started_at = 4; // ISO 8601, empty when never purchased
373
- string expires_at = 5; // ISO 8601
374
- bool auto_renew = 6;
375
- bool has_saved_payment_method = 7;
376
- }
377
-
378
- message SetPremiumAutoRenewRequest {
379
- string user_id = 1;
380
- bool enabled = 2;
381
- }
382
-
383
- // Internal — payments-service hands us the period that was just paid for, plus
384
- // the YooKassa payment_method id (empty for balance/non-recurring purchases).
385
- message ActivatePremiumRequest {
386
- string user_id = 1;
387
- PremiumPeriod period = 2;
388
- string payment_method_id = 3; // empty for BALANCE / one-shot
389
- bool is_renewal = 4; // false = first purchase; true = cron-driven renewal
390
- }
391
-
392
- message IsPremiumActiveRequest {
393
- string user_id = 1;
394
- }
395
-
396
- message IsPremiumActiveResponse {
397
- bool is_active = 1;
398
- }
399
-
400
- // =================================================================
401
- // Avatar borders
402
- // =================================================================
403
-
404
- message AvatarBorder {
405
- string id = 1;
406
- string code = 2;
407
- string name = 3;
408
- string description = 4;
409
- string image_url = 5;
410
- bool is_animated = 6;
411
- string source = 7;
412
- int32 sort_order = 8;
413
- bool is_active = 9;
414
- }
415
-
416
- message ListAvatarBordersRequest {
417
- bool include_inactive = 1; // admin-only — gateway gates by role
418
- string source = 2; // optional filter
419
- }
420
-
421
- message ListAvatarBordersResponse {
422
- repeated AvatarBorder items = 1;
423
- }
424
-
425
- message UpsertAvatarBorderRequest {
426
- string id = 1; // empty = create
427
- string code = 2; // required when creating
428
- string name = 3;
429
- string description = 4;
430
- string image_url = 5;
431
- bool is_animated = 6;
432
- string source = 7;
433
- int32 sort_order = 8;
434
- bool is_active = 9;
435
- }
436
-
437
- message DeleteAvatarBorderRequest {
438
- string id = 1;
439
- }
440
-
441
- message DeleteAvatarBorderResponse {
442
- bool ok = 1;
443
- }
444
-
445
- // =================================================================
446
- // Account settings
447
- // =================================================================
448
-
449
- message UpdatePrivacyRequest {
450
- string user_id = 1;
451
- bool hide_email = 2;
452
- bool hide_balance = 3;
453
- bool hide_online = 4;
454
- }
455
-
456
- message UpdateEmailPrefsRequest {
457
- string user_id = 1;
458
- bool email_on_sales = 2;
459
- bool email_on_reviews = 3;
460
- bool email_on_news = 4;
461
- }
462
-
463
- message UpdateEmailPrefsResponse {
464
- bool email_on_sales = 1;
465
- bool email_on_reviews = 2;
466
- bool email_on_news = 3;
467
- }
468
-
469
- message UpdateLocaleRequest {
470
- string user_id = 1;
471
- string locale = 2; // "ru" | "en" | ...
472
- }
473
-
474
- message UpdateLocaleResponse {
475
- string locale = 1;
476
- }
477
-
478
- message SoftDeleteAccountRequest {
479
- string user_id = 1;
480
- string reason = 2; // optional, for support/audit
481
- int32 cooldown_days = 3; // 0 → use server default (30)
482
- }
483
-
484
- message SoftDeleteAccountResponse {
485
- string deleted_at = 1; // ISO 8601
486
- string scheduled_for_hard_delete = 2;
487
- }
488
-
489
- message RestoreAccountRequest {
490
- string user_id = 1;
491
- }
492
-
493
- message HardDeletePendingRequest {
494
- int32 max_batch = 1; // 0 → default 100
495
- }
496
-
497
- message HardDeletePendingResponse {
498
- int32 deleted = 1;
499
- }
500
-
501
- message ChangePasswordRequest {
502
- string user_id = 1;
503
- string current_password = 2;
504
- string new_password = 3;
505
- // Optional: revoke all other sessions on success.
506
- bool revoke_other_sessions = 4;
507
- }
508
-
509
- // =================================================================
510
- // Blocks
511
- // =================================================================
512
-
513
- message BlockUserRequest {
514
- string blocker_id = 1;
515
- string blocked_id = 2;
516
- string reason = 3; // optional
517
- }
518
-
519
- message BlockUserResponse {
520
- bool blocked = 1;
521
- }
522
-
523
- message BlockedUserRow {
524
- string id = 1;
525
- string username = 2;
526
- string avatar_url = 3;
527
- string blocked_at = 4;
528
- string reason = 5;
529
- }
530
-
531
- message ListBlockedUsersRequest {
532
- string blocker_id = 1;
533
- int32 page = 2;
534
- int32 limit = 3;
535
- }
536
-
537
- message ListBlockedUsersResponse {
538
- repeated BlockedUserRow items = 1;
539
- int32 total = 2;
540
- int32 page = 3;
541
- int32 limit = 4;
542
- }
543
-
544
- message IsBlockedRequest {
545
- string blocker_id = 1;
546
- string blocked_id = 2;
547
- }
548
-
549
- message IsBlockedResponse {
550
- bool is_blocked = 1;
551
- }
552
-
553
- // =================================================================
554
- // Data export
555
- // =================================================================
556
-
557
- message DataExportRequestRow {
558
- string id = 1;
559
- string user_id = 2;
560
- string status = 3; // PENDING | PROCESSING | READY | FAILED
561
- string file_url = 4;
562
- int32 file_size = 5;
563
- string error = 6;
564
- string created_at = 7;
565
- string completed_at = 8;
566
- }
567
-
568
- message RequestDataExportRequest {
569
- string user_id = 1;
570
- }
571
-
572
- message ListDataExportsRequest {
573
- string user_id = 1;
574
- int32 page = 2;
575
- int32 limit = 3;
576
- }
577
-
578
- message ListDataExportsResponse {
579
- repeated DataExportRequestRow items = 1;
580
- int32 total = 2;
581
- int32 page = 3;
582
- int32 limit = 4;
583
- }
584
-
585
- message PickPendingDataExportRequest {
586
- // tasks-service polls; returns one PENDING row marked PROCESSING, or empty.
587
- bool _placeholder = 1;
588
- }
589
-
590
- message CompleteDataExportRequest {
591
- string id = 1;
592
- string file_url = 2;
593
- int32 file_size = 3;
594
- string error = 4; // if set, marks FAILED instead of READY
595
- }
596
-
597
- message CollectUserExportDataRequest {
598
- string user_id = 1;
599
- }
600
-
601
- message CollectUserExportDataResponse {
602
- // Opaque JSON-as-string of user-owned data. tasks-service writes it to
603
- // S3 verbatim. Keeps the schema-specific marshaling private to users-service.
604
- string payload_json = 1;
605
- }
606
-
607
- // =================================================================
608
- // Settings — unified read
609
- // =================================================================
610
-
611
- message GetAccountSettingsRequest {
612
- string user_id = 1;
613
- }
614
-
615
- message AccountSettingsResponse {
616
- // Identity
617
- string id = 1;
618
- string username = 2;
619
- string email = 3;
620
- string full_name = 4;
621
- string about_me = 5;
622
- string avatar_url = 6;
623
- string banner_url = 7;
624
- string locale = 8;
625
- string timezone = 9;
626
- string country_id = 10;
627
- string created_at = 11;
628
-
629
- // 2FA
630
- string two_factor_type = 12; // NONE | APP | EMAIL
631
- bool two_factor_enabled = 13;
632
- int32 backup_codes_remaining = 14;
633
-
634
- // Privacy
635
- bool hide_email = 15;
636
- bool hide_balance = 16;
637
- bool hide_online = 17;
638
- bool hide_subscriptions = 18;
639
-
640
- // Email prefs
641
- bool email_on_sales = 19;
642
- bool email_on_reviews = 20;
643
- bool email_on_news = 21;
644
-
645
- // Payout defaults
646
- string payout_method = 22;
647
- string payout_details = 23;
648
-
649
- // Premium snapshot
650
- bool premium_is_active = 24;
651
- string premium_status = 25;
652
- string premium_expires_at = 26;
653
- bool premium_auto_renew = 27;
654
-
655
- // Soft-delete state
656
- string deleted_at = 28;
657
- string scheduled_for_hard_delete = 29;
658
-
659
- repeated SocialMedia social_media = 30;
660
- }
1
+ syntax = "proto3";
2
+
3
+ package users.v1;
4
+
5
+ service UsersService {
6
+ rpc GetPublicProfile(GetPublicProfileRequest) returns (PublicProfileResponse);
7
+ rpc UpdateProfile(UpdateProfileRequest) returns (PublicProfileResponse);
8
+ rpc UpdateUser(UpdateUserRequest) returns (UserResponse);
9
+ rpc UpdateUserRole(UpdateUserRoleRequest) returns (UserResponse);
10
+ rpc GetUsers(GetUsersRequest) returns (GetUsersResponse);
11
+ rpc GetActiveDevices(GetActiveDevicesRequest) returns (GetActiveDevicesResponse);
12
+ rpc ManageDevices(ManageDevicesRequest) returns (ManageDevicesResponse);
13
+ rpc GetUserFields(GetUserFieldsRequest) returns (GetUserFieldsResponse);
14
+
15
+ rpc CreateSubscription(SubscriptionRequest) returns (SubscriptionResponse);
16
+ rpc DeleteSubscription(SubscriptionRequest) returns (SubscriptionResponse);
17
+
18
+ rpc BanUser(BanUserRequest) returns (UserResponse);
19
+ rpc UnbanUser(UnbanUserRequest) returns (UserResponse);
20
+
21
+ // Premium (paid). State lives in users-service; payments-service handles
22
+ // money and calls ActivatePremium (internal) on completion.
23
+ rpc GetMyPremium(GetMyPremiumRequest) returns (PremiumStateResponse);
24
+ rpc SetPremiumAutoRenew(SetPremiumAutoRenewRequest) returns (PremiumStateResponse);
25
+ // Internal — InternalServiceGuard. Called by payments-service after a
26
+ // successful premium payment (balance or YooKassa webhook).
27
+ rpc ActivatePremium(ActivatePremiumRequest) returns (PremiumStateResponse);
28
+ // Internal — fast lookup for the commission engine in payments-service.
29
+ rpc IsPremiumActive(IsPremiumActiveRequest) returns (IsPremiumActiveResponse);
30
+
31
+ // Avatar borders (catalog + admin CRUD). Catalog read is public; mutations
32
+ // are admin only (gateway enforces with RolesGuard).
33
+ rpc ListAvatarBorders(ListAvatarBordersRequest) returns (ListAvatarBordersResponse);
34
+ rpc UpsertAvatarBorder(UpsertAvatarBorderRequest) returns (AvatarBorder);
35
+ rpc DeleteAvatarBorder(DeleteAvatarBorderRequest) returns (DeleteAvatarBorderResponse);
36
+
37
+ // Account settings — bundled write surface for the UI.
38
+ rpc UpdatePrivacy(UpdatePrivacyRequest) returns (PublicProfileResponse);
39
+ rpc UpdateEmailPrefs(UpdateEmailPrefsRequest) returns (UpdateEmailPrefsResponse);
40
+ rpc UpdateLocale(UpdateLocaleRequest) returns (UpdateLocaleResponse);
41
+
42
+ // Soft-delete + restore. Internal AuthService also queries the soft-delete
43
+ // state on login (rejects if deletedAt set).
44
+ rpc SoftDeleteAccount(SoftDeleteAccountRequest) returns (SoftDeleteAccountResponse);
45
+ rpc RestoreAccount(RestoreAccountRequest) returns (UserResponse);
46
+ rpc HardDeletePending(HardDeletePendingRequest) returns (HardDeletePendingResponse); // tasks-service cron
47
+
48
+ // Blocks
49
+ rpc BlockUser(BlockUserRequest) returns (BlockUserResponse);
50
+ rpc UnblockUser(BlockUserRequest) returns (BlockUserResponse);
51
+ rpc ListBlockedUsers(ListBlockedUsersRequest) returns (ListBlockedUsersResponse);
52
+ rpc IsBlocked(IsBlockedRequest) returns (IsBlockedResponse); // internal lookup
53
+
54
+ // Data export
55
+ rpc RequestDataExport(RequestDataExportRequest) returns (DataExportRequestRow);
56
+ rpc ListDataExports(ListDataExportsRequest) returns (ListDataExportsResponse);
57
+ rpc PickPendingDataExport(PickPendingDataExportRequest) returns (DataExportRequestRow); // tasks-service
58
+ rpc CompleteDataExport(CompleteDataExportRequest) returns (DataExportRequestRow); // tasks-service
59
+ rpc CollectUserExportData(CollectUserExportDataRequest) returns (CollectUserExportDataResponse); // tasks-service
60
+
61
+ // Unified read for the settings page
62
+ rpc GetAccountSettings(GetAccountSettingsRequest) returns (AccountSettingsResponse);
63
+
64
+ // Password — separate from generic createCode/verifyCode reset flow.
65
+ rpc ChangePassword(ChangePasswordRequest) returns (Ok);
66
+ }
67
+
68
+ message Ok { bool ok = 1; }
69
+
70
+ // =================================================================
71
+ // Common types
72
+ // =================================================================
73
+
74
+ message User {
75
+ string id = 1;
76
+ string email = 2;
77
+ string username = 3;
78
+ string avatar_url = 4;
79
+ repeated string roles = 5;
80
+ string role = 6; // primary role for backwards compat
81
+ string created_at = 7; // ISO 8601
82
+ string updated_at = 8;
83
+ bool is_banned = 9;
84
+ string banned_until = 10; // ISO 8601, empty = permanent/none
85
+ string ban_reason = 11;
86
+ }
87
+
88
+ message BanUserRequest {
89
+ string admin_id = 1;
90
+ string user_id = 2;
91
+ string reason = 3;
92
+ string until = 4; // ISO 8601; empty = permanent ban
93
+ }
94
+
95
+ message UnbanUserRequest {
96
+ string admin_id = 1;
97
+ string user_id = 2;
98
+ }
99
+
100
+ message Country {
101
+ string id = 1;
102
+ string code = 2;
103
+ string name = 3;
104
+ }
105
+
106
+ message SocialMedia {
107
+ string id = 1;
108
+ string platform = 2;
109
+ string url = 3;
110
+ }
111
+
112
+ message CreateSocialMedia {
113
+ string platform = 1;
114
+ string url = 2;
115
+ }
116
+
117
+ message ProductUser {
118
+ string id = 1;
119
+ string username = 2;
120
+ string avatar_url = 3;
121
+ }
122
+
123
+ message Category {
124
+ string id = 1;
125
+ string name = 2;
126
+ }
127
+
128
+ message Product {
129
+ string id = 1;
130
+ string name = 2;
131
+ string description = 3;
132
+ string price = 4; // Decimal as string (D7)
133
+ string created_at = 5;
134
+ string updated_at = 6;
135
+ // profile-card preview fields
136
+ string preview = 7;
137
+ string final_price = 8; // Decimal as string
138
+ string average_rating = 9; // number as string
139
+ string status = 10;
140
+ // Author of the product (same person as the profile owner, but echoed per
141
+ // product so cards can render the author independently).
142
+ ProductUser user = 11;
143
+ repeated Category categories = 12;
144
+ }
145
+
146
+ message Video {
147
+ string id = 1;
148
+ string title = 2;
149
+ string description = 3;
150
+ string url = 4;
151
+ string created_at = 5;
152
+ string updated_at = 6;
153
+ }
154
+
155
+ message ActiveDevice {
156
+ string id = 1;
157
+ string user_agent = 2;
158
+ string ip = 3;
159
+ string created_at = 4;
160
+ string last_used_at = 5;
161
+ }
162
+
163
+ message ProfileStats {
164
+ int32 subscribers_count = 1;
165
+ int32 subscriptions_count = 2;
166
+ int32 products_count = 3;
167
+ int32 videos_count = 4;
168
+ }
169
+
170
+ // Compact public user card for subscriber/subscription lists — no email/ban data.
171
+ message ProfileUser {
172
+ string id = 1;
173
+ string username = 2;
174
+ string avatar_url = 3;
175
+ }
176
+
177
+ // =================================================================
178
+ // GetPublicProfile
179
+ // =================================================================
180
+
181
+ message GetPublicProfileRequest {
182
+ string username = 1;
183
+ string viewer_user_id = 2; // optional — present when caller is authenticated
184
+ }
185
+
186
+ message PublicProfileResponse {
187
+ string id = 1;
188
+ string username = 2;
189
+ string full_name = 3;
190
+ string rating = 4; // Decimal as string
191
+ string about_me = 5;
192
+ repeated SocialMedia social_media = 6;
193
+ ProfileStats stats = 7;
194
+ repeated Video videos = 8;
195
+ repeated Product products = 9;
196
+ Country country = 10;
197
+ string avatar_url = 11;
198
+ string banner_url = 12;
199
+ string role = 13;
200
+ string created_at = 14;
201
+ bool hide_subscriptions = 15;
202
+ // Either populated lists or empty depending on hide_subscriptions
203
+ repeated ProfileUser subscriptions = 16;
204
+ repeated ProfileUser subscribers = 17;
205
+ bool is_owner = 18;
206
+ bool is_subscribed = 19; // viewer is subscribed to this profile (false when anonymous / self)
207
+ // Premium fields — populated only when the profile owner has an active
208
+ // PremiumSubscription. Non-premium viewers and non-premium profiles see
209
+ // empty strings / false.
210
+ bool is_premium = 20;
211
+ string profile_color = 21;
212
+ AvatarBorder avatar_border = 22;
213
+ string animated_avatar_url = 23;
214
+ string animated_banner_url = 24;
215
+ string premium_expires_at = 25; // ISO 8601, empty when not premium
216
+ }
217
+
218
+ // =================================================================
219
+ // UpdateProfile
220
+ // =================================================================
221
+
222
+ message UpdateProfileRequest {
223
+ string user_id = 1;
224
+ string full_name = 2;
225
+ string about_me = 3;
226
+ bool newsletter_subscription = 4;
227
+ bool hide_subscriptions = 5;
228
+ string avatar_url = 6;
229
+ string banner_url = 7;
230
+ string country_id = 8;
231
+ string timezone = 9;
232
+ repeated CreateSocialMedia social_media = 10;
233
+ // Premium-only fields — rejected with FAILED_PRECONDITION when the caller
234
+ // has no active premium. Empty string clears the value.
235
+ string profile_color = 11;
236
+ string avatar_border_id = 12;
237
+ string animated_avatar_url = 13;
238
+ string animated_banner_url = 14;
239
+ }
240
+
241
+ // =================================================================
242
+ // UpdateUser
243
+ // =================================================================
244
+
245
+ message UpdateUserRequest {
246
+ string user_id = 1;
247
+ string username = 2;
248
+ string email = 3;
249
+ string code = 4; // confirmation code when changing email/username
250
+ }
251
+
252
+ message UserResponse {
253
+ User user = 1;
254
+ }
255
+
256
+ // =================================================================
257
+ // UpdateUserRole (admin)
258
+ // =================================================================
259
+
260
+ message UpdateUserRoleRequest {
261
+ string admin_id = 1;
262
+ string user_id = 2;
263
+ string role = 3;
264
+ string action = 4; // "grant" (default) | "revoke"
265
+ }
266
+
267
+ // =================================================================
268
+ // GetUsers (paginated)
269
+ // =================================================================
270
+
271
+ message GetUsersRequest {
272
+ string page = 1;
273
+ string limit = 2;
274
+ string search = 3;
275
+ string role_name = 4;
276
+ }
277
+
278
+ message GetUsersResponse {
279
+ repeated User items = 1;
280
+ int32 total = 2;
281
+ int32 page = 3;
282
+ int32 limit = 4;
283
+ }
284
+
285
+ // =================================================================
286
+ // Active devices
287
+ // =================================================================
288
+
289
+ message GetActiveDevicesRequest {
290
+ string user_id = 1;
291
+ }
292
+
293
+ message GetActiveDevicesResponse {
294
+ repeated ActiveDevice devices = 1;
295
+ }
296
+
297
+ message ManageDevicesRequest {
298
+ string user_id = 1;
299
+ repeated string device_ids_to_remove = 2;
300
+ }
301
+
302
+ message ManageDevicesResponse {
303
+ bool ok = 1;
304
+ int32 removed_count = 2;
305
+ }
306
+
307
+ // =================================================================
308
+ // GetUserFields — paginated dynamic field sets
309
+ // =================================================================
310
+
311
+ enum UserFieldsType {
312
+ USER_FIELDS_TYPE_UNSPECIFIED = 0;
313
+ USER_FIELDS_TYPE_VIDEOS = 1;
314
+ USER_FIELDS_TYPE_PRODUCTS = 2;
315
+ USER_FIELDS_TYPE_SUBSCRIPTIONS = 3;
316
+ USER_FIELDS_TYPE_SUBSCRIBERS = 4;
317
+ }
318
+
319
+ message GetUserFieldsRequest {
320
+ string username = 1;
321
+ UserFieldsType type = 2;
322
+ int32 page = 3;
323
+ int32 limit = 4;
324
+ string viewer_user_id = 5; // optional
325
+ }
326
+
327
+ message GetUserFieldsResponse {
328
+ repeated Video videos = 1;
329
+ repeated Product products = 2;
330
+ repeated ProfileUser subscriptions = 3;
331
+ repeated ProfileUser subscribers = 4;
332
+ int32 total = 5;
333
+ int32 page = 6;
334
+ int32 limit = 7;
335
+ }
336
+
337
+ // =================================================================
338
+ // Subscriptions
339
+ // =================================================================
340
+
341
+ message SubscriptionRequest {
342
+ string subscriber_id = 1; // the authenticated user performing the action
343
+ string target_id = 2; // the user being (un)subscribed to/from
344
+ }
345
+
346
+ message SubscriptionResponse {
347
+ bool subscribed = 1; // state after the call
348
+ int32 subscribers_count = 2; // target's subscriber count after the call
349
+ }
350
+
351
+ // =================================================================
352
+ // Premium
353
+ // =================================================================
354
+
355
+ // Periods mirror PremiumPeriod enum in schema.prisma.
356
+ enum PremiumPeriod {
357
+ PREMIUM_PERIOD_UNSPECIFIED = 0;
358
+ PREMIUM_PERIOD_MONTH_1 = 1;
359
+ PREMIUM_PERIOD_MONTH_3 = 2;
360
+ PREMIUM_PERIOD_MONTH_6 = 3;
361
+ PREMIUM_PERIOD_MONTH_12 = 4;
362
+ }
363
+
364
+ message GetMyPremiumRequest {
365
+ string user_id = 1;
366
+ }
367
+
368
+ message PremiumStateResponse {
369
+ bool is_active = 1;
370
+ string status = 2; // ACTIVE | EXPIRED | CANCELED | empty (never purchased)
371
+ PremiumPeriod current_period = 3;
372
+ string started_at = 4; // ISO 8601, empty when never purchased
373
+ string expires_at = 5; // ISO 8601
374
+ bool auto_renew = 6;
375
+ bool has_saved_payment_method = 7;
376
+ }
377
+
378
+ message SetPremiumAutoRenewRequest {
379
+ string user_id = 1;
380
+ bool enabled = 2;
381
+ }
382
+
383
+ // Internal — payments-service hands us the period that was just paid for, plus
384
+ // the YooKassa payment_method id (empty for balance/non-recurring purchases).
385
+ message ActivatePremiumRequest {
386
+ string user_id = 1;
387
+ PremiumPeriod period = 2;
388
+ string payment_method_id = 3; // empty for BALANCE / one-shot
389
+ bool is_renewal = 4; // false = first purchase; true = cron-driven renewal
390
+ }
391
+
392
+ message IsPremiumActiveRequest {
393
+ string user_id = 1;
394
+ }
395
+
396
+ message IsPremiumActiveResponse {
397
+ bool is_active = 1;
398
+ }
399
+
400
+ // =================================================================
401
+ // Avatar borders
402
+ // =================================================================
403
+
404
+ message AvatarBorder {
405
+ string id = 1;
406
+ string code = 2;
407
+ string name = 3;
408
+ string description = 4;
409
+ string image_url = 5;
410
+ bool is_animated = 6;
411
+ string source = 7;
412
+ int32 sort_order = 8;
413
+ bool is_active = 9;
414
+ }
415
+
416
+ message ListAvatarBordersRequest {
417
+ bool include_inactive = 1; // admin-only — gateway gates by role
418
+ string source = 2; // optional filter
419
+ }
420
+
421
+ message ListAvatarBordersResponse {
422
+ repeated AvatarBorder items = 1;
423
+ }
424
+
425
+ message UpsertAvatarBorderRequest {
426
+ string id = 1; // empty = create
427
+ string code = 2; // required when creating
428
+ string name = 3;
429
+ string description = 4;
430
+ string image_url = 5;
431
+ bool is_animated = 6;
432
+ string source = 7;
433
+ int32 sort_order = 8;
434
+ bool is_active = 9;
435
+ }
436
+
437
+ message DeleteAvatarBorderRequest {
438
+ string id = 1;
439
+ }
440
+
441
+ message DeleteAvatarBorderResponse {
442
+ bool ok = 1;
443
+ }
444
+
445
+ // =================================================================
446
+ // Account settings
447
+ // =================================================================
448
+
449
+ message UpdatePrivacyRequest {
450
+ string user_id = 1;
451
+ bool hide_email = 2;
452
+ bool hide_balance = 3;
453
+ bool hide_online = 4;
454
+ }
455
+
456
+ message UpdateEmailPrefsRequest {
457
+ string user_id = 1;
458
+ bool email_on_sales = 2;
459
+ bool email_on_reviews = 3;
460
+ bool email_on_news = 4;
461
+ }
462
+
463
+ message UpdateEmailPrefsResponse {
464
+ bool email_on_sales = 1;
465
+ bool email_on_reviews = 2;
466
+ bool email_on_news = 3;
467
+ }
468
+
469
+ message UpdateLocaleRequest {
470
+ string user_id = 1;
471
+ string locale = 2; // "ru" | "en" | ...
472
+ }
473
+
474
+ message UpdateLocaleResponse {
475
+ string locale = 1;
476
+ }
477
+
478
+ message SoftDeleteAccountRequest {
479
+ string user_id = 1;
480
+ string reason = 2; // optional, for support/audit
481
+ int32 cooldown_days = 3; // 0 → use server default (30)
482
+ }
483
+
484
+ message SoftDeleteAccountResponse {
485
+ string deleted_at = 1; // ISO 8601
486
+ string scheduled_for_hard_delete = 2;
487
+ }
488
+
489
+ message RestoreAccountRequest {
490
+ string user_id = 1;
491
+ }
492
+
493
+ message HardDeletePendingRequest {
494
+ int32 max_batch = 1; // 0 → default 100
495
+ }
496
+
497
+ message HardDeletePendingResponse {
498
+ int32 deleted = 1;
499
+ }
500
+
501
+ message ChangePasswordRequest {
502
+ string user_id = 1;
503
+ string current_password = 2;
504
+ string new_password = 3;
505
+ // Optional: revoke all other sessions on success.
506
+ bool revoke_other_sessions = 4;
507
+ }
508
+
509
+ // =================================================================
510
+ // Blocks
511
+ // =================================================================
512
+
513
+ message BlockUserRequest {
514
+ string blocker_id = 1;
515
+ string blocked_id = 2;
516
+ string reason = 3; // optional
517
+ }
518
+
519
+ message BlockUserResponse {
520
+ bool blocked = 1;
521
+ }
522
+
523
+ message BlockedUserRow {
524
+ string id = 1;
525
+ string username = 2;
526
+ string avatar_url = 3;
527
+ string blocked_at = 4;
528
+ string reason = 5;
529
+ }
530
+
531
+ message ListBlockedUsersRequest {
532
+ string blocker_id = 1;
533
+ int32 page = 2;
534
+ int32 limit = 3;
535
+ }
536
+
537
+ message ListBlockedUsersResponse {
538
+ repeated BlockedUserRow items = 1;
539
+ int32 total = 2;
540
+ int32 page = 3;
541
+ int32 limit = 4;
542
+ }
543
+
544
+ message IsBlockedRequest {
545
+ string blocker_id = 1;
546
+ string blocked_id = 2;
547
+ }
548
+
549
+ message IsBlockedResponse {
550
+ bool is_blocked = 1;
551
+ }
552
+
553
+ // =================================================================
554
+ // Data export
555
+ // =================================================================
556
+
557
+ message DataExportRequestRow {
558
+ string id = 1;
559
+ string user_id = 2;
560
+ string status = 3; // PENDING | PROCESSING | READY | FAILED
561
+ string file_url = 4;
562
+ int32 file_size = 5;
563
+ string error = 6;
564
+ string created_at = 7;
565
+ string completed_at = 8;
566
+ }
567
+
568
+ message RequestDataExportRequest {
569
+ string user_id = 1;
570
+ }
571
+
572
+ message ListDataExportsRequest {
573
+ string user_id = 1;
574
+ int32 page = 2;
575
+ int32 limit = 3;
576
+ }
577
+
578
+ message ListDataExportsResponse {
579
+ repeated DataExportRequestRow items = 1;
580
+ int32 total = 2;
581
+ int32 page = 3;
582
+ int32 limit = 4;
583
+ }
584
+
585
+ message PickPendingDataExportRequest {
586
+ // tasks-service polls; returns one PENDING row marked PROCESSING, or empty.
587
+ bool _placeholder = 1;
588
+ }
589
+
590
+ message CompleteDataExportRequest {
591
+ string id = 1;
592
+ string file_url = 2;
593
+ int32 file_size = 3;
594
+ string error = 4; // if set, marks FAILED instead of READY
595
+ }
596
+
597
+ message CollectUserExportDataRequest {
598
+ string user_id = 1;
599
+ }
600
+
601
+ message CollectUserExportDataResponse {
602
+ // Opaque JSON-as-string of user-owned data. tasks-service writes it to
603
+ // S3 verbatim. Keeps the schema-specific marshaling private to users-service.
604
+ string payload_json = 1;
605
+ }
606
+
607
+ // =================================================================
608
+ // Settings — unified read
609
+ // =================================================================
610
+
611
+ message GetAccountSettingsRequest {
612
+ string user_id = 1;
613
+ }
614
+
615
+ message AccountSettingsResponse {
616
+ // Identity
617
+ string id = 1;
618
+ string username = 2;
619
+ string email = 3;
620
+ string full_name = 4;
621
+ string about_me = 5;
622
+ string avatar_url = 6;
623
+ string banner_url = 7;
624
+ string locale = 8;
625
+ string timezone = 9;
626
+ string country_id = 10;
627
+ string created_at = 11;
628
+
629
+ // 2FA
630
+ string two_factor_type = 12; // NONE | APP | EMAIL
631
+ bool two_factor_enabled = 13;
632
+ int32 backup_codes_remaining = 14;
633
+
634
+ // Privacy
635
+ bool hide_email = 15;
636
+ bool hide_balance = 16;
637
+ bool hide_online = 17;
638
+ bool hide_subscriptions = 18;
639
+
640
+ // Email prefs
641
+ bool email_on_sales = 19;
642
+ bool email_on_reviews = 20;
643
+ bool email_on_news = 21;
644
+
645
+ // Payout defaults
646
+ string payout_method = 22;
647
+ string payout_details = 23;
648
+
649
+ // Premium snapshot
650
+ bool premium_is_active = 24;
651
+ string premium_status = 25;
652
+ string premium_expires_at = 26;
653
+ bool premium_auto_renew = 27;
654
+
655
+ // Soft-delete state
656
+ string deleted_at = 28;
657
+ string scheduled_for_hard_delete = 29;
658
+
659
+ repeated SocialMedia social_media = 30;
660
+ }