chore: update workspace service

This commit is contained in:
Steven 2024-03-21 21:39:34 +08:00
parent 1d83c68cb5
commit 18d16abdb5
16 changed files with 121 additions and 89 deletions

View file

@ -14,18 +14,21 @@ service WorkspaceService {
}
message WorkspaceProfile {
// The name of intance owner.
// Format: "users/{id}"
string owner = 1;
// version is the current version of instance
string version = 1;
string version = 2;
// mode is the instance mode (e.g. "prod", "dev" or "demo").
string mode = 2;
// allow_registration is whether the registration is allowed.
bool allow_registration = 3;
// allow_password_login is whether the password login is allowed.
bool disable_password_login = 4;
string mode = 3;
// disallow_signup is whether the signup is disallowed.
bool disallow_signup = 4;
// disable_password_login is whether the password login is disabled.
bool disable_password_login = 5;
// additional_script is the additional script.
string additional_script = 5;
string additional_script = 6;
// additional_style is the additional style.
string additional_style = 6;
string additional_style = 7;
}
message GetWorkspaceProfileRequest {}

View file

@ -2600,10 +2600,11 @@ Used internally for obfuscating the page token.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| owner | [string](#string) | | The name of intance owner. Format: "users/{id}" |
| version | [string](#string) | | version is the current version of instance |
| mode | [string](#string) | | mode is the instance mode (e.g. "prod", "dev" or "demo"). |
| allow_registration | [bool](#bool) | | allow_registration is whether the registration is allowed. |
| disable_password_login | [bool](#bool) | | allow_password_login is whether the password login is allowed. |
| disallow_signup | [bool](#bool) | | disallow_signup is whether the signup is disallowed. |
| disable_password_login | [bool](#bool) | | disable_password_login is whether the password login is disabled. |
| additional_script | [string](#string) | | additional_script is the additional script. |
| additional_style | [string](#string) | | additional_style is the additional style. |

View file

@ -26,18 +26,21 @@ type WorkspaceProfile struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The name of intance owner.
// Format: "users/{id}"
Owner string `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"`
// version is the current version of instance
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
// mode is the instance mode (e.g. "prod", "dev" or "demo").
Mode string `protobuf:"bytes,2,opt,name=mode,proto3" json:"mode,omitempty"`
// allow_registration is whether the registration is allowed.
AllowRegistration bool `protobuf:"varint,3,opt,name=allow_registration,json=allowRegistration,proto3" json:"allow_registration,omitempty"`
// allow_password_login is whether the password login is allowed.
DisablePasswordLogin bool `protobuf:"varint,4,opt,name=disable_password_login,json=disablePasswordLogin,proto3" json:"disable_password_login,omitempty"`
Mode string `protobuf:"bytes,3,opt,name=mode,proto3" json:"mode,omitempty"`
// disallow_signup is whether the signup is disallowed.
DisallowSignup bool `protobuf:"varint,4,opt,name=disallow_signup,json=disallowSignup,proto3" json:"disallow_signup,omitempty"`
// disable_password_login is whether the password login is disabled.
DisablePasswordLogin bool `protobuf:"varint,5,opt,name=disable_password_login,json=disablePasswordLogin,proto3" json:"disable_password_login,omitempty"`
// additional_script is the additional script.
AdditionalScript string `protobuf:"bytes,5,opt,name=additional_script,json=additionalScript,proto3" json:"additional_script,omitempty"`
AdditionalScript string `protobuf:"bytes,6,opt,name=additional_script,json=additionalScript,proto3" json:"additional_script,omitempty"`
// additional_style is the additional style.
AdditionalStyle string `protobuf:"bytes,6,opt,name=additional_style,json=additionalStyle,proto3" json:"additional_style,omitempty"`
AdditionalStyle string `protobuf:"bytes,7,opt,name=additional_style,json=additionalStyle,proto3" json:"additional_style,omitempty"`
}
func (x *WorkspaceProfile) Reset() {
@ -72,6 +75,13 @@ func (*WorkspaceProfile) Descriptor() ([]byte, []int) {
return file_api_v2_workspace_service_proto_rawDescGZIP(), []int{0}
}
func (x *WorkspaceProfile) GetOwner() string {
if x != nil {
return x.Owner
}
return ""
}
func (x *WorkspaceProfile) GetVersion() string {
if x != nil {
return x.Version
@ -86,9 +96,9 @@ func (x *WorkspaceProfile) GetMode() string {
return ""
}
func (x *WorkspaceProfile) GetAllowRegistration() bool {
func (x *WorkspaceProfile) GetDisallowSignup() bool {
if x != nil {
return x.AllowRegistration
return x.DisallowSignup
}
return false
}
@ -206,22 +216,23 @@ var file_api_v2_workspace_service_proto_rawDesc = []byte{
0x63, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x12, 0x0c, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x1a, 0x1c,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd, 0x01, 0x0a,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x02, 0x0a,
0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6d,
0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12,
0x2d, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c,
0x6f, 0x77, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f,
0x77, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e,
0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x53, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x12, 0x34,
0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f,
0x72, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14,
0x72, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14,
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4c,
0x6f, 0x67, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x61, 0x6c, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
0x61, 0x6c, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x10, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x63, 0x72, 0x69, 0x70,
0x74, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f,
0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x64, 0x64,
0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x64, 0x64,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x22, 0x1c, 0x0a, 0x1a,
0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66,
0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6a, 0x0a, 0x1b, 0x47, 0x65,

View file

@ -1857,9 +1857,10 @@ GetActivity returns the activity with the given id.
| Name | Type | Description | Required |
| ---- | ---- | ----------- | -------- |
| owner | string | | No |
| version | string | | No |
| mode | string | mode is the instance mode (e.g. "prod", "dev" or "demo"). | No |
| allowRegistration | boolean | allow_registration is whether the registration is allowed. | No |
| disablePasswordLogin | boolean | allow_password_login is whether the password login is allowed. | No |
| disallowSignup | boolean | disallow_signup is whether the signup is disallowed. | No |
| disablePasswordLogin | boolean | disable_password_login is whether the password login is disabled. | No |
| additionalScript | string | additional_script is the additional script. | No |
| additionalStyle | string | additional_style is the additional style. | No |

View file

@ -2346,18 +2346,23 @@ definitions:
v2WorkspaceProfile:
type: object
properties:
owner:
type: string
title: |-
The name of intance owner.
Format: "users/{id}"
version:
type: string
title: version is the current version of instance
mode:
type: string
description: mode is the instance mode (e.g. "prod", "dev" or "demo").
allowRegistration:
disallowSignup:
type: boolean
description: allow_registration is whether the registration is allowed.
description: disallow_signup is whether the signup is disallowed.
disablePasswordLogin:
type: boolean
description: allow_password_login is whether the password login is allowed.
description: disable_password_login is whether the password login is disabled.
additionalScript:
type: string
description: additional_script is the additional script.

View file

@ -3,15 +3,56 @@ package v2
import (
"context"
"github.com/pkg/errors"
apiv2pb "github.com/usememos/memos/proto/gen/api/v2"
"github.com/usememos/memos/store"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func (s *APIV2Service) GetWorkspaceProfile(_ context.Context, _ *apiv2pb.GetWorkspaceProfileRequest) (*apiv2pb.GetWorkspaceProfileResponse, error) {
var owner *apiv2pb.User = nil
func (s *APIV2Service) GetWorkspaceProfile(ctx context.Context, _ *apiv2pb.GetWorkspaceProfileRequest) (*apiv2pb.GetWorkspaceProfileResponse, error) {
workspaceProfile := &apiv2pb.WorkspaceProfile{
Version: s.Profile.Version,
Mode: s.Profile.Mode,
}
owner, err := s.GetInstanceOwner(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get instance owner: %v", err)
}
if owner != nil {
workspaceProfile.Owner = owner.Name
}
generalSetting, err := s.Store.GetWorkspaceGeneralSetting(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace general setting: %v", err)
}
workspaceProfile.DisallowSignup = generalSetting.DisallowSignup
workspaceProfile.DisablePasswordLogin = generalSetting.DisallowPasswordLogin
workspaceProfile.AdditionalStyle = generalSetting.AdditionalStyle
workspaceProfile.AdditionalScript = generalSetting.AdditionalScript
return &apiv2pb.GetWorkspaceProfileResponse{
WorkspaceProfile: workspaceProfile,
}, nil
}
func (s *APIV2Service) GetInstanceOwner(ctx context.Context) (*apiv2pb.User, error) {
if owner != nil {
return owner, nil
}
hostUserType := store.RoleHost
user, err := s.Store.GetUser(ctx, &store.FindUser{
Role: &hostUserType,
})
if err != nil {
return nil, errors.Wrapf(err, "failed to find owner")
}
if user == nil {
return nil, errors.New("owner not found")
}
owner = convertUserFromStore(user)
return owner, nil
}

View file

@ -16,7 +16,7 @@ const App = () => {
const globalStore = useGlobalStore();
const workspaceSettingStore = useWorkspaceSettingStore();
const userStore = useUserStore();
const { appearance, locale, systemStatus } = globalStore.state;
const { appearance, locale, systemStatus, workspaceProfile } = globalStore.state;
const userSetting = userStore.userSetting;
const workspaceGeneralSetting =
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_GENERAL).generalSetting ||
@ -24,10 +24,10 @@ const App = () => {
// Redirect to sign up page if no host.
useEffect(() => {
if (!systemStatus.host) {
if (!workspaceProfile.owner) {
navigateTo("/auth/signup");
}
}, [systemStatus.host]);
}, [workspaceProfile.owner]);
useEffect(() => {
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");

View file

@ -15,7 +15,7 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
const currentUser = useCurrentUser();
const userStore = useUserStore();
const globalStore = useGlobalStore();
const profile = globalStore.state.systemStatus.profile;
const profile = globalStore.state.workspaceProfile;
const [newPassword, setNewPassword] = useState("");
const [newPasswordAgain, setNewPasswordAgain] = useState("");

View file

@ -3,24 +3,14 @@ import { useEffect, useState } from "react";
import { toast } from "react-hot-toast";
import { Link } from "react-router-dom";
import * as api from "@/helpers/api";
import { useGlobalStore } from "@/store/module";
import { useTranslate } from "@/utils/i18n";
import showCreateIdentityProviderDialog from "../CreateIdentityProviderDialog";
import { showCommonDialog } from "../Dialog/CommonDialog";
import Icon from "../Icon";
import LearnMore from "../LearnMore";
interface State {
disablePasswordLogin: boolean;
}
const SSOSection = () => {
const t = useTranslate();
const globalStore = useGlobalStore();
const systemStatus = globalStore.state.systemStatus;
const [state] = useState<State>({
disablePasswordLogin: systemStatus.disablePasswordLogin,
});
const [identityProviderList, setIdentityProviderList] = useState<IdentityProvider[]>([]);
useEffect(() => {
@ -33,11 +23,7 @@ const SSOSection = () => {
};
const handleDeleteIdentityProvider = async (identityProvider: IdentityProvider) => {
let content = t("setting.sso-section.confirm-delete", { name: identityProvider.name });
if (state.disablePasswordLogin) {
content += "\n\n" + t("setting.sso-section.disabled-password-login-warning");
}
const content = t("setting.sso-section.confirm-delete", { name: identityProvider.name });
showCommonDialog({
title: t("setting.sso-section.delete-sso"),

View file

@ -88,7 +88,7 @@ const Setting = () => {
onClick={() => handleSectionSelectorItemClick(item)}
/>
))}
<span className="px-3 mt-2 opacity-70 text-sm">Version: v{globalStore.state.systemStatus.profile.version}</span>
<span className="px-3 mt-2 opacity-70 text-sm">Version: v{globalStore.state.workspaceProfile.version}</span>
</div>
</>
) : null}

View file

@ -23,8 +23,7 @@ const SignIn = () => {
const workspaceSettingStore = useWorkspaceSettingStore();
const userStore = useUserStore();
const actionBtnLoadingState = useLoading(false);
const { appearance, locale, systemStatus } = globalStore.state;
const mode = systemStatus.profile.mode;
const { appearance, locale, systemStatus, workspaceProfile } = globalStore.state;
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [remember, setRemember] = useState(true);
@ -42,11 +41,11 @@ const SignIn = () => {
}, []);
useEffect(() => {
if (mode === "demo") {
if (workspaceProfile.mode === "demo") {
setUsername("memos-demo");
setPassword("secret");
}
}, [mode]);
}, [workspaceProfile.mode]);
const handleUsernameInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value as string;

View file

@ -17,7 +17,7 @@ const SignUp = () => {
const globalStore = useGlobalStore();
const userStore = useUserStore();
const actionBtnLoadingState = useLoading(false);
const { appearance, locale, systemStatus } = globalStore.state;
const { appearance, locale, systemStatus, workspaceProfile } = globalStore.state;
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
@ -119,7 +119,7 @@ const SignUp = () => {
</Button>
</div>
</form>
{!systemStatus.host && <p className="w-full mt-4 text-sm font-medium dark:text-gray-500">{t("auth.host-tip")}</p>}
{!workspaceProfile.owner && <p className="w-full mt-4 text-sm font-medium dark:text-gray-500">{t("auth.host-tip")}</p>}
<p className="w-full mt-4 text-sm">
<span className="dark:text-gray-500">{t("auth.sign-in-tip")}</span>
<Link to="/auth" className="cursor-pointer ml-2 text-blue-600 hover:underline" unstable_viewTransition>

View file

@ -1,6 +1,8 @@
import { workspaceServiceClient } from "@/grpcweb";
import * as api from "@/helpers/api";
import storage from "@/helpers/storage";
import i18n from "@/i18n";
import { WorkspaceProfile } from "@/types/proto/api/v2/workspace_service";
import { findNearestMatchedLanguage } from "@/utils/i18n";
import store, { useAppSelector } from "../";
import { setAppearance, setGlobalState, setLocale } from "../reducer/global";
@ -22,8 +24,14 @@ export const initialGlobalState = async () => {
appearance: "system",
},
} as SystemStatus,
workspaceProfile: WorkspaceProfile.fromPartial({}),
};
const { workspaceProfile } = await workspaceServiceClient.getWorkspaceProfile({});
if (workspaceProfile) {
defaultGlobalState.workspaceProfile = workspaceProfile;
}
const { data } = await api.getSystemStatus();
if (data) {
const customizedProfile = data.customizedProfile;
@ -62,7 +70,7 @@ export const useGlobalStore = () => {
return store.getState().global.systemStatus.disablePublicMemos;
},
isDev: () => {
return state.systemStatus.profile.mode !== "prod";
return state.workspaceProfile.mode !== "prod";
},
fetchSystemStatus: async () => {
const { data: systemStatus } = await api.getSystemStatus();

View file

@ -1,9 +1,11 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { WorkspaceProfile } from "@/types/proto/api/v2/workspace_service";
interface State {
locale: Locale;
appearance: Appearance;
systemStatus: SystemStatus;
workspaceProfile: WorkspaceProfile;
}
const globalSlice = createSlice({
@ -12,11 +14,6 @@ const globalSlice = createSlice({
locale: "en",
appearance: "system",
systemStatus: {
host: undefined,
profile: {
mode: "demo",
version: "",
},
disablePasswordLogin: false,
disablePublicMemos: false,
memoDisplayWithUpdatedTs: false,
@ -28,6 +25,7 @@ const globalSlice = createSlice({
appearance: "system",
},
},
workspaceProfile: WorkspaceProfile.fromPartial({}),
} as State,
reducers: {
setGlobalState: (state, action: PayloadAction<Partial<State>>) => {

View file

@ -1,8 +1,3 @@
interface Profile {
mode: string;
version: string;
}
interface CustomizedProfile {
name: string;
logoUrl: string;
@ -12,8 +7,6 @@ interface CustomizedProfile {
}
interface SystemStatus {
host?: User;
profile: Profile;
// System settings
disablePasswordLogin: boolean;
disablePublicMemos: boolean;

View file

@ -1,14 +0,0 @@
type UserRole = "HOST" | "USER";
interface User {
id: number;
createdTs: number;
updatedTs: number;
username: string;
role: UserRole;
email: string;
nickname: string;
avatarUrl: string;
}