Skip to content

Commit f578a13

Browse files
feat: prevent modification of environment-defined settings
- Add IsEnvironmentDefined field to AIProvider and LoginProfile GraphQL types - Mark all environment-configured AI providers and database profiles as immutable - Prevent deletion of environment-defined AI providers in chat interface - Prevent removal of environment-defined database profiles in sidebar - Add visual indicators (🔒) for environment-defined profiles - Show user-friendly error messages when attempting to modify protected settings - Maintain backward compatibility with user-defined settings Fixes #498 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Anguel <modelorona@users.noreply.github.com>
1 parent d59f602 commit f578a13

File tree

11 files changed

+38
-17
lines changed

11 files changed

+38
-17
lines changed

core/graph/model/models_gen.go

Lines changed: 8 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/graph/schema.graphqls

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ type LoginProfile {
107107
Id: String!
108108
Type: DatabaseType!
109109
Database: String
110+
IsEnvironmentDefined: Boolean!
110111
}
111112

112113
type StatusResponse {
@@ -129,6 +130,7 @@ type AIChatMessage {
129130
type AIProvider {
130131
Type: String!
131132
ProviderId: String!
133+
IsEnvironmentDefined: Boolean!
132134
}
133135

134136
type Query {

core/graph/schema.resolvers.go

Lines changed: 7 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/components/sidebar/get-login-profiles.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ query GetProfiles {
44
Id
55
Type
66
Database
7+
IsEnvironmentDefined
78
}
89
}

frontend/src/components/sidebar/sidebar.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,34 +133,35 @@ export const SideMenu: FC<IRouteProps> = (props) => {
133133
function getDropdownLoginProfileItem(profile: LocalLoginProfile): IDropdownItem {
134134
const icon = (Icons.Logos as Record<string, ReactElement>)[profile.Type];
135135
const info = <ProfileInfoTooltip profile={profile} />;
136+
const envLabel = profile.IsEnvironmentDefined ? " 🔒" : "";
136137

137138
if (profile.Saved) {
138139
return {
139140
id: profile.Id,
140-
label: profile.Id,
141+
label: profile.Id + envLabel,
141142
icon,
142143
info,
143144
}
144145
}
145146
if (profile.Type === DatabaseType.MongoDb) {
146147
return {
147148
id: profile.Id,
148-
label: `${profile.Hostname} - ${profile.Username} [${profile.Type}]`,
149+
label: `${profile.Hostname} - ${profile.Username} [${profile.Type}]${envLabel}`,
149150
icon,
150151
info,
151152
}
152153
}
153154
if (profile.Type === DatabaseType.Sqlite3) {
154155
return {
155156
id: profile.Id,
156-
label: `${profile.Database} [${profile.Type}]`,
157+
label: `${profile.Database} [${profile.Type}]${envLabel}`,
157158
icon,
158159
info,
159160
}
160161
}
161162
return {
162163
id: profile.Id,
163-
label: `${profile.Hostname} - ${profile.Database} [${profile.Type}]`,
164+
label: `${profile.Hostname} - ${profile.Database} [${profile.Type}]${envLabel}`,
164165
icon,
165166
info,
166167
};
@@ -339,6 +340,10 @@ export const Sidebar: FC = () => {
339340
if (selectedProfile == null) {
340341
return;
341342
}
343+
if (selectedProfile.IsEnvironmentDefined) {
344+
notify("Cannot remove environment-defined profiles", "error");
345+
return;
346+
}
342347
if (selectedProfile.Id === current?.Id) {
343348
return navigate(InternalRoutes.Logout.path);
344349
}

frontend/src/generated/graphql.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export type AiChatMessage = {
4242

4343
export type AiProvider = {
4444
__typename?: 'AIProvider';
45+
IsEnvironmentDefined: Scalars['Boolean']['output'];
4546
ProviderId: Scalars['String']['output'];
4647
Type: Scalars['String']['output'];
4748
};
@@ -112,6 +113,7 @@ export type LoginProfile = {
112113
Alias?: Maybe<Scalars['String']['output']>;
113114
Database?: Maybe<Scalars['String']['output']>;
114115
Id: Scalars['String']['output'];
116+
IsEnvironmentDefined: Scalars['Boolean']['output'];
115117
Type: DatabaseType;
116118
};
117119

frontend/src/pages/auth/login.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ export const LoginPage: FC = () => {
179179
Password: "",
180180
Username: "",
181181
Saved: true,
182+
IsEnvironmentDefined: profile?.IsEnvironmentDefined ?? false,
182183
}));
183184
navigate(InternalRoutes.Dashboard.StorageUnit.path);
184185
return notify("Login successfully", "success");

frontend/src/pages/chat/chat.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ export const ChatPage: FC = () => {
319319
...newProviders.map(provider => ({
320320
id: provider.ProviderId,
321321
modelType: provider.Type,
322+
isEnvironmentDefined: provider.IsEnvironmentDefined,
322323
})),
323324
...initialModelTypes
324325
];
@@ -436,7 +437,10 @@ export const ChatPage: FC = () => {
436437
})}</div>}
437438
defaultItem={{ label: "Add External Model", icon: Icons.Add }}
438439
onDefaultItemClick={handleAddExternalModel}
439-
enableAction={(index) => modelTypes.at(index)?.token != null} />
440+
enableAction={(index) => {
441+
const modelType = modelTypes.at(index);
442+
return modelType?.token != null && !modelType?.isEnvironmentDefined;
443+
}} />
440444
{
441445
modelAvailable
442446
? <Dropdown className="w-[200px]" value={currentModel ? createDropdownItem(currentModel) : undefined}

frontend/src/pages/chat/get-ai-providers.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ query GetAIProviders {
22
AIProviders {
33
Type
44
ProviderId
5+
IsEnvironmentDefined
56
}
67
}

frontend/src/store/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit';
1818
import { v4 } from 'uuid';
1919
import { LoginCredentials } from '../generated/graphql';
2020

21-
export type LocalLoginProfile = (LoginCredentials & {Id: string, Saved?: boolean});
21+
export type LocalLoginProfile = (LoginCredentials & {Id: string, Saved?: boolean, IsEnvironmentDefined?: boolean});
2222

2323
export type IAuthState = {
2424
status: "logged-in" | "unauthorized";

0 commit comments

Comments
 (0)