@mdguggenbichler/slugbase-core 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/backend/dist/app-factory.d.ts +1 -1
- package/backend/dist/app-factory.d.ts.map +1 -1
- package/backend/dist/app-factory.js +2 -13
- package/backend/dist/app-factory.js.map +1 -1
- package/backend/dist/index.js +1 -1
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/routes/admin/settings.d.ts.map +1 -1
- package/backend/dist/routes/admin/settings.js +0 -281
- package/backend/dist/routes/admin/settings.js.map +1 -1
- package/backend/dist/routes/admin/stats.d.ts.map +1 -1
- package/backend/dist/routes/admin/stats.js +0 -39
- package/backend/dist/routes/admin/stats.js.map +1 -1
- package/backend/dist/routes/admin/teams.d.ts.map +1 -1
- package/backend/dist/routes/admin/teams.js +0 -344
- package/backend/dist/routes/admin/teams.js.map +1 -1
- package/backend/dist/routes/admin/users.d.ts.map +1 -1
- package/backend/dist/routes/admin/users.js +0 -317
- package/backend/dist/routes/admin/users.js.map +1 -1
- package/backend/dist/routes/auth.d.ts.map +1 -1
- package/backend/dist/routes/auth.js +0 -284
- package/backend/dist/routes/auth.js.map +1 -1
- package/backend/dist/routes/bookmarks.d.ts.map +1 -1
- package/backend/dist/routes/bookmarks.js +4 -525
- package/backend/dist/routes/bookmarks.js.map +1 -1
- package/backend/dist/routes/config.d.ts.map +1 -1
- package/backend/dist/routes/config.js +4 -29
- package/backend/dist/routes/config.js.map +1 -1
- package/backend/dist/routes/csrf.d.ts.map +1 -1
- package/backend/dist/routes/csrf.js +0 -24
- package/backend/dist/routes/csrf.js.map +1 -1
- package/backend/dist/routes/dashboard.d.ts.map +1 -1
- package/backend/dist/routes/dashboard.js +0 -35
- package/backend/dist/routes/dashboard.js.map +1 -1
- package/backend/dist/routes/email-verification.d.ts.map +1 -1
- package/backend/dist/routes/email-verification.js +0 -53
- package/backend/dist/routes/email-verification.js.map +1 -1
- package/backend/dist/routes/folders.d.ts.map +1 -1
- package/backend/dist/routes/folders.js +0 -217
- package/backend/dist/routes/folders.js.map +1 -1
- package/backend/dist/routes/go.d.ts.map +1 -1
- package/backend/dist/routes/go.js +0 -95
- package/backend/dist/routes/go.js.map +1 -1
- package/backend/dist/routes/health.d.ts.map +1 -1
- package/backend/dist/routes/health.js +0 -45
- package/backend/dist/routes/health.js.map +1 -1
- package/backend/dist/routes/oidc-providers.d.ts.map +1 -1
- package/backend/dist/routes/oidc-providers.js +0 -221
- package/backend/dist/routes/oidc-providers.js.map +1 -1
- package/backend/dist/routes/password-reset.d.ts.map +1 -1
- package/backend/dist/routes/password-reset.js +0 -67
- package/backend/dist/routes/password-reset.js.map +1 -1
- package/backend/dist/routes/tags.d.ts.map +1 -1
- package/backend/dist/routes/tags.js +0 -174
- package/backend/dist/routes/tags.js.map +1 -1
- package/backend/dist/routes/teams.d.ts.map +1 -1
- package/backend/dist/routes/teams.js +0 -36
- package/backend/dist/routes/teams.js.map +1 -1
- package/backend/dist/routes/tokens.d.ts.map +1 -1
- package/backend/dist/routes/tokens.js +0 -108
- package/backend/dist/routes/tokens.js.map +1 -1
- package/backend/dist/routes/users.d.ts.map +1 -1
- package/backend/dist/routes/users.js +0 -81
- package/backend/dist/routes/users.js.map +1 -1
- package/backend/dist/utils/ai-feature.d.ts +9 -7
- package/backend/dist/utils/ai-feature.d.ts.map +1 -1
- package/backend/dist/utils/ai-feature.js +29 -13
- package/backend/dist/utils/ai-feature.js.map +1 -1
- package/frontend/src/App.tsx +4 -2
- package/frontend/src/components/admin/AdminAI.tsx +68 -58
- package/frontend/src/contexts/AppConfigContext.tsx +7 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react';
|
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import api from '../../api/client';
|
|
4
4
|
import { useToast } from '../ui/Toast';
|
|
5
|
+
import { useAppConfig } from '../../contexts/AppConfigContext';
|
|
5
6
|
import { Save, Sparkles } from 'lucide-react';
|
|
6
7
|
import Button from '../ui/Button';
|
|
7
8
|
import { PageLoadingSkeleton } from '../ui/PageLoadingSkeleton';
|
|
@@ -17,6 +18,7 @@ const PROVIDER_OPTIONS = [
|
|
|
17
18
|
export default function AdminAI() {
|
|
18
19
|
const { t } = useTranslation();
|
|
19
20
|
const { showToast } = useToast();
|
|
21
|
+
const { adminAiOnlyToggle } = useAppConfig();
|
|
20
22
|
const [loading, setLoading] = useState(true);
|
|
21
23
|
const [saving, setSaving] = useState(false);
|
|
22
24
|
const [settings, setSettings] = useState({
|
|
@@ -34,10 +36,10 @@ export default function AdminAI() {
|
|
|
34
36
|
const res = await api.get('/admin/settings/ai');
|
|
35
37
|
setSettings({
|
|
36
38
|
ai_enabled: res.data.ai_enabled ?? false,
|
|
37
|
-
ai_provider: res.data.ai_provider || 'openai',
|
|
38
|
-
ai_model: res.data.ai_model || 'gpt-4o-mini',
|
|
39
|
+
ai_provider: adminAiOnlyToggle ? 'openai' : (res.data.ai_provider || 'openai'),
|
|
40
|
+
ai_model: adminAiOnlyToggle ? 'gpt-4o-mini' : (res.data.ai_model || 'gpt-4o-mini'),
|
|
39
41
|
ai_api_key: '',
|
|
40
|
-
ai_api_key_set: res.data.ai_api_key_set ?? false,
|
|
42
|
+
ai_api_key_set: adminAiOnlyToggle ? false : (res.data.ai_api_key_set ?? false),
|
|
41
43
|
});
|
|
42
44
|
} catch (err: unknown) {
|
|
43
45
|
const e = err as { response?: { status?: number; data?: { error?: string } } };
|
|
@@ -61,7 +63,7 @@ export default function AdminAI() {
|
|
|
61
63
|
} finally {
|
|
62
64
|
setLoading(false);
|
|
63
65
|
}
|
|
64
|
-
}, [showToast, t]);
|
|
66
|
+
}, [showToast, t, adminAiOnlyToggle]);
|
|
65
67
|
|
|
66
68
|
useEffect(() => {
|
|
67
69
|
loadSettings();
|
|
@@ -82,12 +84,13 @@ export default function AdminAI() {
|
|
|
82
84
|
}, [showToast, t]);
|
|
83
85
|
|
|
84
86
|
useEffect(() => {
|
|
87
|
+
if (adminAiOnlyToggle) return;
|
|
85
88
|
if (settings.ai_api_key_set && !loading) {
|
|
86
89
|
loadModels();
|
|
87
90
|
} else {
|
|
88
91
|
setModels([]);
|
|
89
92
|
}
|
|
90
|
-
}, [settings.ai_api_key_set, loading, loadModels]);
|
|
93
|
+
}, [adminAiOnlyToggle, settings.ai_api_key_set, loading, loadModels]);
|
|
91
94
|
|
|
92
95
|
const providerOptions = PROVIDER_OPTIONS.map((p) => ({
|
|
93
96
|
value: p.value,
|
|
@@ -105,12 +108,15 @@ export default function AdminAI() {
|
|
|
105
108
|
e.preventDefault();
|
|
106
109
|
setSaving(true);
|
|
107
110
|
try {
|
|
108
|
-
|
|
109
|
-
ai_enabled: settings.ai_enabled
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
const payload = adminAiOnlyToggle
|
|
112
|
+
? { ai_enabled: settings.ai_enabled }
|
|
113
|
+
: {
|
|
114
|
+
ai_enabled: settings.ai_enabled,
|
|
115
|
+
ai_provider: settings.ai_provider,
|
|
116
|
+
ai_model: settings.ai_model,
|
|
117
|
+
ai_api_key: settings.ai_api_key || undefined,
|
|
118
|
+
};
|
|
119
|
+
await api.post('/admin/settings/ai', payload);
|
|
114
120
|
showToast(t('common.success'), 'success');
|
|
115
121
|
await loadSettings();
|
|
116
122
|
} catch (err: unknown) {
|
|
@@ -154,53 +160,57 @@ export default function AdminAI() {
|
|
|
154
160
|
</Label>
|
|
155
161
|
</div>
|
|
156
162
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
163
|
+
{!adminAiOnlyToggle && (
|
|
164
|
+
<>
|
|
165
|
+
<div>
|
|
166
|
+
<Label className="block text-sm font-semibold text-gray-900 dark:text-white mb-2">
|
|
167
|
+
{t('admin.ai.provider')}
|
|
168
|
+
</Label>
|
|
169
|
+
<Select
|
|
170
|
+
value={settings.ai_provider}
|
|
171
|
+
onChange={(value) => setSettings({ ...settings, ai_provider: value })}
|
|
172
|
+
options={providerOptions}
|
|
173
|
+
className="max-w-xs"
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div>
|
|
178
|
+
<Label className="block text-sm font-semibold text-gray-900 dark:text-white mb-2">
|
|
179
|
+
{t('admin.ai.apiKey')}
|
|
180
|
+
</Label>
|
|
181
|
+
<Input
|
|
182
|
+
type="password"
|
|
183
|
+
value={settings.ai_api_key}
|
|
184
|
+
onChange={(e) => setSettings({ ...settings, ai_api_key: e.target.value })}
|
|
185
|
+
placeholder={settings.ai_api_key_set ? t('admin.ai.apiKeyPlaceholder') : 'sk-...'}
|
|
186
|
+
className="max-w-md font-mono text-sm"
|
|
187
|
+
/>
|
|
188
|
+
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
189
|
+
{settings.ai_api_key_set ? t('admin.ai.apiKeyChangeHint') : t('admin.ai.apiKeyHint')}
|
|
190
|
+
</p>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<div>
|
|
194
|
+
<Label className="block text-sm font-semibold text-gray-900 dark:text-white mb-2">
|
|
195
|
+
{t('admin.ai.model')}
|
|
196
|
+
</Label>
|
|
197
|
+
<Select
|
|
198
|
+
value={settings.ai_model}
|
|
199
|
+
onChange={(value) => setSettings({ ...settings, ai_model: value })}
|
|
200
|
+
options={modelOptionsWithCurrent}
|
|
201
|
+
placeholder={
|
|
202
|
+
!settings.ai_api_key_set
|
|
203
|
+
? t('admin.ai.modelPlaceholderNoKey')
|
|
204
|
+
: modelsLoading
|
|
205
|
+
? t('admin.ai.modelPlaceholderLoading')
|
|
206
|
+
: undefined
|
|
207
|
+
}
|
|
208
|
+
disabled={!settings.ai_api_key_set || modelsLoading}
|
|
209
|
+
className="max-w-xs"
|
|
210
|
+
/>
|
|
211
|
+
</div>
|
|
212
|
+
</>
|
|
213
|
+
)}
|
|
204
214
|
|
|
205
215
|
<div className="pt-2">
|
|
206
216
|
<Button type="submit" variant="primary" disabled={saving}>
|
|
@@ -11,6 +11,8 @@ export interface AppConfig {
|
|
|
11
11
|
pathPrefixForLinks: string;
|
|
12
12
|
/** When true, hide Admin OIDC and SMTP/Settings (e.g. cloud uses Postmark and global OIDC). */
|
|
13
13
|
hideAdminOidcAndSmtp?: boolean;
|
|
14
|
+
/** When true, Admin AI page shows only the enable/disable toggle (e.g. cloud uses env for provider/model/key). */
|
|
15
|
+
adminAiOnlyToggle?: boolean;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
const defaultConfig: AppConfig = {
|
|
@@ -19,6 +21,7 @@ const defaultConfig: AppConfig = {
|
|
|
19
21
|
appRootPath: defaultAppRootPath,
|
|
20
22
|
pathPrefixForLinks: defaultAppBasePath,
|
|
21
23
|
hideAdminOidcAndSmtp: false,
|
|
24
|
+
adminAiOnlyToggle: false,
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
const AppConfigContext = createContext<AppConfig>(defaultConfig);
|
|
@@ -31,6 +34,7 @@ export function AppConfigProvider({
|
|
|
31
34
|
skipSetupFlow,
|
|
32
35
|
pathPrefixForLinks,
|
|
33
36
|
hideAdminOidcAndSmtp,
|
|
37
|
+
adminAiOnlyToggle,
|
|
34
38
|
}: {
|
|
35
39
|
children: React.ReactNode;
|
|
36
40
|
appBasePath?: string;
|
|
@@ -41,6 +45,8 @@ export function AppConfigProvider({
|
|
|
41
45
|
pathPrefixForLinks?: string;
|
|
42
46
|
/** When true, hide Admin OIDC and SMTP/Settings (e.g. cloud). */
|
|
43
47
|
hideAdminOidcAndSmtp?: boolean;
|
|
48
|
+
/** When true, Admin AI page shows only the enable/disable toggle (e.g. cloud). */
|
|
49
|
+
adminAiOnlyToggle?: boolean;
|
|
44
50
|
}) {
|
|
45
51
|
const base = appBasePath ?? defaultConfig.appBasePath;
|
|
46
52
|
const value: AppConfig = {
|
|
@@ -50,6 +56,7 @@ export function AppConfigProvider({
|
|
|
50
56
|
skipSetupFlow,
|
|
51
57
|
pathPrefixForLinks: pathPrefixForLinks !== undefined ? pathPrefixForLinks : base,
|
|
52
58
|
hideAdminOidcAndSmtp: hideAdminOidcAndSmtp ?? defaultConfig.hideAdminOidcAndSmtp,
|
|
59
|
+
adminAiOnlyToggle: adminAiOnlyToggle ?? defaultConfig.adminAiOnlyToggle,
|
|
53
60
|
};
|
|
54
61
|
return <AppConfigContext.Provider value={value}>{children}</AppConfigContext.Provider>;
|
|
55
62
|
}
|