|
|
@@ -0,0 +1,168 @@
|
|
|
+syntax = "proto3";
|
|
|
+
|
|
|
+package chatto.api.v1;
|
|
|
+
|
|
|
+import "google/protobuf/field_mask.proto";
|
|
|
+import "buf/validate/validate.proto";
|
|
|
+import "chatto/api/v1/common.proto";
|
|
|
+import "chatto/api/v1/member_directory.proto";
|
|
|
+import "chatto/api/v1/pagination.proto";
|
|
|
+import "chatto/api/v1/users.proto";
|
|
|
+
|
|
|
+// Request for users visible to the authenticated user.
|
|
|
+// Results use creation time, oldest first. Users without a creation time
|
|
|
+// appear last, ordered by case-insensitive login.
|
|
|
+message ListUsersRequest {
|
|
|
+ reserved 2, 3;
|
|
|
+ reserved "limit", "offset";
|
|
|
+
|
|
|
+ // Optional case-insensitive search against login and display name.
|
|
|
+ string search = 1 [(buf.validate.field).string.max_len = 100];
|
|
|
+ // Defaults to 20 results when absent or limit is zero. Maximum: 500.
|
|
|
+ PageRequest page = 4;
|
|
|
+}
|
|
|
+
|
|
|
+// User directory page.
|
|
|
+message ListUsersResponse {
|
|
|
+ reserved 2, 3;
|
|
|
+ reserved "total_count", "has_more";
|
|
|
+
|
|
|
+ // Users in the requested page.
|
|
|
+ repeated DirectoryMember users = 1;
|
|
|
+ // Page metadata.
|
|
|
+ PageInfo page = 4;
|
|
|
+}
|
|
|
+
|
|
|
+// Request one visible user by stable user ID or login.
|
|
|
+message GetUserRequest {
|
|
|
+ oneof target {
|
|
|
+ // Target stable user ID.
|
|
|
+ string user_id = 1 [(buf.validate.field).string.min_len = 1];
|
|
|
+ // Target login identifier.
|
|
|
+ string login = 2 [(buf.validate.field).string.min_len = 1];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// User directory response.
|
|
|
+message GetUserResponse {
|
|
|
+ // Requested user.
|
|
|
+ DirectoryMember user = 1;
|
|
|
+}
|
|
|
+
|
|
|
+// Request user directory rows for a set of stable user IDs.
|
|
|
+message BatchGetUsersRequest {
|
|
|
+ // Required target user IDs. Unknown IDs are omitted from the response.
|
|
|
+ repeated string user_ids = 1 [(buf.validate.field).repeated = {
|
|
|
+ min_items: 1
|
|
|
+ max_items: 100
|
|
|
+ items: {
|
|
|
+ string: {min_len: 1}
|
|
|
+ }
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+// Batch user directory response.
|
|
|
+message BatchGetUsersResponse {
|
|
|
+ // Found users. The server preserves first-seen request order and
|
|
|
+ // de-duplicates repeated IDs.
|
|
|
+ repeated DirectoryMember users = 1;
|
|
|
+}
|
|
|
+
|
|
|
+// Request to update one user's public profile. update_mask selects fields
|
|
|
+// to apply or reset. Fields outside the mask stay unchanged.
|
|
|
+message UpdateUserProfileRequest {
|
|
|
+ // Required target user ID. Self-updates are allowed. Updating another user
|
|
|
+ // requires the applicable account-management or bot-management authority.
|
|
|
+ string user_id = 1 [(buf.validate.field).string.min_len = 1];
|
|
|
+ // New non-empty display name. The server also rejects control and
|
|
|
+ // confusing invisible characters.
|
|
|
+ optional string display_name = 2 [(buf.validate.field).string = {min_len: 1, max_len: 32}];
|
|
|
+ // New login identifier, when changing it. The server accepts ASCII letters,
|
|
|
+ // digits, period, underscore, and hyphen, starting with a letter or digit.
|
|
|
+ optional string login = 3 [(buf.validate.field).string = {
|
|
|
+ min_len: 2
|
|
|
+ max_len: 32
|
|
|
+ }];
|
|
|
+ // New Markdown biography, when changing it. Empty clears the bio.
|
|
|
+ optional string bio = 4 [(buf.validate.field).string.max_len = 1000];
|
|
|
+ // Editable fields to apply or reset: display_name, login, bio.
|
|
|
+ // Omit to infer populated fields; * selects all editable fields. An explicit
|
|
|
+ // empty mask is invalid. Unselected values are ignored. Selected absent values
|
|
|
+ // reset the field to its default, subject to field validation.
|
|
|
+ google.protobuf.FieldMask update_mask = 5;
|
|
|
+}
|
|
|
+
|
|
|
+// Result of a user profile update.
|
|
|
+message UpdateUserProfileResponse {
|
|
|
+ // Updated user profile.
|
|
|
+ User user = 1;
|
|
|
+}
|
|
|
+
|
|
|
+// Request to upload and set one user's avatar.
|
|
|
+message UploadAvatarRequest {
|
|
|
+ reserved 1 to 3;
|
|
|
+ reserved "filename", "content_type";
|
|
|
+
|
|
|
+ // Image payload. The server validates, resizes, and stores a WebP avatar.
|
|
|
+ ImageUpload image = 4;
|
|
|
+ // Required target user ID. Self-updates are allowed. Updating another user
|
|
|
+ // requires the applicable account-management or bot-management authority.
|
|
|
+ string user_id = 5 [(buf.validate.field).string.min_len = 1];
|
|
|
+}
|
|
|
+
|
|
|
+// Result of uploading one user's avatar.
|
|
|
+message UploadAvatarResponse {
|
|
|
+ // Updated user profile.
|
|
|
+ User user = 1;
|
|
|
+}
|
|
|
+
|
|
|
+// Request to delete one user's avatar.
|
|
|
+message DeleteAvatarRequest {
|
|
|
+ // Required target user ID. Self-updates are allowed. Updating another user
|
|
|
+ // requires the applicable account-management or bot-management authority.
|
|
|
+ string user_id = 1 [(buf.validate.field).string.min_len = 1];
|
|
|
+}
|
|
|
+
|
|
|
+// Result of deleting one user's avatar.
|
|
|
+message DeleteAvatarResponse {
|
|
|
+ // Updated user profile.
|
|
|
+ User user = 1;
|
|
|
+}
|
|
|
+
|
|
|
+// Reads users and manages public user profiles and avatars.
|
|
|
+service UserService {
|
|
|
+ // Lists users visible to the authenticated user. Admin-sensitive account
|
|
|
+ // fields stay out of this public row shape.
|
|
|
+ rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
|
|
|
+ // Gets one visible user by user ID or login. Returns NOT_FOUND when the
|
|
|
+ // target is unknown.
|
|
|
+ rpc GetUser(GetUserRequest) returns (GetUserResponse);
|
|
|
+ // Gets visible user rows for multiple stable user IDs. Unknown IDs are
|
|
|
+ // omitted from the response.
|
|
|
+ rpc BatchGetUsers(BatchGetUsersRequest) returns (BatchGetUsersResponse);
|
|
|
+ // Updates the target user's login, display name, and/or bio. Users can
|
|
|
+ // update themselves. Updating another human requires user.manage-accounts.
|
|
|
+ // A human who updates a bot needs ownership of the bot,
|
|
|
+ // user.manage-accounts, or bot.manage. A bot cannot target another account.
|
|
|
+ // A login change returns FAILED_PRECONDITION while the target's
|
|
|
+ // username-change cooldown is active. A successful login change starts a
|
|
|
+ // new cooldown. This also applies when a bot owner or bot manager changes a
|
|
|
+ // bot's login. A caller with user.manage-accounts bypasses the cooldown and
|
|
|
+ // does not start it. A case-only change does not check or start the
|
|
|
+ // cooldown. Unknown or deleted targets return NOT_FOUND. An invalid ID or a
|
|
|
+ // request without a selected field returns INVALID_ARGUMENT.
|
|
|
+ rpc UpdateUserProfile(UpdateUserProfileRequest) returns (UpdateUserProfileResponse);
|
|
|
+ // Uploads and sets an avatar for the target user. Users can update
|
|
|
+ // themselves. Updating another human requires user.manage-accounts.
|
|
|
+ // A human who updates a bot needs ownership of the bot,
|
|
|
+ // user.manage-accounts, or bot.manage.
|
|
|
+ // A bot cannot target another account. Unknown or deleted targets return
|
|
|
+ // NOT_FOUND. Invalid IDs or missing images return INVALID_ARGUMENT.
|
|
|
+ rpc UploadAvatar(UploadAvatarRequest) returns (UploadAvatarResponse);
|
|
|
+ // Deletes the target user's avatar. Authorization matches UploadAvatar. The
|
|
|
+ // call is idempotent. Unknown or deleted targets return NOT_FOUND. An
|
|
|
+ // invalid ID returns INVALID_ARGUMENT.
|
|
|
+ rpc DeleteAvatar(DeleteAvatarRequest) returns (DeleteAvatarResponse) {
|
|
|
+ option idempotency_level = IDEMPOTENT;
|
|
|
+ }
|
|
|
+}
|