Ver Fonte

message fix

anhuiqiang há 1 semana atrás
pai
commit
3bb061a246

+ 3 - 0
server/src/i18n/messages.ts

@@ -35,6 +35,7 @@ export const errorMessages = {
     adminOnlyDeleteUser: '只有管理员可以删除用户',
     cannotDeleteSelf: '不能删除自己的账户',
     cannotDeleteBuiltinAdmin: '无法删除内置管理员账户',
+    invalidMemberRole: '无效的角色。只允许 USER 和 TENANT_ADMIN',
     incorrectCredentials: '用户名或密码不正确',
     incorrectCurrentPassword: '当前密码错误',
     usernameExists: '用户名已存在',
@@ -111,6 +112,7 @@ export const errorMessages = {
     adminOnlyDeleteUser: '管理者のみがユーザーを削除できます',
     cannotDeleteSelf: '自分自身のアカウントを削除できません',
     cannotDeleteBuiltinAdmin: 'ビルトイン管理者アカウントを削除できません',
+    invalidMemberRole: '無効な役割です。USER と TENANT_ADMIN のみ許可されています',
     incorrectCredentials: 'ユーザー名またはパスワードが間違っています',
     incorrectCurrentPassword: '現在のパスワードが間違っています',
     usernameExists: 'ユーザー名が既に存在します',
@@ -188,6 +190,7 @@ export const errorMessages = {
     adminOnlyDeleteUser: 'Only admins can delete users',
     cannotDeleteSelf: 'Cannot delete your own account',
     cannotDeleteBuiltinAdmin: 'Cannot delete built-in admin account',
+    invalidMemberRole: 'Invalid role. Only USER and TENANT_ADMIN are allowed',
     incorrectCredentials: 'Incorrect username or password',
     incorrectCurrentPassword: 'Incorrect current password',
     usernameExists: 'Username already exists',

+ 3 - 1
server/src/search-history/chat-message.entity.ts

@@ -32,7 +32,9 @@ export class ChatMessage {
   @CreateDateColumn({ name: 'created_at' })
   createdAt: Date;
 
-  @ManyToOne(() => SearchHistory, (history) => history.messages)
+  @ManyToOne(() => SearchHistory, (history) => history.messages, {
+    onDelete: 'CASCADE',
+  })
   @JoinColumn({ name: 'search_history_id' })
   searchHistory: SearchHistory;
 

+ 3 - 1
server/src/super-admin/super-admin.controller.ts

@@ -5,6 +5,7 @@ import { CombinedAuthGuard } from '../auth/combined-auth.guard';
 import { RolesGuard } from '../auth/roles.guard';
 import { Roles } from '../auth/roles.decorator';
 import { UserRole } from '../user/user-role.enum';
+import { I18nService } from '../i18n/i18n.service';
 
 @Controller('v1/tenants')
 @UseGuards(CombinedAuthGuard, RolesGuard)
@@ -13,6 +14,7 @@ export class SuperAdminController {
     constructor(
         private readonly superAdminService: SuperAdminService,
         private readonly tenantService: TenantService,
+        private readonly i18nService: I18nService,
     ) { }
 
     @Get()
@@ -97,7 +99,7 @@ export class SuperAdminController {
         @Body() body: { role: string },
     ) {
         if (body.role !== UserRole.USER && body.role !== UserRole.TENANT_ADMIN) {
-            throw new ForbiddenException('Invalid role. Only USER and TENANT_ADMIN are allowed.');
+            throw new ForbiddenException(this.i18nService.getErrorMessage('invalidMemberRole'));
         }
         return this.tenantService.updateMemberRole(tenantId, userId, body.role);
     }

+ 0 - 1
server/src/user/dto/create-user.dto.ts

@@ -9,7 +9,6 @@ export class CreateUserDto {
 
   @IsString()
   @IsNotEmpty()
-  @MinLength(8, { message: 'Password must be at least 8 characters long' })
   password: string;
 
   @ApiPropertyOptional()

+ 4 - 0
web/components/SettingsModal.tsx

@@ -298,6 +298,7 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({
                             onChange={e => setPasswordForm({ ...passwordForm, current: e.target.value })}
                             className="w-full px-3 py-2 text-sm border border-slate-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
                             required
+                            autoComplete="new-password"
                         />
                     </div>
                     <div>
@@ -308,6 +309,7 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({
                             onChange={e => setPasswordForm({ ...passwordForm, new: e.target.value })}
                             className="w-full px-3 py-2 text-sm border border-slate-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
                             required
+                            autoComplete="new-password"
                         />
                     </div>
                     <div>
@@ -318,6 +320,7 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({
                             onChange={e => setPasswordForm({ ...passwordForm, confirm: e.target.value })}
                             className="w-full px-3 py-2 text-sm border border-slate-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
                             required
+                            autoComplete="new-password"
                         />
                     </div>
                     {passwordSuccess && <p className="text-xs text-green-600">{passwordSuccess}</p>}
@@ -377,6 +380,7 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({
                         onChange={e => setNewUser({ ...newUser, password: e.target.value })}
                         className="w-full px-3 py-2 text-sm border border-slate-300 rounded-md"
                         required
+                        autoComplete="new-password"
                     />
                     <div className="flex justify-end gap-2">
                         <button type="button" onClick={() => setShowAddUser(false)} className="text-xs text-slate-500 hover:text-slate-700 px-2 py-1">{t('cancel')}</button>

+ 4 - 0
web/components/views/SettingsView.tsx

@@ -764,6 +764,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
                             onChange={e => setPasswordForm({ ...passwordForm, current: e.target.value })}
                             className="w-full px-3 py-2 text-sm border border-slate-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
                             required
+                            autoComplete="new-password"
                         />
                     </div>
                     <div>
@@ -774,6 +775,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
                             onChange={e => setPasswordForm({ ...passwordForm, new: e.target.value })}
                             className="w-full px-3 py-2 text-sm border border-slate-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
                             required
+                            autoComplete="new-password"
                         />
                     </div>
                     <div>
@@ -784,6 +786,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
                             onChange={e => setPasswordForm({ ...passwordForm, confirm: e.target.value })}
                             className="w-full px-3 py-2 text-sm border border-slate-300 rounded-md focus:ring-2 focus:ring-blue-500 outline-none"
                             required
+                            autoComplete="new-password"
                         />
                     </div>
                     {passwordSuccess && <p className="text-xs text-green-600">{passwordSuccess}</p>}
@@ -909,6 +912,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
                             onChange={e => setNewUser({ ...newUser, password: e.target.value })}
                             className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-medium focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50 outline-none transition-all"
                             required
+                            autoComplete="new-password"
                         />
                     </div>
                     <div className="flex items-center justify-between">

+ 13 - 39
web/src/components/layouts/WorkspaceLayout.tsx

@@ -9,10 +9,8 @@ import {
     LayoutTemplate,
     Plus,
     Menu,
-    Search,
     BookOpen,
     Library,
-    UserCircle,
     HardDrive,
     Building2,
     ChevronRight,
@@ -179,31 +177,6 @@ const WorkspaceLayout: React.FC = () => {
                         />
                     </div>
                 </nav>
-
-                {/* User Profile Footer */}
-                <div className="p-4 border-t border-slate-100/80 mt-auto">
-                    <div className="flex items-center gap-3 p-2 mb-3">
-                        <div className="flex items-center justify-center w-9 h-9 text-sm font-bold text-white bg-indigo-600 rounded-full shrink-0 shadow-sm">
-                            {user?.username?.[0]?.toUpperCase() || 'A'}
-                        </div>
-                        <div className="flex-1 min-w-0">
-                            <div className="flex items-center gap-2 mb-0.5">
-                                <p className="text-sm font-semibold text-slate-900 truncate">{user?.displayName || user?.username}</p>
-                                <span className="px-1.5 py-0.5 text-[9px] font-black bg-blue-50 text-blue-600 rounded-md border border-blue-100 uppercase tracking-tighter">
-                                    {activeTenant?.role?.replace('_', ' ') || user?.role?.replace('_', ' ') || 'USER'}
-                                </span>
-                            </div>
-                            <p className="text-[12px] text-slate-500 truncate">{activeTenant?.tenant?.name || t('defaultTenant')}</p>
-                        </div>
-                    </div>
-                    <button
-                        onClick={logout}
-                        className="flex items-center gap-2 px-3 py-2 text-[13px] font-medium text-red-500 hover:bg-red-50 w-full rounded-lg transition-colors"
-                    >
-                        <LogOut size={14} />
-                        {t('logout')}
-                    </button>
-                </div>
             </aside>
 
             {/* Main Content Area */}
@@ -291,20 +264,21 @@ const WorkspaceLayout: React.FC = () => {
                                 )}
                             </AnimatePresence>
                         </div>
-
-                        <div className="relative w-full max-w-lg ml-4">
-                            <Search className="absolute text-slate-400 left-4 top-1/2 -translate-y-1/2" size={18} />
-                            <input
-                                type="text"
-                                placeholder={t('searchPlaceholder')}
-                                className="w-full h-11 pl-12 pr-4 bg-slate-100/70 border-none rounded-full focus:bg-white focus:ring-2 focus:ring-blue-500/10 focus:shadow-inner outline-none transition-all text-sm font-medium"
-                            />
-                        </div>
                     </div>
-                    <div className="flex items-center gap-4">
-                        <div className="w-10 h-10 rounded-full bg-slate-50 border border-slate-200/50 flex items-center justify-center text-slate-400 shadow-inner">
-                            <UserCircle size={24} />
+                    <div className="flex items-center gap-2">
+                        <div className="flex items-center gap-2 px-3 py-1.5 bg-slate-50 rounded-lg border border-slate-200/50">
+                            <div className="flex items-center justify-center w-8 h-8 text-sm font-bold text-white bg-indigo-600 rounded-full shrink-0 shadow-sm">
+                                {user?.username?.[0]?.toUpperCase() || 'A'}
+                            </div>
+                            <p className="text-sm font-semibold text-slate-900 truncate">{user?.displayName || user?.username}</p>
                         </div>
+                        <button
+                            onClick={logout}
+                            className="p-2 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded-lg transition-colors"
+                            title={t('logout')}
+                        >
+                            <LogOut size={18} />
+                        </button>
                     </div>
                 </header>