chore: update list users

pull/2658/head^2
Steven 1 year ago
parent c267074851
commit df3303dcd3

@ -27,6 +27,29 @@ var (
usernameMatcher = regexp.MustCompile("^[a-z0-9]([a-z0-9-]{1,30}[a-z0-9])$")
)
func (s *APIV2Service) ListUsers(ctx context.Context, _ *apiv2pb.ListUsersRequest) (*apiv2pb.ListUsersResponse, error) {
currentUser, err := getCurrentUser(ctx, s.Store)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
}
if currentUser.Role != store.RoleHost && currentUser.Role != store.RoleAdmin {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
}
users, err := s.Store.ListUsers(ctx, &store.FindUser{})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to list users: %v", err)
}
response := &apiv2pb.ListUsersResponse{
Users: []*apiv2pb.User{},
}
for _, user := range users {
response.Users = append(response.Users, convertUserFromStore(user))
}
return response, nil
}
func (s *APIV2Service) GetUser(ctx context.Context, request *apiv2pb.GetUserRequest) (*apiv2pb.GetUserResponse, error) {
username, err := ExtractUsernameFromName(request.Name)
if err != nil {

@ -12,6 +12,10 @@ import "google/protobuf/timestamp.proto";
option go_package = "gen/api/v2";
service UserService {
// ListUsers returns a list of users.
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) {
option (google.api.http) = {get: "/api/v2/users"};
}
// GetUser gets a user by name.
rpc GetUser(GetUserRequest) returns (GetUserResponse) {
option (google.api.http) = {get: "/api/v2/{name=users/*}"};
@ -99,6 +103,12 @@ message User {
google.protobuf.Timestamp update_time = 10;
}
message ListUsersRequest {}
message ListUsersResponse {
repeated User users = 1;
}
message GetUserRequest {
// The name of the user.
// Format: users/{username}

@ -31,6 +31,8 @@
- [GetUserSettingResponse](#memos-api-v2-GetUserSettingResponse)
- [ListUserAccessTokensRequest](#memos-api-v2-ListUserAccessTokensRequest)
- [ListUserAccessTokensResponse](#memos-api-v2-ListUserAccessTokensResponse)
- [ListUsersRequest](#memos-api-v2-ListUsersRequest)
- [ListUsersResponse](#memos-api-v2-ListUsersResponse)
- [UpdateUserRequest](#memos-api-v2-UpdateUserRequest)
- [UpdateUserResponse](#memos-api-v2-UpdateUserResponse)
- [UpdateUserSettingRequest](#memos-api-v2-UpdateUserSettingRequest)
@ -541,6 +543,31 @@
<a name="memos-api-v2-ListUsersRequest"></a>
### ListUsersRequest
<a name="memos-api-v2-ListUsersResponse"></a>
### ListUsersResponse
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| users | [User](#memos-api-v2-User) | repeated | |
<a name="memos-api-v2-UpdateUserRequest"></a>
### UpdateUserRequest
@ -691,6 +718,7 @@
| Method Name | Request Type | Response Type | Description |
| ----------- | ------------ | ------------- | ------------|
| ListUsers | [ListUsersRequest](#memos-api-v2-ListUsersRequest) | [ListUsersResponse](#memos-api-v2-ListUsersResponse) | ListUsers returns a list of users. |
| GetUser | [GetUserRequest](#memos-api-v2-GetUserRequest) | [GetUserResponse](#memos-api-v2-GetUserResponse) | GetUser gets a user by name. |
| CreateUser | [CreateUserRequest](#memos-api-v2-CreateUserRequest) | [CreateUserResponse](#memos-api-v2-CreateUserResponse) | CreateUser creates a new user. |
| UpdateUser | [UpdateUserRequest](#memos-api-v2-UpdateUserRequest) | [UpdateUserResponse](#memos-api-v2-UpdateUserResponse) | UpdateUser updates a user. |

File diff suppressed because it is too large Load Diff

@ -31,6 +31,24 @@ var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = metadata.Join
func request_UserService_ListUsers_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListUsersRequest
var metadata runtime.ServerMetadata
msg, err := client.ListUsers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_ListUsers_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListUsersRequest
var metadata runtime.ServerMetadata
msg, err := server.ListUsers(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_GetUser_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetUserRequest
var metadata runtime.ServerMetadata
@ -619,6 +637,31 @@ func local_request_UserService_DeleteUserAccessToken_0(ctx context.Context, mars
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterUserServiceHandlerFromEndpoint instead.
func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server UserServiceServer) error {
mux.Handle("GET", pattern_UserService_ListUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/ListUsers", runtime.WithHTTPPathPattern("/api/v2/users"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_ListUsers_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_ListUsers_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_UserService_GetUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@ -885,6 +928,28 @@ func RegisterUserServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn
// "UserServiceClient" to call the correct interceptors.
func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client UserServiceClient) error {
mux.Handle("GET", pattern_UserService_ListUsers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/ListUsers", runtime.WithHTTPPathPattern("/api/v2/users"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_ListUsers_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_ListUsers_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_UserService_GetUser_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@ -1087,6 +1152,8 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
}
var (
pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v2", "users"}, ""))
pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v2", "users", "name"}, ""))
pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "users"}, ""))
@ -1107,6 +1174,8 @@ var (
)
var (
forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage
forward_UserService_GetUser_0 = runtime.ForwardResponseMessage
forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage

@ -19,6 +19,7 @@ import (
const _ = grpc.SupportPackageIsVersion7
const (
UserService_ListUsers_FullMethodName = "/memos.api.v2.UserService/ListUsers"
UserService_GetUser_FullMethodName = "/memos.api.v2.UserService/GetUser"
UserService_CreateUser_FullMethodName = "/memos.api.v2.UserService/CreateUser"
UserService_UpdateUser_FullMethodName = "/memos.api.v2.UserService/UpdateUser"
@ -34,6 +35,8 @@ const (
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type UserServiceClient interface {
// ListUsers returns a list of users.
ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error)
// GetUser gets a user by name.
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error)
// CreateUser creates a new user.
@ -60,6 +63,15 @@ func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
return &userServiceClient{cc}
}
func (c *userServiceClient) ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error) {
out := new(ListUsersResponse)
err := c.cc.Invoke(ctx, UserService_ListUsers_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) {
out := new(GetUserResponse)
err := c.cc.Invoke(ctx, UserService_GetUser_FullMethodName, in, out, opts...)
@ -145,6 +157,8 @@ func (c *userServiceClient) DeleteUserAccessToken(ctx context.Context, in *Delet
// All implementations must embed UnimplementedUserServiceServer
// for forward compatibility
type UserServiceServer interface {
// ListUsers returns a list of users.
ListUsers(context.Context, *ListUsersRequest) (*ListUsersResponse, error)
// GetUser gets a user by name.
GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error)
// CreateUser creates a new user.
@ -168,6 +182,9 @@ type UserServiceServer interface {
type UnimplementedUserServiceServer struct {
}
func (UnimplementedUserServiceServer) ListUsers(context.Context, *ListUsersRequest) (*ListUsersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUsers not implemented")
}
func (UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented")
}
@ -208,6 +225,24 @@ func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
s.RegisterService(&UserService_ServiceDesc, srv)
}
func _UserService_ListUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListUsersRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).ListUsers(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_ListUsers_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).ListUsers(ctx, req.(*ListUsersRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserRequest)
if err := dec(in); err != nil {
@ -377,6 +412,10 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "memos.api.v2.UserService",
HandlerType: (*UserServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListUsers",
Handler: _UserService_ListUsers_Handler,
},
{
MethodName: "GetUser",
Handler: _UserService_GetUser_Handler,

@ -39,7 +39,7 @@ func NewFrontendService(profile *profile.Profile, store *store.Store) *FrontendS
}
func (s *FrontendService) Serve(e *echo.Echo) {
// Use echo static middleware to serve the built dist folder
// Use echo static middleware to serve the built dist folder.
// refer: https://github.com/labstack/echo/blob/master/middleware/static.go
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
Skipper: defaultAPIRequestSkipper,
@ -80,6 +80,10 @@ func (s *FrontendService) registerRoutes(e *echo.Echo) {
return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set")
}
instanceURL := instanceURLSetting.Value
if instanceURL == "" {
return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set")
}
robotsTxt := fmt.Sprintf(`User-agent: *
Allow: /
Host: %s
@ -98,8 +102,11 @@ Sitemap: %s/sitemap.xml`, instanceURL, instanceURL)
if instanceURLSetting == nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set")
}
instanceURL := instanceURLSetting.Value
if instanceURL == "" {
return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set")
}
urlsets := []string{}
// Append memo list.
memoList, err := s.Store.ListMemos(ctx, &store.FindMemo{

@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { useUserV1Store, UserNamePrefix } from "@/store/v1";
import { useUserV1Store } from "@/store/v1";
import { User } from "@/types/proto/api/v2/user_service";
import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog";
import Icon from "./Icon";
@ -49,7 +50,7 @@ const ChangeMemberPasswordDialog: React.FC<Props> = (props: Props) => {
try {
await userV1Store.updateUser(
{
name: `${UserNamePrefix}${user.username}`,
name: user.name,
password: newPassword,
},
["password"]
@ -66,7 +67,7 @@ const ChangeMemberPasswordDialog: React.FC<Props> = (props: Props) => {
<>
<div className="dialog-header-container !w-64">
<p className="title-text">
{t("setting.account-section.change-password")} ({user.username})
{t("setting.account-section.change-password")} ({user.nickname})
</p>
<button className="btn close-btn" onClick={handleCloseBtnClick}>
<Icon.X />

@ -2,11 +2,10 @@ import { Button, Dropdown, Input, Menu, MenuButton } from "@mui/joy";
import React, { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { userServiceClient } from "@/grpcweb";
import * as api from "@/helpers/api";
import useCurrentUser from "@/hooks/useCurrentUser";
import { UserNamePrefix, useUserV1Store } from "@/store/v1";
import { UserNamePrefix, extractUsernameFromName, useUserV1Store } from "@/store/v1";
import { RowStatus } from "@/types/proto/api/v2/common";
import { User_Role } from "@/types/proto/api/v2/user_service";
import { User, User_Role } from "@/types/proto/api/v2/user_service";
import { useTranslate } from "@/utils/i18n";
import showChangeMemberPasswordDialog from "../ChangeMemberPasswordDialog";
import { showCommonDialog } from "../Dialog/CommonDialog";
@ -32,8 +31,8 @@ const MemberSection = () => {
}, []);
const fetchUserList = async () => {
const { data } = await api.getUserList();
setUserList(data.sort((a, b) => a.id - b.id));
const users = await userV1Store.fetchUsers();
setUserList(users);
};
const handleUsernameInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@ -81,13 +80,13 @@ const MemberSection = () => {
const handleArchiveUserClick = (user: User) => {
showCommonDialog({
title: t("setting.member-section.archive-member"),
content: t("setting.member-section.archive-warning", { username: user.username }),
content: t("setting.member-section.archive-warning", { username: user.nickname }),
style: "danger",
dialogName: "archive-user-dialog",
onConfirm: async () => {
await userServiceClient.updateUser({
user: {
name: `${UserNamePrefix}${user.username}`,
name: user.name,
rowStatus: RowStatus.ARCHIVED,
},
updateMask: ["row_status"],
@ -100,7 +99,7 @@ const MemberSection = () => {
const handleRestoreUserClick = async (user: User) => {
await userServiceClient.updateUser({
user: {
name: `${UserNamePrefix}${user.username}`,
name: user.name,
rowStatus: RowStatus.ACTIVE,
},
updateMask: ["row_status"],
@ -111,11 +110,11 @@ const MemberSection = () => {
const handleDeleteUserClick = (user: User) => {
showCommonDialog({
title: t("setting.member-section.delete-member"),
content: t("setting.member-section.delete-warning", { username: user.username }),
content: t("setting.member-section.delete-warning", { username: user.nickname }),
style: "danger",
dialogName: "delete-user-dialog",
onConfirm: async () => {
await userV1Store.deleteUser(`${UserNamePrefix}${user.username}`);
await userV1Store.deleteUser(user.name);
fetchUserList();
},
});
@ -165,8 +164,8 @@ const MemberSection = () => {
<tr key={user.id}>
<td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-300">{user.id}</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">
{user.username}
<span className="ml-1 italic">{user.rowStatus === "ARCHIVED" && "(Archived)"}</span>
{extractUsernameFromName(user.name)}
<span className="ml-1 italic">{user.rowStatus === RowStatus.ARCHIVED && "(Archived)"}</span>
</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">{user.nickname}</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">{user.email}</td>
@ -185,7 +184,7 @@ const MemberSection = () => {
>
{t("setting.account-section.change-password")}
</button>
{user.rowStatus === "NORMAL" ? (
{user.rowStatus === RowStatus.ACTIVE ? (
<button
className="w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick={() => handleArchiveUserClick(user)}

@ -44,10 +44,6 @@ export function signout() {
return axios.post("/api/v1/auth/signout");
}
export function getUserList() {
return axios.get<User[]>("/api/v1/user");
}
export function getMemoStats(username: string) {
return axios.get<number[]>(`/api/v1/memo/stats?creatorUsername=${username}`);
}

@ -1,21 +1,21 @@
import { create } from "zustand";
import { combine } from "zustand/middleware";
import { authServiceClient, userServiceClient } from "@/grpcweb";
import { User, UserSetting } from "@/types/proto/api/v2/user_service";
import { UserNamePrefix, extractUsernameFromName } from "./resourceName";
interface UserV1Store {
interface State {
userMapByUsername: Record<string, User>;
currentUser?: User;
userSetting?: UserSetting;
getOrFetchUserByUsername: (username: string) => Promise<User>;
getUserByUsername: (username: string) => User;
updateUser: (user: Partial<User>, updateMask: string[]) => Promise<User>;
deleteUser: (name: string) => Promise<void>;
fetchCurrentUser: () => Promise<User>;
setCurrentUser: (user: User) => void;
updateUserSetting: (userSetting: Partial<UserSetting>, updateMark: string[]) => Promise<UserSetting>;
}
const getDefaultState = (): State => ({
userMapByUsername: {},
currentUser: undefined,
userSetting: undefined,
});
const getDefaultUserSetting = () => {
return UserSetting.fromPartial({
locale: "en",
@ -27,82 +27,93 @@ const getDefaultUserSetting = () => {
// Request cache is used to prevent multiple requests.
const requestCache = new Map<string, Promise<any>>();
export const useUserV1Store = create<UserV1Store>()((set, get) => ({
userMapByUsername: {},
getOrFetchUserByUsername: async (username: string) => {
const userMap = get().userMapByUsername;
if (userMap[username]) {
return userMap[username] as User;
}
if (requestCache.has(username)) {
return await requestCache.get(username);
}
export const useUserV1Store = create(
combine(getDefaultState(), (set, get) => ({
fetchUsers: async () => {
const { users } = await userServiceClient.listUsers({});
const userMap = get().userMapByUsername;
for (const user of users) {
const username = extractUsernameFromName(user.name);
userMap[username] = user;
}
set({ userMapByUsername: userMap });
return users;
},
getOrFetchUserByUsername: async (username: string) => {
const userMap = get().userMapByUsername;
if (userMap[username]) {
return userMap[username] as User;
}
if (requestCache.has(username)) {
return await requestCache.get(username);
}
const promisedUser = userServiceClient
.getUser({
name: `${UserNamePrefix}${username}`,
})
.then(({ user }) => user);
requestCache.set(username, promisedUser);
const user = await promisedUser;
if (!user) {
throw new Error("User not found");
}
requestCache.delete(username);
userMap[username] = user;
set(userMap);
return user;
},
getUserByUsername: (username: string) => {
const userMap = get().userMapByUsername;
return userMap[username];
},
updateUser: async (user: Partial<User>, updateMask: string[]) => {
const { user: updatedUser } = await userServiceClient.updateUser({
user: user,
updateMask: updateMask,
});
if (!updatedUser) {
throw new Error("User not found");
}
const username = extractUsernameFromName(updatedUser.name);
const userMap = get().userMapByUsername;
userMap[username] = updatedUser;
set(userMap);
return updatedUser;
},
deleteUser: async (name: string) => {
await userServiceClient.deleteUser({
name,
});
},
fetchCurrentUser: async () => {
const { user } = await authServiceClient.getAuthStatus({});
if (!user) {
throw new Error("User not found");
}
set({ currentUser: user });
const { setting } = await userServiceClient.getUserSetting({});
set({
userSetting: UserSetting.fromPartial({
...getDefaultUserSetting(),
...setting,
}),
});
return user;
},
setCurrentUser: (user: User) => {
set({ currentUser: user });
},
updateUserSetting: async (userSetting: Partial<UserSetting>, updateMask: string[]) => {
const { setting: updatedUserSetting } = await userServiceClient.updateUserSetting({
setting: userSetting,
updateMask: updateMask,
});
if (!updatedUserSetting) {
throw new Error("User setting not found");
}
set({ userSetting: updatedUserSetting });
return updatedUserSetting;
},
}));
const promisedUser = userServiceClient
.getUser({
name: `${UserNamePrefix}${username}`,
})
.then(({ user }) => user);
requestCache.set(username, promisedUser);
const user = await promisedUser;
if (!user) {
throw new Error("User not found");
}
requestCache.delete(username);
userMap[username] = user;
set({ userMapByUsername: userMap });
return user;
},
getUserByUsername: (username: string) => {
const userMap = get().userMapByUsername;
return userMap[username];
},
updateUser: async (user: Partial<User>, updateMask: string[]) => {
const { user: updatedUser } = await userServiceClient.updateUser({
user: user,
updateMask: updateMask,
});
if (!updatedUser) {
throw new Error("User not found");
}
const username = extractUsernameFromName(updatedUser.name);
const userMap = get().userMapByUsername;
userMap[username] = updatedUser;
set({ userMapByUsername: userMap });
return updatedUser;
},
deleteUser: async (name: string) => {
await userServiceClient.deleteUser({
name,
});
},
fetchCurrentUser: async () => {
const { user } = await authServiceClient.getAuthStatus({});
if (!user) {
throw new Error("User not found");
}
set({ currentUser: user });
const { setting } = await userServiceClient.getUserSetting({});
set({
userSetting: UserSetting.fromPartial({
...getDefaultUserSetting(),
...setting,
}),
});
return user;
},
setCurrentUser: (user: User) => {
set({ currentUser: user });
},
updateUserSetting: async (userSetting: Partial<UserSetting>, updateMask: string[]) => {
const { setting: updatedUserSetting } = await userServiceClient.updateUserSetting({
setting: userSetting,
updateMask: updateMask,
});
if (!updatedUserSetting) {
throw new Error("User setting not found");
}
set({ userSetting: updatedUserSetting });
return updatedUserSetting;
},
}))
);

Loading…
Cancel
Save