Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@

# misc
**/.DS_Store
**/.env.local
**/.env.development.local
**/.env.test.local
**/.env.production.local

**/npm-debug.log*
**/yarn-debug.log*
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ yarn-error.log*
.vscode/*
.idea
stats.html
/public/moeflow-runtime-config.json
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
locale-json: src/locales/en.json src/locales/zh-cn.json

locale-json-watch:
watch make locale-json

format:
npm run format:fix

src/locales/en.json: src/locales/messages.yaml
node_modules/.bin/tsx scripts/generate-locale-json.ts

src/locales/zh-cn.json: src/locales/messages.yaml
node_modules/.bin/tsx scripts/generate-locale-json.ts

63 changes: 2 additions & 61 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@fortawesome/free-regular-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.19",
"@gradio/client": "1.14.0",
"@gradio/client": "^1.14.0",
"@jokester/ts-commonutil": "^0.6.1",
"@reduxjs/toolkit": "^1.9.7",
"@zip.js/zip.js": "^2.7.60",
Expand Down
7 changes: 7 additions & 0 deletions public/moeflow-runtime-config.sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"comment": "Runtime configuration overrides. See src/configs.tsx",
"moeflowCompanion": {
"gradioUrl": "http://localhost:7860",
"defaultMultimodalModel": "gemini-2.0-flash-lite"
}
}
25 changes: 18 additions & 7 deletions src/apis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
import qs from 'qs';
import { createElement } from 'react';
import { Icon } from '../components';
import { configs, runtimeConfig } from '@/configs';
import { runtimeConfig } from '@/configs';
import { createDebugLogger } from '@/utils/debug-logger';
import { getIntl } from '@/locales';
import store from '../store';
import { setUserToken } from '@/store/user/slice';

Check warning on line 16 in src/apis/index.ts

View workflow job for this annotation

GitHub Actions / check-pr

'setUserToken' is defined but never used
import { toFormErrors, toLowerCamelCase } from '@/utils';
import application from './application';
import auth from './auth';
Expand All @@ -39,11 +39,22 @@

const debugLogger = createDebugLogger('apis');

const instanceP = lazyThenable(async () =>
axios.create({
baseURL: `${(await runtimeConfig).baseURL}`,
}),
);
const instanceP = lazyThenable(() => {
return runtimeConfig.then((r) => {
const token = store.getState().user.token;
// dev-only workaround: hot reload may create another instanceP with no common headers sets
const commonHeaders =
process.env.NODE_ENV === 'development' && token
? { Authorization: `Bearer ${token}` }
: {};
return axios.create({
baseURL: `${r.baseURL}`,
headers: {
common: commonHeaders,
},
});
});
});
Comment on lines 42 to 57

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Authorization header only set at creation and only in development — will drop auth in production and on token refresh.

  • In prod, no Authorization header is ever attached.
  • After login/refresh, the token in headers won’t update because it’s captured once at instance creation.

Switch to a request interceptor that reads the latest store token for every request (works in all envs) and remove the dev-only header path.

Apply:

-const instanceP = lazyThenable(() => {
-  return runtimeConfig.then((r) => {
-    const token = store.getState().user.token;
-    // dev-only workaround: hot reload may create another instanceP with no common headers sets
-    const commonHeaders =
-      process.env.NODE_ENV === 'development' && token
-        ? { Authorization: `Bearer ${token}` }
-        : {};
-    return axios.create({
-      baseURL: `${r.baseURL}`,
-      headers: {
-        common: commonHeaders,
-      },
-    });
-  });
-});
+const instanceP = lazyThenable(() => {
+  return runtimeConfig.then((r) => {
+    const inst = axios.create({
+      baseURL: r.baseURL,
+      // optional but recommended
+      timeout: 30000,
+    });
+    inst.interceptors.request.use((req) => {
+      const t = store.getState().user.token;
+      if (t) {
+        req.headers = { ...(req.headers || {}), Authorization: `Bearer ${t}` };
+      }
+      return req;
+    });
+    return inst;
+  });
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const instanceP = lazyThenable(() => {
return runtimeConfig.then((r) => {
const token = store.getState().user.token;
// dev-only workaround: hot reload may create another instanceP with no common headers sets
const commonHeaders =
process.env.NODE_ENV === 'development' && token
? { Authorization: `Bearer ${token}` }
: {};
return axios.create({
baseURL: `${r.baseURL}`,
headers: {
common: commonHeaders,
},
});
});
});
const instanceP = lazyThenable(() => {
return runtimeConfig.then((r) => {
const inst = axios.create({
baseURL: r.baseURL,
// optional but recommended
timeout: 30000,
});
inst.interceptors.request.use((req) => {
const t = store.getState().user.token;
if (t) {
req.headers = { ...(req.headers || {}), Authorization: `Bearer ${t}` };
}
return req;
});
return inst;
});
});
🤖 Prompt for AI Agents
In src/apis/index.ts around lines 42–57 the axios instance sets an Authorization
header only at creation time and only in development, so production requests and
subsequent token updates lose auth; remove the dev-only common header logic and
instead register a request interceptor on the created axios instance that, for
every outgoing request, reads the latest token from store.getState().user.token
and sets request.headers.Authorization = `Bearer ${token}` when present (or
deletes the header when absent); add the interceptor before returning the
instance from lazyThenable so every request uses the current token in all
environments.


let languageInterceptor: number | null = null;

Expand Down Expand Up @@ -205,7 +216,7 @@
data: error.response.data,
default: () => {
// 清理 token
store.dispatch(setUserToken({ token: '' }));
// store.dispatch(setUserToken({ token: '' }));
},
};
throw result;
Expand Down
4 changes: 2 additions & 2 deletions src/components/admin/AdminSiteSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import { APISiteSetting } from '@/apis/siteSetting';
import { FC } from '@/interfaces';
import { toLowerCamelCase } from '@/utils';
import { Form } from '@/components/Form';
import { FormItem } from '@/components/FormItem';
import { Form } from '@/components/shared-form/Form';
import { FormItem } from '@/components/shared-form/FormItem';

function textareaToArray(textarea: string): string[] {
return textarea.trim() === ''
Expand Down Expand Up @@ -111,7 +111,7 @@
.finally(() => {
setLoading(false);
});
}, []);

Check warning on line 114 in src/components/admin/AdminSiteSetting.tsx

View workflow job for this annotation

GitHub Actions / check-pr

React Hook useEffect has a missing dependency: 'form'. Either include it or remove the dependency array

return loading ? (
<Spin />
Expand Down
6 changes: 3 additions & 3 deletions src/components/admin/AdminUserList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
TablePaginationConfig,
Form as AntdForm,
} from 'antd';
import type { FilterValue, SorterResult } from 'antd/es/table/interface';

Check warning on line 16 in src/components/admin/AdminUserList.tsx

View workflow job for this annotation

GitHub Actions / check-pr

'SorterResult' is defined but never used
import { api } from '@/apis';
import { toLowerCamelCase } from '@/utils';
import { APIUser } from '@/apis/user';
import { FormItem } from '@/components/FormItem';
import { Form } from '@/components/Form';
import { EmailInput } from '@/components/EmailInput';
import { FormItem } from '@/components/shared-form/FormItem';
import { Form } from '@/components/shared-form/Form';
import { EmailInput } from '@/components/shared-form/EmailInput';
import { EMAIL_REGEX, USER_NAME_REGEX } from '@/utils/regex';

interface TableParams {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import React from 'react';
import { useIntl } from 'react-intl';
import { useDispatch, useSelector } from 'react-redux';
import { matchPath, useHistory, useLocation } from 'react-router-dom';
import { Avatar, Dropdown, Icon, ListItem, TeamList, Tooltip } from '.';
import { Avatar, Dropdown, Icon, ListItem, TeamList, Tooltip } from '..';
import { FC } from '@/interfaces';
import { AppState } from '@/store';
import { resetProjectsState } from '@/store/project/slice';
import { setUserToken, UserState } from '@/store/user/slice';
import style from '../style';
import style from '../../style';
import { clickEffect } from '@/utils/style';
import { routes } from '@/pages/routes';

Expand Down
123 changes: 58 additions & 65 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,60 @@
export { Icon } from './icon';
export { ApplicationList } from './ApplicationList';
export { AuthFormWrapper } from './AuthFormWrapper';
export { AuthLoginedTip } from './AuthLoginedTip';
export { Avatar } from './Avatar';
export { Button } from './Button';
export { CAPTCHAInput } from './CAPTCHAInput';
export { CAPTCHAModal } from './CAPTCHAModal';
export { Content } from './Content';
export { ContentItem } from './ContentItem';
export { ContentTitle } from './ContentTitle';
export { DashboardBox } from './DashboardBox';
export { DashboardMenu } from './DashboardMenu';
export { DebounceStatus } from './DebounceStatus';
export { Dropdown } from './Dropdown';
export { EmailInput } from './EmailInput';
export { EmailVCodeInputItem } from './EmailVCodeInputItem';
export { EmptyTip } from './EmptyTip';
export { FileCover } from './FileCover';
export { FileItem } from './FileItem';
export { FileList } from './FileList';
export { FileUploadProgress } from './FileUploadProgress';
export { Form } from './Form';
export { FormItem } from './FormItem';
export { GroupJoinForm } from './GroupJoinForm';
export { Header } from './Header';
export { ApplicationList } from './shared-member/ApplicationList';
export { AuthFormWrapper } from './shared-form/AuthFormWrapper';
export { Avatar } from './shared/Avatar';
export { Button } from './shared/Button';
export { CAPTCHAInput } from './shared-form/CAPTCHAInput';
export { CAPTCHAModal } from './shared-form/CAPTCHAModal';
export { Content } from './shared/Content';
export { ContentItem } from './shared/ContentItem';
export { ContentTitle } from './shared/ContentTitle';
export { DashboardBox } from './shared/DashboardBox';
export { DashboardMenu } from './dashboard/DashboardMenu';
export { DebounceStatus } from './shared/DebounceStatus';
export { Dropdown } from './shared/Dropdown';
export { EmailInput } from './shared-form/EmailInput';
export { EmailVCodeInputItem } from './shared-form/EmailVCodeInputItem';
export { EmptyTip } from './shared/EmptyTip';
export { FileList } from './project/FileList';
export { Form } from './shared-form/Form';
export { FormItem } from './shared-form/FormItem';
export { GroupJoinForm } from './shared-form/GroupJoinForm';
export { Header } from './shared/Header';
export { useHotKey } from './HotKey';
export { ImageOCRProgress } from './ImageOCRProgress';
export { InvitationList } from './InvitationList';
export { InviteUser } from './InviteUser';
export { Label } from './Label';
export { LanguageSelect } from './LanguageSelect';
export { List } from './List';
export { ListItem, LIST_ITEM_DEFAULT_HEIGHT } from './ListItem';
export { ListSearchInput } from './ListSearchInput';
export { ListSkeletonItem } from './ListSkeletonItem';
export { LoadingIcon } from './LoadingIcon';
export { MemberList } from './MemberList';
export { MovableArea, MovableItem } from './Movable';
export { NavTab } from './NavTab';
export { NavTabs } from './NavTabs';
export { Output } from './Output';
export { OutputList } from './OutputList';
export { ProjectItem } from './ProjectItem';
export { ProjectList } from './ProjectList';
export { ProjectSetCreateForm } from './ProjectSetCreateForm';
export { ProjectSetEditForm } from './ProjectSetEditForm';
export { ProjectSetList } from './ProjectSetList';
export { ProjectSetSettingBase } from './ProjectSetSettingBase';
export { RoleRadioGroup } from './RoleRadioGroup';
export { RoleSelect } from './RoleSelect';
export { Spin } from './Spin';
export { TabBarM } from './TabBarM';
export { TeamCreateForm } from './TeamCreateForm';
export { TeamEditForm } from './TeamEditForm';
export { TeamInsightProjectList } from './TeamInsightProjectList';
export { TeamInsightUserList } from './TeamInsightUserList';
export { TeamList } from './TeamList';
export { TeamSearchList } from './TeamSearchList';
export { TeamSettingBase } from './TeamSettingBase';
export { Tooltip } from './Tooltip';
export { TranslationProgress } from './TranslationProgress';
export { TypeRadioGroup } from './TypeRadioGroup';
export { UserEmailEditForm } from './UserEmailEditForm';
export { UserInvitationList } from './UserInvitationList';
export { UserPasswordEditForm } from './UserPasswordEditForm';
export { VCodeInput } from './VCodeInput';
export { InvitationList } from './shared-member/InvitationList';
export { InviteUser } from './shared-member/InviteUser';
export { Label } from './shared/Label';
export { LanguageSelect } from './project/LanguageSelect';
export { List } from './shared/List';
export { ListItem, LIST_ITEM_DEFAULT_HEIGHT } from './shared/ListItem';
export { ListSearchInput } from './shared/ListSearchInput';
export { ListSkeletonItem } from './shared/ListSkeletonItem';
export { LoadingIcon } from './shared/LoadingIcon';
export { MemberList } from './shared-form/MemberList';
export { MovableArea, MovableItem } from './shared/Movable';
export { NavTab } from './shared/NavTab';
export { NavTabs } from './shared/NavTabs';
export { OutputList } from './project/OutputList';
export { ProjectList } from './project-list/ProjectList';
export { ProjectSetCreateForm } from './project-set/ProjectSetCreateForm';
export { ProjectSetEditForm } from './project-set/ProjectSetEditForm';
export { ProjectSetList } from './project-set/ProjectSetList';
export { ProjectSetSettingBase } from './project-set/ProjectSetSettingBase';
export { RoleRadioGroup } from './shared-form/RoleRadioGroup';
export { RoleSelect } from './shared-form/RoleSelect';
export { Spin } from './shared/Spin';
export { TabBarM } from './shared/TabBarM';
export { TeamCreateForm } from './team/TeamCreateForm';
export { TeamEditForm } from './team/TeamEditForm';
export { TeamInsightProjectList } from './team/TeamInsightProjectList';
export { TeamInsightUserList } from './team/TeamInsightUserList';
export { TeamList } from './team/TeamList';
export { TeamSearchList } from './team/TeamSearchList';
export { TeamSettingBase } from './team/TeamSettingBase';
export { Tooltip } from './shared/Tooltip';
export { TranslationProgress } from './shared/TranslationProgress';
export { TypeRadioGroup } from './shared-form/TypeRadioGroup';
export { UserEmailEditForm } from './shared-form/UserEmailEditForm';
export { UserInvitationList } from './shared-member/UserInvitationList';
export { UserPasswordEditForm } from './shared-form/UserPasswordEditForm';
export { VCodeInput } from './shared-form/VCodeInput';
10 changes: 7 additions & 3 deletions src/components/project-file/ImageSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { css } from '@emotion/core';
import { Button } from 'antd';
import classNames from 'classnames';
import React, { useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useIntl } from 'react-intl';
import { useSelector } from 'react-redux';
import { useClickAway } from 'react-use';
import apis from '@/apis';
import { api } from '@/apis';
import { FC, File } from '@/interfaces';
import { AppState } from '@/store';
import style from '@/style';
import { toLowerCamelCase } from '@/utils';
import { clickEffect } from '@/utils/style';
import {
useMoeflowCompanion,
moeflowCompanionServiceState,
} from '@/services/moeflow_companion/use_moeflow_companion';

/** 图片文件选择下拉框的属性接口 */
interface ImageSelectProps {
Expand Down Expand Up @@ -53,7 +57,7 @@ export const ImageSelect: FC<ImageSelectProps> = ({
}) => {
setLoading(true);
if (!currentProject) return;
apis
api.file
.getProjectFiles({
projectID: currentProject.id,
params: { page, limit },
Expand Down
Loading
Loading