@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.
Files changed (71) hide show
  1. package/backend/dist/app-factory.d.ts +1 -1
  2. package/backend/dist/app-factory.d.ts.map +1 -1
  3. package/backend/dist/app-factory.js +2 -13
  4. package/backend/dist/app-factory.js.map +1 -1
  5. package/backend/dist/index.js +1 -1
  6. package/backend/dist/index.js.map +1 -1
  7. package/backend/dist/routes/admin/settings.d.ts.map +1 -1
  8. package/backend/dist/routes/admin/settings.js +0 -281
  9. package/backend/dist/routes/admin/settings.js.map +1 -1
  10. package/backend/dist/routes/admin/stats.d.ts.map +1 -1
  11. package/backend/dist/routes/admin/stats.js +0 -39
  12. package/backend/dist/routes/admin/stats.js.map +1 -1
  13. package/backend/dist/routes/admin/teams.d.ts.map +1 -1
  14. package/backend/dist/routes/admin/teams.js +0 -344
  15. package/backend/dist/routes/admin/teams.js.map +1 -1
  16. package/backend/dist/routes/admin/users.d.ts.map +1 -1
  17. package/backend/dist/routes/admin/users.js +0 -317
  18. package/backend/dist/routes/admin/users.js.map +1 -1
  19. package/backend/dist/routes/auth.d.ts.map +1 -1
  20. package/backend/dist/routes/auth.js +0 -284
  21. package/backend/dist/routes/auth.js.map +1 -1
  22. package/backend/dist/routes/bookmarks.d.ts.map +1 -1
  23. package/backend/dist/routes/bookmarks.js +4 -525
  24. package/backend/dist/routes/bookmarks.js.map +1 -1
  25. package/backend/dist/routes/config.d.ts.map +1 -1
  26. package/backend/dist/routes/config.js +4 -29
  27. package/backend/dist/routes/config.js.map +1 -1
  28. package/backend/dist/routes/csrf.d.ts.map +1 -1
  29. package/backend/dist/routes/csrf.js +0 -24
  30. package/backend/dist/routes/csrf.js.map +1 -1
  31. package/backend/dist/routes/dashboard.d.ts.map +1 -1
  32. package/backend/dist/routes/dashboard.js +0 -35
  33. package/backend/dist/routes/dashboard.js.map +1 -1
  34. package/backend/dist/routes/email-verification.d.ts.map +1 -1
  35. package/backend/dist/routes/email-verification.js +0 -53
  36. package/backend/dist/routes/email-verification.js.map +1 -1
  37. package/backend/dist/routes/folders.d.ts.map +1 -1
  38. package/backend/dist/routes/folders.js +0 -217
  39. package/backend/dist/routes/folders.js.map +1 -1
  40. package/backend/dist/routes/go.d.ts.map +1 -1
  41. package/backend/dist/routes/go.js +0 -95
  42. package/backend/dist/routes/go.js.map +1 -1
  43. package/backend/dist/routes/health.d.ts.map +1 -1
  44. package/backend/dist/routes/health.js +0 -45
  45. package/backend/dist/routes/health.js.map +1 -1
  46. package/backend/dist/routes/oidc-providers.d.ts.map +1 -1
  47. package/backend/dist/routes/oidc-providers.js +0 -221
  48. package/backend/dist/routes/oidc-providers.js.map +1 -1
  49. package/backend/dist/routes/password-reset.d.ts.map +1 -1
  50. package/backend/dist/routes/password-reset.js +0 -67
  51. package/backend/dist/routes/password-reset.js.map +1 -1
  52. package/backend/dist/routes/tags.d.ts.map +1 -1
  53. package/backend/dist/routes/tags.js +0 -174
  54. package/backend/dist/routes/tags.js.map +1 -1
  55. package/backend/dist/routes/teams.d.ts.map +1 -1
  56. package/backend/dist/routes/teams.js +0 -36
  57. package/backend/dist/routes/teams.js.map +1 -1
  58. package/backend/dist/routes/tokens.d.ts.map +1 -1
  59. package/backend/dist/routes/tokens.js +0 -108
  60. package/backend/dist/routes/tokens.js.map +1 -1
  61. package/backend/dist/routes/users.d.ts.map +1 -1
  62. package/backend/dist/routes/users.js +0 -81
  63. package/backend/dist/routes/users.js.map +1 -1
  64. package/backend/dist/utils/ai-feature.d.ts +9 -7
  65. package/backend/dist/utils/ai-feature.d.ts.map +1 -1
  66. package/backend/dist/utils/ai-feature.js +29 -13
  67. package/backend/dist/utils/ai-feature.js.map +1 -1
  68. package/frontend/src/App.tsx +4 -2
  69. package/frontend/src/components/admin/AdminAI.tsx +68 -58
  70. package/frontend/src/contexts/AppConfigContext.tsx +7 -0
  71. 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
- await api.post('/admin/settings/ai', {
109
- ai_enabled: settings.ai_enabled,
110
- ai_provider: settings.ai_provider,
111
- ai_model: settings.ai_model,
112
- ai_api_key: settings.ai_api_key || undefined,
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
- <div>
158
- <Label className="block text-sm font-semibold text-gray-900 dark:text-white mb-2">
159
- {t('admin.ai.provider')}
160
- </Label>
161
- <Select
162
- value={settings.ai_provider}
163
- onChange={(value) => setSettings({ ...settings, ai_provider: value })}
164
- options={providerOptions}
165
- className="max-w-xs"
166
- />
167
- </div>
168
-
169
- <div>
170
- <Label className="block text-sm font-semibold text-gray-900 dark:text-white mb-2">
171
- {t('admin.ai.apiKey')}
172
- </Label>
173
- <Input
174
- type="password"
175
- value={settings.ai_api_key}
176
- onChange={(e) => setSettings({ ...settings, ai_api_key: e.target.value })}
177
- placeholder={settings.ai_api_key_set ? t('admin.ai.apiKeyPlaceholder') : 'sk-...'}
178
- className="max-w-md font-mono text-sm"
179
- />
180
- <p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
181
- {settings.ai_api_key_set ? t('admin.ai.apiKeyChangeHint') : t('admin.ai.apiKeyHint')}
182
- </p>
183
- </div>
184
-
185
- <div>
186
- <Label className="block text-sm font-semibold text-gray-900 dark:text-white mb-2">
187
- {t('admin.ai.model')}
188
- </Label>
189
- <Select
190
- value={settings.ai_model}
191
- onChange={(value) => setSettings({ ...settings, ai_model: value })}
192
- options={modelOptionsWithCurrent}
193
- placeholder={
194
- !settings.ai_api_key_set
195
- ? t('admin.ai.modelPlaceholderNoKey')
196
- : modelsLoading
197
- ? t('admin.ai.modelPlaceholderLoading')
198
- : undefined
199
- }
200
- disabled={!settings.ai_api_key_set || modelsLoading}
201
- className="max-w-xs"
202
- />
203
- </div>
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdguggenbichler/slugbase-core",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "SlugBase core: backend and frontend entrypoints for self-hosted and cloud apps",
5
5
  "type": "module",
6
6
  "exports": {