mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: update list users
This commit is contained in:
@ -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,8 +27,18 @@ 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: {},
|
||||
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]) {
|
||||
@ -50,7 +60,7 @@ export const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
||||
}
|
||||
requestCache.delete(username);
|
||||
userMap[username] = user;
|
||||
set(userMap);
|
||||
set({ userMapByUsername: userMap });
|
||||
return user;
|
||||
},
|
||||
getUserByUsername: (username: string) => {
|
||||
@ -68,7 +78,7 @@ export const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
||||
const username = extractUsernameFromName(updatedUser.name);
|
||||
const userMap = get().userMapByUsername;
|
||||
userMap[username] = updatedUser;
|
||||
set(userMap);
|
||||
set({ userMapByUsername: userMap });
|
||||
return updatedUser;
|
||||
},
|
||||
deleteUser: async (name: string) => {
|
||||
@ -105,4 +115,5 @@ export const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
||||
set({ userSetting: updatedUserSetting });
|
||||
return updatedUserSetting;
|
||||
},
|
||||
}));
|
||||
}))
|
||||
);
|
||||
|
Reference in New Issue
Block a user