insforge 1.3.0 → 1.4.8
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/CHANGELOG.md +2 -0
- package/auth/package.json +5 -3
- package/auth/src/lib/broadcastService.ts +115 -117
- package/auth/src/lib/insforge.ts +8 -0
- package/auth/src/main.tsx +2 -4
- package/auth/src/pages/SignInPage.tsx +60 -60
- package/auth/src/pages/SignUpPage.tsx +60 -60
- package/auth/src/pages/VerifyEmailPage.tsx +18 -0
- package/auth/tsconfig.json +2 -1
- package/backend/package.json +10 -6
- package/backend/src/api/middlewares/rate-limiters.ts +127 -127
- package/backend/src/api/routes/ai/index.routes.ts +475 -468
- package/backend/src/api/routes/auth/index.routes.ts +85 -32
- package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
- package/backend/src/api/routes/database/index.routes.ts +2 -0
- package/backend/src/api/routes/database/records.routes.ts +39 -175
- package/backend/src/api/routes/database/rpc.routes.ts +69 -0
- package/backend/src/api/routes/deployments/index.routes.ts +192 -0
- package/backend/src/api/routes/docs/index.routes.ts +3 -2
- package/backend/src/api/routes/email/index.routes.ts +35 -35
- package/backend/src/api/routes/functions/index.routes.ts +3 -3
- package/backend/src/api/routes/metadata/index.routes.ts +26 -0
- package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
- package/backend/src/infra/database/database.manager.ts +0 -10
- package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
- package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
- package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
- package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
- package/backend/src/infra/security/token.manager.ts +1 -4
- package/backend/src/providers/ai/openrouter.provider.ts +12 -3
- package/backend/src/providers/database/base.provider.ts +39 -0
- package/backend/src/providers/database/cloud.provider.ts +159 -0
- package/backend/src/providers/deployments/vercel.provider.ts +516 -0
- package/backend/src/server.ts +19 -7
- package/backend/src/services/ai/ai-config.service.ts +6 -6
- package/backend/src/services/ai/ai-model.service.ts +60 -60
- package/backend/src/services/ai/ai-usage.service.ts +7 -7
- package/backend/src/services/ai/chat-completion.service.ts +415 -220
- package/backend/src/services/ai/helpers.ts +64 -64
- package/backend/src/services/ai/index.ts +13 -13
- package/backend/src/services/auth/auth-config.service.ts +4 -4
- package/backend/src/services/auth/auth-otp.service.ts +6 -6
- package/backend/src/services/auth/auth.service.ts +134 -74
- package/backend/src/services/auth/index.ts +4 -4
- package/backend/src/services/auth/oauth-config.service.ts +12 -12
- package/backend/src/services/database/database-advance.service.ts +19 -55
- package/backend/src/services/database/database-table.service.ts +38 -85
- package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
- package/backend/src/services/deployments/deployment.service.ts +693 -0
- package/backend/src/services/functions/function.service.ts +61 -41
- package/backend/src/services/logs/audit.service.ts +10 -10
- package/backend/src/services/secrets/secret.service.ts +101 -27
- package/backend/src/services/storage/storage.service.ts +30 -30
- package/backend/src/services/usage/usage.service.ts +6 -6
- package/backend/src/types/ai.ts +8 -0
- package/backend/src/types/auth.ts +5 -1
- package/backend/src/types/database.ts +2 -0
- package/backend/src/types/deployments.ts +33 -0
- package/backend/src/types/storage.ts +1 -1
- package/backend/src/types/webhooks.ts +45 -0
- package/backend/src/utils/cookies.ts +34 -35
- package/backend/src/utils/environment.ts +0 -14
- package/backend/src/utils/s3-config-loader.ts +64 -64
- package/backend/src/utils/seed.ts +334 -301
- package/backend/src/utils/sql-parser.ts +126 -0
- package/backend/src/utils/utils.ts +114 -114
- package/backend/src/utils/validations.ts +10 -10
- package/backend/tests/local/test-rpc.sh +141 -0
- package/backend/tests/local/test-secrets.sh +1 -1
- package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
- package/backend/tests/manual/test-rawsql-modes.sh +24 -24
- package/backend/tests/unit/database-advance.test.ts +326 -0
- package/backend/tests/unit/helpers.test.ts +2 -2
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
- package/docker-compose.prod.yml +1 -1
- package/docker-compose.yml +1 -1
- package/docs/agent-docs/deployment.md +79 -0
- package/docs/changelog.mdx +165 -72
- package/docs/core-concepts/ai/architecture.mdx +1 -23
- package/docs/core-concepts/ai/sdk.mdx +26 -1
- package/docs/core-concepts/authentication/architecture.mdx +6 -8
- package/docs/core-concepts/authentication/sdk.mdx +387 -91
- package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
- package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
- package/docs/core-concepts/database/architecture.mdx +58 -21
- package/docs/core-concepts/database/pgvector.mdx +138 -0
- package/docs/core-concepts/database/sdk.mdx +17 -17
- package/docs/core-concepts/deployments/architecture.mdx +152 -0
- package/docs/core-concepts/email/architecture.mdx +4 -2
- package/docs/core-concepts/functions/architecture.mdx +1 -1
- package/docs/core-concepts/functions/sdk.mdx +0 -1
- package/docs/core-concepts/realtime/architecture.mdx +1 -1
- package/docs/core-concepts/storage/architecture.mdx +1 -1
- package/docs/core-concepts/storage/sdk.mdx +25 -25
- package/docs/docs.json +14 -6
- package/docs/favicon.png +0 -0
- package/docs/favicon.svg +3 -18
- package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
- package/docs/images/changelog/dec-2025/moreModels.png +0 -0
- package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
- package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
- package/docs/images/changelog/dec-2025/realtime2.png +0 -0
- package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
- package/docs/images/mcp-setup/claude-code-connect.png +0 -0
- package/docs/images/mcp-setup/cline-1.png +0 -0
- package/docs/images/mcp-setup/cline-2.png +0 -0
- package/docs/images/mcp-setup/cline-3.png +0 -0
- package/docs/images/mcp-setup/connect-project.png +0 -0
- package/docs/images/mcp-setup/copilot-1.png +0 -0
- package/docs/images/mcp-setup/copilot-2.png +0 -0
- package/docs/images/mcp-setup/copilot-3.png +0 -0
- package/docs/images/mcp-setup/mcp-json-1.png +0 -0
- package/docs/images/mcp-setup/mcp-json-2.png +0 -0
- package/docs/images/mcp-setup/qoder-1.png +0 -0
- package/docs/images/mcp-setup/qoder-2.png +0 -0
- package/docs/images/mcp-setup/roocode-1.png +0 -0
- package/docs/images/mcp-setup/roocode-2.png +0 -0
- package/docs/images/mcp-setup/trae-1.png +0 -0
- package/docs/images/mcp-setup/trae-2.png +0 -0
- package/docs/images/mcp-setup/trae-3.png +0 -0
- package/docs/images/mcp-setup/trae-4.png +0 -0
- package/docs/images/mcp-setup/trae-5.png +0 -0
- package/docs/images/mcp-setup/windsurf-1.png +0 -0
- package/docs/images/mcp-setup/windsurf-2.png +0 -0
- package/docs/insforge-instructions-sdk.md +7 -3
- package/docs/introduction.mdx +9 -8
- package/docs/mcp-setup.mdx +332 -0
- package/docs/oauth-server.mdx +563 -0
- package/docs/partnership.mdx +79 -10
- package/docs/quickstart.mdx +1 -1
- package/docs/vscode-extension.mdx +74 -0
- package/eslint.config.js +1 -0
- package/examples/response-examples.md +1 -1
- package/frontend/package.json +1 -1
- package/frontend/src/App.tsx +8 -3
- package/frontend/src/assets/logos/antigravity.svg +1 -0
- package/frontend/src/assets/logos/copilot.svg +10 -0
- package/frontend/src/assets/logos/deepseek.svg +139 -0
- package/frontend/src/assets/logos/kiro.svg +9 -0
- package/frontend/src/assets/logos/qoder.svg +4 -0
- package/frontend/src/assets/logos/qwen.svg +15 -0
- package/frontend/src/components/CodeBlock.tsx +2 -2
- package/frontend/src/components/ConnectCTA.tsx +3 -2
- package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
- package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
- package/frontend/src/components/datagrid/index.ts +1 -1
- package/frontend/src/components/index.ts +0 -1
- package/frontend/src/components/layout/AppHeader.tsx +4 -27
- package/frontend/src/components/layout/AppSidebar.tsx +85 -100
- package/frontend/src/components/layout/Layout.tsx +34 -32
- package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
- package/frontend/src/components/radix/Select.tsx +151 -151
- package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
- package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
- package/frontend/src/features/ai/components/index.ts +6 -6
- package/frontend/src/features/ai/helpers.ts +147 -141
- package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
- package/frontend/src/features/auth/components/index.ts +5 -5
- package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
- package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
- package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
- package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
- package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
- package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
- package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
- package/frontend/src/features/database/constants.ts +16 -28
- package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
- package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
- package/frontend/src/features/database/hooks/useTables.ts +5 -7
- package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
- package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
- package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
- package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
- package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
- package/frontend/src/features/database/services/advance.service.ts +1 -15
- package/frontend/src/features/database/services/record.service.ts +4 -20
- package/frontend/src/features/database/services/table.service.ts +1 -4
- package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
- package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
- package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
- package/frontend/src/features/database/templates/notion-clone.ts +8 -8
- package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
- package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
- package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
- package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
- package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
- package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
- package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
- package/frontend/src/features/functions/components/index.ts +5 -5
- package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
- package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
- package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
- package/frontend/src/features/functions/services/function.service.ts +8 -25
- package/frontend/src/features/functions/services/secret.service.ts +23 -41
- package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
- package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
- package/frontend/src/features/logs/components/index.ts +1 -0
- package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
- package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
- package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
- package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
- package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
- package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
- package/frontend/src/features/onboard/components/index.ts +9 -4
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
- package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
- package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
- package/frontend/src/features/onboard/index.ts +17 -13
- package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
- package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
- package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
- package/frontend/src/index.css +249 -249
- package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
- package/frontend/src/lib/hooks/useMetadata.ts +45 -1
- package/frontend/src/lib/hooks/useModal.tsx +2 -0
- package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
- package/frontend/src/lib/services/metadata.service.ts +20 -3
- package/frontend/src/lib/utils/menuItems.ts +223 -207
- package/frontend/src/lib/utils/utils.ts +196 -196
- package/functions/server.ts +315 -315
- package/functions/worker-template.js +1 -1
- package/openapi/ai.yaml +115 -5
- package/openapi/auth.yaml +97 -17
- package/openapi/logs.yaml +0 -2
- package/openapi/metadata.yaml +0 -2
- package/openapi/records.yaml +21 -21
- package/openapi/tables.yaml +1 -2
- package/package.json +1 -1
- package/shared-schemas/package.json +1 -1
- package/shared-schemas/src/ai-api.schema.ts +251 -143
- package/shared-schemas/src/ai.schema.ts +63 -63
- package/shared-schemas/src/auth-api.schema.ts +34 -6
- package/shared-schemas/src/auth.schema.ts +17 -10
- package/shared-schemas/src/cloud-events.schema.ts +26 -0
- package/shared-schemas/src/deployments-api.schema.ts +55 -0
- package/shared-schemas/src/deployments.schema.ts +30 -0
- package/shared-schemas/src/docs.schema.ts +8 -2
- package/shared-schemas/src/email-api.schema.ts +30 -30
- package/shared-schemas/src/functions-api.schema.ts +13 -4
- package/shared-schemas/src/functions.schema.ts +1 -1
- package/shared-schemas/src/index.ts +22 -18
- package/shared-schemas/src/metadata.schema.ts +30 -4
- package/shared-schemas/src/secrets-api.schema.ts +44 -0
- package/shared-schemas/src/secrets.schema.ts +15 -0
- package/zeabur/README.md +13 -0
- package/zeabur/template.yml +20 -51
- package/backend/src/types/profile.ts +0 -55
- package/frontend/src/components/ProjectInfoModal.tsx +0 -128
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useState, useCallback, useMemo } from 'react';
|
|
2
2
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
-
import { functionService
|
|
3
|
+
import { functionService } from '../services/function.service';
|
|
4
|
+
import { FunctionSchema } from '@insforge/shared-schemas';
|
|
4
5
|
import { useToast } from '@/lib/hooks/useToast';
|
|
5
6
|
|
|
6
7
|
export function useFunctions() {
|
|
7
8
|
const queryClient = useQueryClient();
|
|
8
9
|
const { showToast } = useToast();
|
|
9
|
-
const [selectedFunction, setSelectedFunction] = useState<
|
|
10
|
+
const [selectedFunction, setSelectedFunction] = useState<FunctionSchema | null>(null);
|
|
10
11
|
|
|
11
12
|
// Query to fetch all functions
|
|
12
13
|
const {
|
|
@@ -26,7 +27,7 @@ export function useFunctions() {
|
|
|
26
27
|
|
|
27
28
|
// Function to fetch and set selected function details
|
|
28
29
|
const selectFunction = useCallback(
|
|
29
|
-
async (func:
|
|
30
|
+
async (func: FunctionSchema) => {
|
|
30
31
|
try {
|
|
31
32
|
const data = await functionService.getFunctionBySlug(func.slug);
|
|
32
33
|
setSelectedFunction(data);
|
|
@@ -97,7 +98,7 @@ export function useFunctions() {
|
|
|
97
98
|
|
|
98
99
|
// Helpers
|
|
99
100
|
getFunctionBySlug: useCallback(
|
|
100
|
-
(slug: string):
|
|
101
|
+
(slug: string): FunctionSchema | undefined => {
|
|
101
102
|
return displayFunctions.find((func) => func.slug === slug);
|
|
102
103
|
},
|
|
103
104
|
[displayFunctions]
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { useState, useCallback } from 'react';
|
|
2
2
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type Secret,
|
|
6
|
-
type CreateSecretInput,
|
|
7
|
-
} from '@/features/functions/services/secret.service';
|
|
3
|
+
import { secretService } from '@/features/functions/services/secret.service';
|
|
4
|
+
import type { SecretSchema, CreateSecretRequest } from '@insforge/shared-schemas';
|
|
8
5
|
import { useToast } from '@/lib/hooks/useToast';
|
|
9
6
|
import { useConfirm } from '@/lib/hooks/useConfirm';
|
|
10
7
|
|
|
@@ -27,11 +24,11 @@ export function useSecrets() {
|
|
|
27
24
|
});
|
|
28
25
|
|
|
29
26
|
// Filter out inactive secrets
|
|
30
|
-
const secrets = allSecrets.filter((secret:
|
|
27
|
+
const secrets = allSecrets.filter((secret: SecretSchema) => secret.isActive);
|
|
31
28
|
|
|
32
29
|
// Create secret mutation
|
|
33
30
|
const createSecretMutation = useMutation({
|
|
34
|
-
mutationFn: (input:
|
|
31
|
+
mutationFn: (input: CreateSecretRequest) => secretService.createSecret(input),
|
|
35
32
|
onSuccess: () => {
|
|
36
33
|
void queryClient.invalidateQueries({ queryKey: ['secrets'] });
|
|
37
34
|
showToast('Secret created successfully', 'success');
|
|
@@ -80,7 +77,7 @@ export function useSecrets() {
|
|
|
80
77
|
|
|
81
78
|
// Delete secret with confirmation
|
|
82
79
|
const deleteSecret = useCallback(
|
|
83
|
-
async (secret:
|
|
80
|
+
async (secret: SecretSchema) => {
|
|
84
81
|
if (secret.isReserved) {
|
|
85
82
|
showToast('Cannot delete reserved secrets', 'error');
|
|
86
83
|
return false;
|
|
@@ -108,7 +105,7 @@ export function useSecrets() {
|
|
|
108
105
|
);
|
|
109
106
|
|
|
110
107
|
// Filter secrets based on search query
|
|
111
|
-
const filteredSecrets = secrets.filter((secret:
|
|
108
|
+
const filteredSecrets = secrets.filter((secret: SecretSchema) =>
|
|
112
109
|
secret.key.toLowerCase().includes(searchQuery.toLowerCase())
|
|
113
110
|
);
|
|
114
111
|
|
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { Button, Input, Skeleton, SearchInput, ConfirmDialog } from '@/components';
|
|
3
|
-
import { SecretRow } from '../components/SecretRow';
|
|
4
|
-
import SecretEmptyState from '../components/SecretEmptyState';
|
|
5
|
-
import { useSecrets } from '@/features/functions/hooks/useSecrets';
|
|
6
|
-
|
|
7
|
-
export default function SecretsPage() {
|
|
8
|
-
const [newSecretKey, setNewSecretKey] = useState('');
|
|
9
|
-
const [newSecretValue, setNewSecretValue] = useState('');
|
|
10
|
-
|
|
11
|
-
const {
|
|
12
|
-
filteredSecrets,
|
|
13
|
-
searchQuery,
|
|
14
|
-
setSearchQuery,
|
|
15
|
-
isLoading: loading,
|
|
16
|
-
createSecret,
|
|
17
|
-
deleteSecret,
|
|
18
|
-
confirmDialogProps,
|
|
19
|
-
} = useSecrets();
|
|
20
|
-
|
|
21
|
-
const handleSaveNewSecret = async () => {
|
|
22
|
-
const success = await createSecret(newSecretKey, newSecretValue);
|
|
23
|
-
if (success) {
|
|
24
|
-
setNewSecretKey('');
|
|
25
|
-
setNewSecretValue('');
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<div className="h-full flex flex-col overflow-hidden">
|
|
31
|
-
<div className="flex flex-col gap-6 p-4">
|
|
32
|
-
{/* Header */}
|
|
33
|
-
<p className="h-7 text-xl text-zinc-950 dark:text-white">Secrets</p>
|
|
34
|
-
|
|
35
|
-
{/* Add New Secret Portal */}
|
|
36
|
-
<div className="bg-white dark:bg-[#333333] rounded-[8px]">
|
|
37
|
-
<div className="p-6 border-b border-gray-200 dark:border-neutral-700">
|
|
38
|
-
<p className="text-base text-zinc-950 dark:text-white">Add New Secret</p>
|
|
39
|
-
</div>
|
|
40
|
-
<div className="p-6 flex gap-6 items-end">
|
|
41
|
-
<div className="flex-1">
|
|
42
|
-
<label className="block text-sm text-zinc-950 dark:text-neutral-50 mb-2">Key</label>
|
|
43
|
-
<Input
|
|
44
|
-
placeholder="e.g CLIENT_KEY"
|
|
45
|
-
value={newSecretKey}
|
|
46
|
-
onChange={(e) => setNewSecretKey(e.target.value)}
|
|
47
|
-
className="shadow-none w-full dark:bg-neutral-900 dark:text-white dark:placeholder:text-neutral-400 dark:border-neutral-700"
|
|
48
|
-
/>
|
|
49
|
-
</div>
|
|
50
|
-
<div className="flex-1">
|
|
51
|
-
<label className="block text-sm text-zinc-950 dark:text-neutral-50 mb-2">Value</label>
|
|
52
|
-
<Input
|
|
53
|
-
placeholder="e.g 1234567890"
|
|
54
|
-
type="text"
|
|
55
|
-
value={newSecretValue}
|
|
56
|
-
onChange={(e) => setNewSecretValue(e.target.value)}
|
|
57
|
-
className="shadow-none w-full dark:bg-neutral-900 dark:text-white dark:placeholder:text-neutral-400 dark:border-neutral-700"
|
|
58
|
-
/>
|
|
59
|
-
</div>
|
|
60
|
-
<Button
|
|
61
|
-
onClick={() => void handleSaveNewSecret()}
|
|
62
|
-
className="bg-black hover:bg-zinc-800 dark:bg-emerald-300 dark:hover:bg-emerald-400 dark:text-black text-white px-3 py-2 w-20 h-9 rounded"
|
|
63
|
-
disabled={!newSecretKey.trim() || !newSecretValue.trim()}
|
|
64
|
-
>
|
|
65
|
-
Save
|
|
66
|
-
</Button>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
{/* Search Bar */}
|
|
71
|
-
<SearchInput
|
|
72
|
-
placeholder="Search secret"
|
|
73
|
-
value={searchQuery}
|
|
74
|
-
onChange={setSearchQuery}
|
|
75
|
-
className="max-w-70 dark:bg-neutral-900 dark:border-neutral-700"
|
|
76
|
-
/>
|
|
77
|
-
|
|
78
|
-
{/* Secrets Table Header */}
|
|
79
|
-
<div className="grid grid-cols-12 px-3 text-sm text-muted-foreground dark:text-neutral-400">
|
|
80
|
-
<div className="col-span-8 py-1 px-3">Name</div>
|
|
81
|
-
{/* <div className="col-span-5 py-1 px-3">Digest</div> */}
|
|
82
|
-
<div className="col-span-3 py-1 px-3">Updated at</div>
|
|
83
|
-
<div className="col-span-1 py-1 px-3" />
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
|
|
87
|
-
{/* Scrollable Table Body */}
|
|
88
|
-
<div className="flex-1 min-h-0 overflow-y-auto px-4 pb-4">
|
|
89
|
-
<div className="flex flex-col gap-2">
|
|
90
|
-
{loading ? (
|
|
91
|
-
<>
|
|
92
|
-
{[...Array(4)].map((_, i) => (
|
|
93
|
-
<Skeleton key={i} className="h-14 rounded-[8px] cols-span-full" />
|
|
94
|
-
))}
|
|
95
|
-
</>
|
|
96
|
-
) : filteredSecrets.length >= 1 ? (
|
|
97
|
-
<>
|
|
98
|
-
{filteredSecrets.map((secret) => (
|
|
99
|
-
<SecretRow
|
|
100
|
-
key={secret.id}
|
|
101
|
-
secret={secret}
|
|
102
|
-
onDelete={() => void deleteSecret(secret)}
|
|
103
|
-
className="cols-span-full"
|
|
104
|
-
/>
|
|
105
|
-
))}
|
|
106
|
-
</>
|
|
107
|
-
) : (
|
|
108
|
-
<div className="cols-span-full">
|
|
109
|
-
<SecretEmptyState searchQuery={searchQuery} />
|
|
110
|
-
</div>
|
|
111
|
-
)}
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
|
|
115
|
-
<ConfirmDialog {...confirmDialogProps} />
|
|
116
|
-
</div>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Button, Input, Skeleton, SearchInput, ConfirmDialog } from '@/components';
|
|
3
|
+
import { SecretRow } from '../components/SecretRow';
|
|
4
|
+
import SecretEmptyState from '../components/SecretEmptyState';
|
|
5
|
+
import { useSecrets } from '@/features/functions/hooks/useSecrets';
|
|
6
|
+
|
|
7
|
+
export default function SecretsPage() {
|
|
8
|
+
const [newSecretKey, setNewSecretKey] = useState('');
|
|
9
|
+
const [newSecretValue, setNewSecretValue] = useState('');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
filteredSecrets,
|
|
13
|
+
searchQuery,
|
|
14
|
+
setSearchQuery,
|
|
15
|
+
isLoading: loading,
|
|
16
|
+
createSecret,
|
|
17
|
+
deleteSecret,
|
|
18
|
+
confirmDialogProps,
|
|
19
|
+
} = useSecrets();
|
|
20
|
+
|
|
21
|
+
const handleSaveNewSecret = async () => {
|
|
22
|
+
const success = await createSecret(newSecretKey, newSecretValue);
|
|
23
|
+
if (success) {
|
|
24
|
+
setNewSecretKey('');
|
|
25
|
+
setNewSecretValue('');
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="h-full flex flex-col overflow-hidden">
|
|
31
|
+
<div className="flex flex-col gap-6 p-4">
|
|
32
|
+
{/* Header */}
|
|
33
|
+
<p className="h-7 text-xl text-zinc-950 dark:text-white">Secrets</p>
|
|
34
|
+
|
|
35
|
+
{/* Add New Secret Portal */}
|
|
36
|
+
<div className="bg-white dark:bg-[#333333] rounded-[8px]">
|
|
37
|
+
<div className="p-6 border-b border-gray-200 dark:border-neutral-700">
|
|
38
|
+
<p className="text-base text-zinc-950 dark:text-white">Add New Secret</p>
|
|
39
|
+
</div>
|
|
40
|
+
<div className="p-6 flex gap-6 items-end">
|
|
41
|
+
<div className="flex-1">
|
|
42
|
+
<label className="block text-sm text-zinc-950 dark:text-neutral-50 mb-2">Key</label>
|
|
43
|
+
<Input
|
|
44
|
+
placeholder="e.g CLIENT_KEY"
|
|
45
|
+
value={newSecretKey}
|
|
46
|
+
onChange={(e) => setNewSecretKey(e.target.value)}
|
|
47
|
+
className="shadow-none w-full dark:bg-neutral-900 dark:text-white dark:placeholder:text-neutral-400 dark:border-neutral-700"
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
<div className="flex-1">
|
|
51
|
+
<label className="block text-sm text-zinc-950 dark:text-neutral-50 mb-2">Value</label>
|
|
52
|
+
<Input
|
|
53
|
+
placeholder="e.g 1234567890"
|
|
54
|
+
type="text"
|
|
55
|
+
value={newSecretValue}
|
|
56
|
+
onChange={(e) => setNewSecretValue(e.target.value)}
|
|
57
|
+
className="shadow-none w-full dark:bg-neutral-900 dark:text-white dark:placeholder:text-neutral-400 dark:border-neutral-700"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
<Button
|
|
61
|
+
onClick={() => void handleSaveNewSecret()}
|
|
62
|
+
className="bg-black hover:bg-zinc-800 dark:bg-emerald-300 dark:hover:bg-emerald-400 dark:text-black text-white px-3 py-2 w-20 h-9 rounded"
|
|
63
|
+
disabled={!newSecretKey.trim() || !newSecretValue.trim()}
|
|
64
|
+
>
|
|
65
|
+
Save
|
|
66
|
+
</Button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
{/* Search Bar */}
|
|
71
|
+
<SearchInput
|
|
72
|
+
placeholder="Search secret"
|
|
73
|
+
value={searchQuery}
|
|
74
|
+
onChange={setSearchQuery}
|
|
75
|
+
className="max-w-70 dark:bg-neutral-900 dark:border-neutral-700"
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
{/* Secrets Table Header */}
|
|
79
|
+
<div className="grid grid-cols-12 px-3 text-sm text-muted-foreground dark:text-neutral-400">
|
|
80
|
+
<div className="col-span-8 py-1 px-3">Name</div>
|
|
81
|
+
{/* <div className="col-span-5 py-1 px-3">Digest</div> */}
|
|
82
|
+
<div className="col-span-3 py-1 px-3">Updated at</div>
|
|
83
|
+
<div className="col-span-1 py-1 px-3" />
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
{/* Scrollable Table Body */}
|
|
88
|
+
<div className="flex-1 min-h-0 overflow-y-auto px-4 pb-4">
|
|
89
|
+
<div className="flex flex-col gap-2">
|
|
90
|
+
{loading ? (
|
|
91
|
+
<>
|
|
92
|
+
{[...Array(4)].map((_, i) => (
|
|
93
|
+
<Skeleton key={i} className="h-14 rounded-[8px] cols-span-full" />
|
|
94
|
+
))}
|
|
95
|
+
</>
|
|
96
|
+
) : filteredSecrets.length >= 1 ? (
|
|
97
|
+
<>
|
|
98
|
+
{filteredSecrets.map((secret) => (
|
|
99
|
+
<SecretRow
|
|
100
|
+
key={secret.id}
|
|
101
|
+
secret={secret}
|
|
102
|
+
onDelete={() => void deleteSecret(secret)}
|
|
103
|
+
className="cols-span-full"
|
|
104
|
+
/>
|
|
105
|
+
))}
|
|
106
|
+
</>
|
|
107
|
+
) : (
|
|
108
|
+
<div className="cols-span-full">
|
|
109
|
+
<SecretEmptyState searchQuery={searchQuery} />
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<ConfirmDialog {...confirmDialogProps} />
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -1,40 +1,23 @@
|
|
|
1
1
|
import { apiClient } from '@/lib/api/client';
|
|
2
|
-
|
|
3
|
-
export interface EdgeFunction {
|
|
4
|
-
id: string;
|
|
5
|
-
slug: string;
|
|
6
|
-
name: string;
|
|
7
|
-
description?: string;
|
|
8
|
-
code?: string;
|
|
9
|
-
status: 'draft' | 'active' | 'error';
|
|
10
|
-
created_at: string;
|
|
11
|
-
updated_at: string;
|
|
12
|
-
deployed_at?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface FunctionsResponse {
|
|
16
|
-
functions: EdgeFunction[];
|
|
17
|
-
runtime: {
|
|
18
|
-
status: 'running' | 'unavailable';
|
|
19
|
-
};
|
|
20
|
-
}
|
|
2
|
+
import { FunctionSchema, ListFunctionsResponse } from '@insforge/shared-schemas';
|
|
21
3
|
|
|
22
4
|
export class FunctionService {
|
|
23
|
-
async listFunctions(): Promise<
|
|
24
|
-
const
|
|
5
|
+
async listFunctions(): Promise<ListFunctionsResponse> {
|
|
6
|
+
const response: ListFunctionsResponse = await apiClient.request('/functions', {
|
|
25
7
|
headers: apiClient.withAccessToken(),
|
|
26
8
|
});
|
|
27
9
|
|
|
28
10
|
return {
|
|
29
|
-
functions: Array.isArray(
|
|
30
|
-
runtime:
|
|
11
|
+
functions: Array.isArray(response.functions) ? response.functions : [],
|
|
12
|
+
runtime: response.runtime || { status: 'unavailable' },
|
|
31
13
|
};
|
|
32
14
|
}
|
|
33
15
|
|
|
34
|
-
async getFunctionBySlug(slug: string): Promise<
|
|
35
|
-
|
|
16
|
+
async getFunctionBySlug(slug: string): Promise<FunctionSchema> {
|
|
17
|
+
const response: FunctionSchema = await apiClient.request(`/functions/${slug}`, {
|
|
36
18
|
headers: apiClient.withAccessToken(),
|
|
37
19
|
});
|
|
20
|
+
return response;
|
|
38
21
|
}
|
|
39
22
|
|
|
40
23
|
async deleteFunction(slug: string): Promise<void> {
|
|
@@ -1,56 +1,38 @@
|
|
|
1
1
|
import { apiClient } from '@/lib/api/client';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
expiresAt: string | null;
|
|
10
|
-
createdAt: string;
|
|
11
|
-
updatedAt: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface CreateSecretInput {
|
|
15
|
-
key: string;
|
|
16
|
-
value: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface SecretsListResponse {
|
|
20
|
-
secrets: Secret[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface SecretValueResponse {
|
|
24
|
-
key: string;
|
|
25
|
-
value: string;
|
|
26
|
-
}
|
|
2
|
+
import {
|
|
3
|
+
SecretSchema,
|
|
4
|
+
CreateSecretRequest,
|
|
5
|
+
CreateSecretResponse,
|
|
6
|
+
ListSecretsResponse,
|
|
7
|
+
DeleteSecretResponse,
|
|
8
|
+
} from '@insforge/shared-schemas';
|
|
27
9
|
|
|
28
10
|
export class SecretService {
|
|
29
|
-
async listSecrets(): Promise<
|
|
11
|
+
async listSecrets(): Promise<SecretSchema[]> {
|
|
30
12
|
const data = (await apiClient.request('/secrets', {
|
|
31
13
|
headers: apiClient.withAccessToken(),
|
|
32
|
-
})) as
|
|
33
|
-
return data.secrets
|
|
14
|
+
})) as ListSecretsResponse;
|
|
15
|
+
return data.secrets as SecretSchema[];
|
|
34
16
|
}
|
|
35
17
|
|
|
36
|
-
async createSecret(
|
|
37
|
-
|
|
38
|
-
): Promise<{ success: boolean; message: string; id?: string }> {
|
|
39
|
-
return apiClient.request('/secrets', {
|
|
18
|
+
async createSecret(input: CreateSecretRequest): Promise<CreateSecretResponse> {
|
|
19
|
+
const response: CreateSecretResponse = await apiClient.request('/secrets', {
|
|
40
20
|
method: 'POST',
|
|
41
|
-
headers:
|
|
42
|
-
'Content-Type': 'application/json',
|
|
43
|
-
...apiClient.withAccessToken(),
|
|
44
|
-
},
|
|
21
|
+
headers: apiClient.withAccessToken(),
|
|
45
22
|
body: JSON.stringify(input),
|
|
46
23
|
});
|
|
24
|
+
return response;
|
|
47
25
|
}
|
|
48
26
|
|
|
49
|
-
async deleteSecret(key: string): Promise<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
27
|
+
async deleteSecret(key: string): Promise<DeleteSecretResponse> {
|
|
28
|
+
const response: DeleteSecretResponse = await apiClient.request(
|
|
29
|
+
`/secrets/${encodeURIComponent(key)}`,
|
|
30
|
+
{
|
|
31
|
+
method: 'DELETE',
|
|
32
|
+
headers: apiClient.withAccessToken(),
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
return response;
|
|
54
36
|
}
|
|
55
37
|
}
|
|
56
38
|
|