mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
fix: update workspace general setting
This commit is contained in:
@ -5,9 +5,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/usememos/memos/internal/log"
|
||||
"github.com/usememos/memos/server/profile"
|
||||
"github.com/usememos/memos/store"
|
||||
)
|
||||
@ -18,18 +16,12 @@ type SystemStatus struct {
|
||||
DBSize int64 `json:"dbSize"`
|
||||
|
||||
// System settings
|
||||
// Allow sign up.
|
||||
AllowSignUp bool `json:"allowSignUp"`
|
||||
// Disable password login.
|
||||
DisablePasswordLogin bool `json:"disablePasswordLogin"`
|
||||
// Disable public memos.
|
||||
DisablePublicMemos bool `json:"disablePublicMemos"`
|
||||
// Max upload size.
|
||||
MaxUploadSizeMiB int `json:"maxUploadSizeMiB"`
|
||||
// Additional style.
|
||||
AdditionalStyle string `json:"additionalStyle"`
|
||||
// Additional script.
|
||||
AdditionalScript string `json:"additionalScript"`
|
||||
// Customized server profile, including server name and external url.
|
||||
CustomizedProfile CustomizedProfile `json:"customizedProfile"`
|
||||
// Storage service ID.
|
||||
@ -74,8 +66,6 @@ func (s *APIV1Service) GetSystemStatus(c echo.Context) error {
|
||||
Mode: s.Profile.Mode,
|
||||
Version: s.Profile.Version,
|
||||
},
|
||||
// Allow sign up by default.
|
||||
AllowSignUp: true,
|
||||
MaxUploadSizeMiB: 32,
|
||||
CustomizedProfile: CustomizedProfile{
|
||||
Name: "Memos",
|
||||
@ -101,10 +91,7 @@ func (s *APIV1Service) GetSystemStatus(c echo.Context) error {
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find workspace general setting").SetInternal(err)
|
||||
}
|
||||
systemStatus.AllowSignUp = !workspaceGeneralSetting.DisallowSignup
|
||||
systemStatus.DisablePasswordLogin = workspaceGeneralSetting.DisallowPasswordLogin
|
||||
systemStatus.AdditionalStyle = workspaceGeneralSetting.AdditionalStyle
|
||||
systemStatus.AdditionalScript = workspaceGeneralSetting.AdditionalScript
|
||||
|
||||
systemSettingList, err := s.Store.ListWorkspaceSettings(ctx, &store.FindWorkspaceSetting{})
|
||||
if err != nil {
|
||||
@ -140,7 +127,7 @@ func (s *APIV1Service) GetSystemStatus(c echo.Context) error {
|
||||
case SystemSettingMemoDisplayWithUpdatedTsName.String():
|
||||
systemStatus.MemoDisplayWithUpdatedTs = baseValue.(bool)
|
||||
default:
|
||||
log.Warn("Unknown system setting name", zap.String("setting name", systemSetting.Name))
|
||||
// Skip unknown system setting.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import "strings"
|
||||
|
||||
var authenticationAllowlistMethods = map[string]bool{
|
||||
"/memos.api.v2.WorkspaceService/GetWorkspaceProfile": true,
|
||||
"/memos.api.v2.WorkspaceService/GetWorkspaceSetting": true,
|
||||
"/memos.api.v2.WorkspaceSettingService/GetWorkspaceSetting": true,
|
||||
"/memos.api.v2.AuthService/GetAuthStatus": true,
|
||||
"/memos.api.v2.AuthService/SignIn": true,
|
||||
"/memos.api.v2.AuthService/SignInWithSSO": true,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useColorScheme } from "@mui/joy";
|
||||
import mermaid from "mermaid";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Outlet } from "react-router-dom";
|
||||
@ -7,19 +6,22 @@ import storage from "./helpers/storage";
|
||||
import { getSystemColorScheme } from "./helpers/utils";
|
||||
import useNavigateTo from "./hooks/useNavigateTo";
|
||||
import { useGlobalStore } from "./store/module";
|
||||
import { useUserStore } from "./store/v1";
|
||||
import { useUserStore, useWorkspaceSettingStore } from "./store/v1";
|
||||
import { WorkspaceGeneralSetting, WorkspaceSettingKey } from "./types/proto/store/workspace_setting";
|
||||
|
||||
const App = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const navigateTo = useNavigateTo();
|
||||
const { mode, setMode } = useColorScheme();
|
||||
const globalStore = useGlobalStore();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const userStore = useUserStore();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { appearance, locale, systemStatus } = globalStore.state;
|
||||
const userSetting = userStore.userSetting;
|
||||
|
||||
mermaid.initialize({ startOnLoad: false, theme: mode });
|
||||
const workspaceGeneralSetting =
|
||||
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_GENERAL).generalSetting ||
|
||||
WorkspaceGeneralSetting.fromPartial({});
|
||||
|
||||
// Redirect to sign up page if no host.
|
||||
useEffect(() => {
|
||||
@ -30,6 +32,7 @@ const App = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const initialState = async () => {
|
||||
await workspaceSettingStore.fetchWorkspaceSetting(WorkspaceSettingKey.WORKSPACE_SETTING_GENERAL);
|
||||
try {
|
||||
await userStore.fetchCurrentUser();
|
||||
} catch (error) {
|
||||
@ -61,21 +64,21 @@ const App = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (systemStatus.additionalStyle) {
|
||||
if (workspaceGeneralSetting.additionalStyle) {
|
||||
const styleEl = document.createElement("style");
|
||||
styleEl.innerHTML = systemStatus.additionalStyle;
|
||||
styleEl.innerHTML = workspaceGeneralSetting.additionalStyle;
|
||||
styleEl.setAttribute("type", "text/css");
|
||||
document.body.insertAdjacentElement("beforeend", styleEl);
|
||||
}
|
||||
}, [systemStatus.additionalStyle]);
|
||||
}, [workspaceGeneralSetting.additionalStyle]);
|
||||
|
||||
useEffect(() => {
|
||||
if (systemStatus.additionalScript) {
|
||||
if (workspaceGeneralSetting.additionalScript) {
|
||||
const scriptEl = document.createElement("script");
|
||||
scriptEl.innerHTML = systemStatus.additionalScript;
|
||||
scriptEl.innerHTML = workspaceGeneralSetting.additionalScript;
|
||||
document.head.appendChild(scriptEl);
|
||||
}
|
||||
}, [systemStatus.additionalScript]);
|
||||
}, [workspaceGeneralSetting.additionalScript]);
|
||||
|
||||
// Dynamic update metadata with customized profile.
|
||||
useEffect(() => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useColorScheme } from "@mui/joy";
|
||||
import mermaid from "mermaid";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
@ -6,14 +7,16 @@ interface Props {
|
||||
}
|
||||
|
||||
const MermaidBlock: React.FC<Props> = ({ content }: Props) => {
|
||||
const { mode } = useColorScheme();
|
||||
const mermaidDockBlock = useRef<null>(null);
|
||||
mermaid.initialize({ startOnLoad: false, theme: mode });
|
||||
|
||||
useEffect(() => {
|
||||
if (!mermaidDockBlock.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Render mermaid when mounted
|
||||
// Render mermaid when mounted.
|
||||
mermaid.run({
|
||||
nodes: [mermaidDockBlock.current],
|
||||
});
|
||||
|
@ -72,13 +72,14 @@ const SystemSection = () => {
|
||||
};
|
||||
|
||||
const handleDisablePasswordLoginChanged = async (value: boolean) => {
|
||||
setWorkspaceGeneralSetting({ ...workspaceGeneralSetting, disallowPasswordLogin: value });
|
||||
const setting = { ...workspaceGeneralSetting, disallowPasswordLogin: value };
|
||||
await workspaceSettingServiceClient.setWorkspaceSetting({
|
||||
setting: {
|
||||
name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.WORKSPACE_SETTING_GENERAL}`,
|
||||
generalSetting: workspaceGeneralSetting,
|
||||
generalSetting: setting,
|
||||
},
|
||||
});
|
||||
setWorkspaceGeneralSetting(setting);
|
||||
};
|
||||
|
||||
const handleUpdateCustomizedProfileButtonClick = () => {
|
||||
@ -223,7 +224,7 @@ const SystemSection = () => {
|
||||
<p className="font-medium text-gray-700 dark:text-gray-500">{t("common.settings")}</p>
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<span className="normal-text">{t("setting.system-section.allow-user-signup")}</span>
|
||||
<Switch checked={workspaceGeneralSetting.disallowSignup} onChange={(event) => handleAllowSignUpChanged(event.target.checked)} />
|
||||
<Switch checked={!workspaceGeneralSetting.disallowSignup} onChange={(event) => handleAllowSignUpChanged(event.target.checked)} />
|
||||
</div>
|
||||
<div className="w-full flex flex-row justify-between items-center">
|
||||
<span className="normal-text">{t("setting.system-section.disable-password-login")}</span>
|
||||
|
@ -11,13 +11,16 @@ import { absolutifyLink } from "@/helpers/utils";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { useGlobalStore } from "@/store/module";
|
||||
import { useUserStore } from "@/store/v1";
|
||||
import { useUserStore, useWorkspaceSettingStore } from "@/store/v1";
|
||||
import { WorkspaceGeneralSetting } from "@/types/proto/api/v2/workspace_setting_service";
|
||||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const SignIn = () => {
|
||||
const t = useTranslate();
|
||||
const navigateTo = useNavigateTo();
|
||||
const globalStore = useGlobalStore();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const userStore = useUserStore();
|
||||
const actionBtnLoadingState = useLoading(false);
|
||||
const { appearance, locale, systemStatus } = globalStore.state;
|
||||
@ -25,8 +28,12 @@ const SignIn = () => {
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [remember, setRemember] = useState(true);
|
||||
const disablePasswordLogin = systemStatus.disablePasswordLogin;
|
||||
const [identityProviderList, setIdentityProviderList] = useState<IdentityProvider[]>([]);
|
||||
const workspaceGeneralSetting =
|
||||
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.WORKSPACE_SETTING_GENERAL).generalSetting ||
|
||||
WorkspaceGeneralSetting.fromPartial({});
|
||||
|
||||
console.log("workspaceGeneralSetting", workspaceGeneralSetting);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchIdentityProviderList = async () => {
|
||||
@ -112,7 +119,7 @@ const SignIn = () => {
|
||||
<img className="h-14 w-auto rounded-full shadow" src={systemStatus.customizedProfile.logoUrl} alt="" />
|
||||
<p className="ml-2 text-5xl text-black opacity-80 dark:text-gray-200">{systemStatus.customizedProfile.name}</p>
|
||||
</div>
|
||||
{!disablePasswordLogin && (
|
||||
{!workspaceGeneralSetting.disallowPasswordLogin && (
|
||||
<>
|
||||
<form className="w-full mt-2" onSubmit={handleFormSubmit}>
|
||||
<div className="flex flex-col justify-start items-start w-full gap-4">
|
||||
@ -164,7 +171,7 @@ const SignIn = () => {
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
{systemStatus.allowSignUp && (
|
||||
{!workspaceGeneralSetting.disallowSignup && (
|
||||
<p className="w-full mt-4 text-sm">
|
||||
<span className="dark:text-gray-500">{t("auth.sign-up-tip")}</span>
|
||||
<Link to="/auth/signup" className="cursor-pointer ml-2 text-blue-600 hover:underline" unstable_viewTransition>
|
||||
@ -176,7 +183,7 @@ const SignIn = () => {
|
||||
)}
|
||||
{identityProviderList.length > 0 && (
|
||||
<>
|
||||
{!disablePasswordLogin && <Divider className="!my-4">{t("common.or")}</Divider>}
|
||||
{!workspaceGeneralSetting.disallowPasswordLogin && <Divider className="!my-4">{t("common.or")}</Divider>}
|
||||
<div className="w-full flex flex-col space-y-2">
|
||||
{identityProviderList.map((identityProvider) => (
|
||||
<Button
|
||||
|
@ -10,12 +10,9 @@ export const initialGlobalState = async () => {
|
||||
locale: "en" as Locale,
|
||||
appearance: "system" as Appearance,
|
||||
systemStatus: {
|
||||
allowSignUp: false,
|
||||
disablePasswordLogin: false,
|
||||
disablePublicMemos: false,
|
||||
maxUploadSizeMiB: 0,
|
||||
additionalStyle: "",
|
||||
additionalScript: "",
|
||||
memoDisplayWithUpdatedTs: false,
|
||||
customizedProfile: {
|
||||
name: "Memos",
|
||||
|
@ -17,11 +17,8 @@ const globalSlice = createSlice({
|
||||
mode: "demo",
|
||||
version: "",
|
||||
},
|
||||
allowSignUp: false,
|
||||
disablePasswordLogin: false,
|
||||
disablePublicMemos: false,
|
||||
additionalStyle: "",
|
||||
additionalScript: "",
|
||||
memoDisplayWithUpdatedTs: false,
|
||||
customizedProfile: {
|
||||
name: "Memos",
|
||||
|
@ -3,3 +3,4 @@ export * from "./memo";
|
||||
export * from "./inbox";
|
||||
export * from "./resourceName";
|
||||
export * from "./resource";
|
||||
export * from "./workspaceSetting";
|
||||
|
29
web/src/store/v1/workspaceSetting.ts
Normal file
29
web/src/store/v1/workspaceSetting.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { create } from "zustand";
|
||||
import { combine } from "zustand/middleware";
|
||||
import { workspaceSettingServiceClient } from "@/grpcweb";
|
||||
import { WorkspaceSetting } from "@/types/proto/api/v2/workspace_setting_service";
|
||||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
||||
import { WorkspaceSettingPrefix } from "./resourceName";
|
||||
|
||||
interface State {
|
||||
workspaceSettingByName: Record<string, WorkspaceSetting>;
|
||||
}
|
||||
|
||||
const getDefaultState = (): State => ({
|
||||
workspaceSettingByName: {},
|
||||
});
|
||||
|
||||
export const useWorkspaceSettingStore = create(
|
||||
combine(getDefaultState(), (set, get) => ({
|
||||
fetchWorkspaceSetting: async (key: WorkspaceSettingKey) => {
|
||||
const { setting } = await workspaceSettingServiceClient.getWorkspaceSetting({ name: `${WorkspaceSettingPrefix}${key}` });
|
||||
if (!setting) {
|
||||
throw new Error("Workspace setting not found");
|
||||
}
|
||||
set({ workspaceSettingByName: { ...get().workspaceSettingByName, [setting.name]: setting } });
|
||||
},
|
||||
getWorkspaceSettingByKey: (key: WorkspaceSettingKey) => {
|
||||
return get().workspaceSettingByName[`${WorkspaceSettingPrefix}${key}`];
|
||||
},
|
||||
})),
|
||||
);
|
3
web/src/types/modules/system.d.ts
vendored
3
web/src/types/modules/system.d.ts
vendored
@ -15,12 +15,9 @@ interface SystemStatus {
|
||||
host?: User;
|
||||
profile: Profile;
|
||||
// System settings
|
||||
allowSignUp: boolean;
|
||||
disablePasswordLogin: boolean;
|
||||
disablePublicMemos: boolean;
|
||||
maxUploadSizeMiB: number;
|
||||
additionalStyle: string;
|
||||
additionalScript: string;
|
||||
customizedProfile: CustomizedProfile;
|
||||
storageServiceId: number;
|
||||
localStoragePath: string;
|
||||
|
Reference in New Issue
Block a user