insforge 1.2.10 → 1.3.0
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/.claude-plugin/marketplace.json +20 -20
- package/.dockerignore +60 -60
- package/.env.example +83 -77
- package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -36
- package/.github/ISSUE_TEMPLATE/config.yml +11 -11
- package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -26
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -7
- package/.github/copilot-instructions.md +146 -146
- package/.github/workflows/build-image.yml +65 -65
- package/.github/workflows/ci-premerge-check.yml +23 -23
- package/.github/workflows/e2e.yml +63 -63
- package/.github/workflows/lint-and-format.yml +32 -32
- package/.prettierignore +64 -64
- package/CHANGELOG.md +44 -44
- package/CLAUDE_PLUGIN.md +104 -104
- package/CODE_OF_CONDUCT.md +128 -128
- package/CONTRIBUTING.md +125 -125
- package/Dockerfile +30 -30
- package/GITHUB_OAUTH_SETUP.md +49 -49
- package/GOOGLE_OAUTH_SETUP.md +148 -148
- package/LICENSE +201 -201
- package/README.md +182 -182
- package/assets/Dark.svg +23 -23
- package/auth/package.json +28 -28
- package/auth/src/lib/broadcastService.ts +117 -115
- package/auth/src/pages/SignInPage.tsx +60 -57
- package/auth/src/pages/SignUpPage.tsx +60 -57
- package/auth/tsconfig.json +32 -32
- package/auth/tsconfig.node.json +11 -11
- package/backend/package.json +78 -75
- package/backend/src/api/routes/ai/index.routes.ts +3 -3
- package/backend/src/api/routes/auth/index.routes.ts +667 -570
- package/backend/src/api/routes/auth/oauth.routes.ts +473 -448
- package/backend/src/api/routes/database/advance.routes.ts +37 -16
- package/backend/src/api/routes/database/index.routes.ts +78 -1
- package/backend/src/api/routes/database/records.routes.ts +10 -10
- package/backend/src/api/routes/database/tables.routes.ts +0 -14
- package/backend/src/api/routes/docs/index.routes.ts +75 -76
- package/backend/src/api/routes/email/index.routes.ts +35 -0
- package/backend/src/api/routes/functions/index.routes.ts +18 -12
- package/backend/src/api/routes/metadata/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
- package/backend/src/api/routes/realtime/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
- package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
- package/backend/src/api/routes/storage/index.routes.ts +18 -12
- package/backend/src/api/routes/usage/index.routes.ts +6 -4
- package/backend/src/infra/database/database.manager.ts +14 -1
- package/backend/src/infra/database/migrations/000_create-base-tables.sql +141 -141
- package/backend/src/infra/database/migrations/001_create-helper-functions.sql +40 -40
- package/backend/src/infra/database/migrations/002_rename-auth-tables.sql +29 -29
- package/backend/src/infra/database/migrations/003_create-users-table.sql +55 -55
- package/backend/src/infra/database/migrations/004_add-reload-postgrest-func.sql +23 -23
- package/backend/src/infra/database/migrations/005_enable-project-admin-modify-users.sql +29 -29
- package/backend/src/infra/database/migrations/006_modify-ai-usage-table.sql +24 -24
- package/backend/src/infra/database/migrations/007_drop-metadata-table.sql +1 -1
- package/backend/src/infra/database/migrations/008_add-system-tables.sql +76 -76
- package/backend/src/infra/database/migrations/009_add-function-secrets.sql +23 -23
- package/backend/src/infra/database/migrations/010_modify-ai-config-modalities.sql +93 -93
- package/backend/src/infra/database/migrations/011_refactor-secrets-table.sql +15 -15
- package/backend/src/infra/database/migrations/012_add-storage-uploaded-by.sql +7 -7
- package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -44
- package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +7 -7
- package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +59 -59
- package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -24
- package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
- package/backend/src/infra/realtime/realtime.manager.ts +246 -0
- package/backend/src/infra/realtime/webhook-sender.ts +82 -0
- package/backend/src/infra/security/token.manager.ts +219 -125
- package/backend/src/infra/socket/socket.manager.ts +198 -64
- package/backend/src/providers/ai/openrouter.provider.ts +12 -9
- package/backend/src/providers/email/base.provider.ts +4 -7
- package/backend/src/providers/email/cloud.provider.ts +84 -0
- package/backend/src/providers/oauth/apple.provider.ts +266 -0
- package/backend/src/providers/oauth/index.ts +1 -0
- package/backend/src/server.ts +317 -284
- package/backend/src/services/ai/ai-model.service.ts +5 -5
- package/backend/src/services/ai/chat-completion.service.ts +4 -4
- package/backend/src/services/ai/image-generation.service.ts +3 -3
- package/backend/src/services/auth/auth.service.ts +14 -0
- package/backend/src/services/database/database-table.service.ts +0 -9
- package/backend/src/services/database/database.service.ts +127 -0
- package/backend/src/services/email/email.service.ts +5 -7
- package/backend/src/services/realtime/index.ts +3 -0
- package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
- package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
- package/backend/src/services/realtime/realtime-message.service.ts +260 -0
- package/backend/src/types/auth.ts +11 -0
- package/backend/src/types/realtime.ts +18 -0
- package/backend/src/types/socket.ts +7 -31
- package/backend/src/utils/cookies.ts +35 -0
- package/backend/src/utils/s3-config-loader.ts +64 -0
- package/backend/src/utils/seed.ts +301 -298
- package/backend/src/utils/sql-parser.ts +90 -0
- package/backend/tests/README.md +133 -133
- package/backend/tests/cleanup-all-test-data.sh +230 -230
- package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
- package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
- package/backend/tests/local/test-ai-config.sh +129 -129
- package/backend/tests/local/test-ai-usage.sh +80 -80
- package/backend/tests/local/test-auth-router.sh +143 -143
- package/backend/tests/local/test-database-router.sh +222 -222
- package/backend/tests/local/test-e2e.sh +240 -240
- package/backend/tests/local/test-fk-errors.sh +96 -96
- package/backend/tests/local/test-functions.sh +123 -123
- package/backend/tests/local/test-id-field.sh +200 -200
- package/backend/tests/local/test-logs.sh +132 -132
- package/backend/tests/local/test-public-bucket.sh +264 -264
- package/backend/tests/local/test-secrets.sh +249 -249
- package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
- package/backend/tests/local/test-traditional-rest.sh +208 -208
- package/backend/tests/manual/README.md +50 -50
- package/backend/tests/manual/create-large-table-simple.sql +10 -10
- package/backend/tests/manual/seed-large-table.sql +100 -100
- package/backend/tests/manual/setup-large-table-extras.sql +33 -33
- package/backend/tests/manual/test-bulk-upsert.sh +409 -409
- package/backend/tests/manual/test-database-advance.sh +296 -296
- package/backend/tests/manual/test-postgrest-stability.sh +191 -191
- package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
- package/backend/tests/manual/test-rawsql-modes.sh +244 -244
- package/backend/tests/manual/test-universal-storage.sh +263 -263
- package/backend/tests/manual/test-users.sql +17 -17
- package/backend/tests/run-all-tests.sh +139 -139
- package/backend/tests/setup.ts +0 -0
- package/backend/tests/test-config.sh +338 -338
- package/backend/tests/unit/analyze-query.test.ts +697 -0
- package/backend/tsconfig.json +22 -22
- package/claude-plugin/.claude-plugin/plugin.json +24 -24
- package/claude-plugin/README.md +133 -133
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -270
- package/docker-compose.prod.yml +204 -200
- package/docker-compose.yml +232 -228
- package/docker-init/db/db-init.sql +97 -97
- package/docker-init/db/jwt.sql +5 -5
- package/docker-init/db/postgresql.conf +16 -16
- package/docker-init/logs/vector.yml +236 -236
- package/docs/README.md +44 -44
- package/docs/agent-docs/real-time.md +269 -0
- package/docs/changelog.mdx +119 -67
- package/docs/core-concepts/ai/architecture.mdx +372 -372
- package/docs/core-concepts/ai/sdk.mdx +213 -213
- package/docs/core-concepts/authentication/architecture.mdx +278 -278
- package/docs/core-concepts/authentication/sdk.mdx +414 -414
- package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -529
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -221
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -184
- package/docs/core-concepts/authentication/ui-components/react.mdx +129 -129
- package/docs/core-concepts/database/architecture.mdx +255 -255
- package/docs/core-concepts/database/sdk.mdx +382 -382
- package/docs/core-concepts/email/architecture.mdx +101 -0
- package/docs/core-concepts/email/sdk.mdx +53 -0
- package/docs/core-concepts/functions/architecture.mdx +105 -105
- package/docs/core-concepts/functions/sdk.mdx +184 -184
- package/docs/core-concepts/realtime/architecture.mdx +446 -0
- package/docs/core-concepts/realtime/sdk.mdx +409 -0
- package/docs/core-concepts/storage/architecture.mdx +243 -243
- package/docs/core-concepts/storage/sdk.mdx +253 -253
- package/docs/deployment/README.md +94 -94
- package/docs/deployment/deploy-to-aws-ec2.md +564 -564
- package/docs/deployment/deploy-to-azure-virtual-machines.md +312 -312
- package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -613
- package/docs/deployment/deploy-to-render.md +441 -441
- package/docs/deprecated/insforge-auth-api.md +214 -214
- package/docs/deprecated/insforge-auth-sdk.md +99 -99
- package/docs/deprecated/insforge-db-api.md +358 -358
- package/docs/deprecated/insforge-db-sdk.md +139 -139
- package/docs/deprecated/insforge-debug-sdk.md +156 -156
- package/docs/deprecated/insforge-debug.md +64 -64
- package/docs/deprecated/insforge-instructions.md +123 -123
- package/docs/deprecated/insforge-project.md +117 -117
- package/docs/deprecated/insforge-storage-api.md +278 -278
- package/docs/deprecated/insforge-storage-sdk.md +158 -158
- package/docs/docs.json +232 -210
- package/docs/examples/framework-guides/nextjs.mdx +131 -131
- package/docs/examples/framework-guides/nuxt.mdx +165 -165
- package/docs/examples/framework-guides/react.mdx +165 -165
- package/docs/examples/framework-guides/svelte.mdx +153 -153
- package/docs/examples/framework-guides/vue.mdx +159 -159
- package/docs/examples/overview.mdx +67 -67
- package/docs/favicon.svg +19 -19
- package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
- package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
- package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
- package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
- package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
- package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
- package/docs/images/icons/ai.svg +4 -4
- package/docs/images/logos/nextjs.svg +4 -4
- package/docs/images/logos/nuxt.svg +4 -4
- package/docs/images/logos/react.svg +5 -5
- package/docs/images/logos/svelte.svg +4 -4
- package/docs/images/logos/vue.svg +5 -5
- package/docs/insforge-instructions-sdk.md +89 -88
- package/docs/introduction.mdx +45 -45
- package/docs/logo/dark.svg +22 -22
- package/docs/logo/light.svg +20 -20
- package/docs/partnership.mdx +651 -646
- package/docs/quickstart.mdx +82 -82
- package/docs/showcase.mdx +52 -52
- package/docs/snippets/sdk-installation.mdx +21 -21
- package/docs/snippets/service-icons.mdx +27 -27
- package/examples/oauth/frontend-oauth-example.html +250 -250
- package/examples/response-examples.md +443 -443
- package/frontend/components.json +17 -17
- package/frontend/package.json +69 -69
- package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
- package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
- package/frontend/src/assets/icons/checked.svg +3 -3
- package/frontend/src/assets/icons/connected.svg +3 -3
- package/frontend/src/assets/icons/error.svg +3 -3
- package/frontend/src/assets/icons/loader.svg +9 -9
- package/frontend/src/assets/icons/pencil.svg +4 -4
- package/frontend/src/assets/icons/refresh.svg +4 -4
- package/frontend/src/assets/icons/step_active.svg +3 -3
- package/frontend/src/assets/icons/step_inactive.svg +11 -11
- package/frontend/src/assets/icons/warning.svg +3 -3
- package/frontend/src/assets/logos/apple.svg +3 -3
- package/frontend/src/assets/logos/claude_code.svg +3 -3
- package/frontend/src/assets/logos/cline.svg +6 -6
- package/frontend/src/assets/logos/cursor.svg +20 -20
- package/frontend/src/assets/logos/discord.svg +8 -8
- package/frontend/src/assets/logos/facebook.svg +3 -3
- package/frontend/src/assets/logos/gemini.svg +19 -19
- package/frontend/src/assets/logos/github.svg +5 -5
- package/frontend/src/assets/logos/google.svg +13 -13
- package/frontend/src/assets/logos/grok.svg +10 -10
- package/frontend/src/assets/logos/insforge_dark.svg +15 -15
- package/frontend/src/assets/logos/insforge_light.svg +15 -15
- package/frontend/src/assets/logos/instagram.svg +1 -1
- package/frontend/src/assets/logos/linkedin.svg +3 -3
- package/frontend/src/assets/logos/openai.svg +10 -10
- package/frontend/src/assets/logos/roo_code.svg +9 -9
- package/frontend/src/assets/logos/spotify.svg +16 -16
- package/frontend/src/assets/logos/tiktok.svg +5 -5
- package/frontend/src/assets/logos/trae.svg +3 -3
- package/frontend/src/assets/logos/windsurf.svg +10 -10
- package/frontend/src/assets/logos/x.svg +3 -3
- package/frontend/src/components/layout/AppHeader.tsx +9 -10
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +1 -0
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +6 -0
- package/frontend/src/features/auth/helpers.tsx +8 -0
- package/frontend/src/features/auth/{page → pages}/UsersPage.tsx +0 -28
- package/frontend/src/features/database/components/SQLModal.tsx +75 -0
- package/frontend/src/features/database/components/TableForm.tsx +0 -4
- package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
- package/frontend/src/features/database/hooks/useTables.ts +32 -28
- package/frontend/src/features/database/index.ts +1 -0
- package/frontend/src/features/database/{page → pages}/FunctionsPage.tsx +29 -37
- package/frontend/src/features/database/{page → pages}/IndexesPage.tsx +35 -47
- package/frontend/src/features/database/{page → pages}/PoliciesPage.tsx +43 -54
- package/frontend/src/features/database/{page → pages}/TablesPage.tsx +0 -42
- package/frontend/src/features/database/{page → pages}/TriggersPage.tsx +35 -47
- package/frontend/src/features/database/services/advance.service.ts +0 -26
- package/frontend/src/features/database/services/database.service.ts +55 -0
- package/frontend/src/features/database/services/table.service.ts +0 -6
- package/frontend/src/features/functions/{page → pages}/FunctionsPage.tsx +21 -44
- package/frontend/src/features/functions/{page → pages}/SecretsPage.tsx +11 -9
- package/frontend/src/features/logs/hooks/useMcpUsage.ts +13 -66
- package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
- package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
- package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
- package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
- package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
- package/frontend/src/features/realtime/index.ts +11 -0
- package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
- package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
- package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
- package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
- package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +1 -29
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +3 -3
- package/frontend/src/features/visualizer/{page → pages}/VisualizerPage.tsx +1 -35
- package/frontend/src/lib/contexts/SocketContext.tsx +119 -75
- package/frontend/src/lib/routing/AppRoutes.tsx +35 -20
- package/frontend/src/lib/utils/cloudMessaging.ts +1 -1
- package/frontend/src/lib/utils/menuItems.ts +24 -0
- package/frontend/src/lib/utils/utils.ts +14 -1
- package/frontend/tsconfig.json +25 -25
- package/frontend/tsconfig.node.json +9 -9
- package/functions/deno.json +24 -24
- package/functions/server.ts +315 -315
- package/i18n/README.ar.md +130 -130
- package/i18n/README.de.md +130 -130
- package/i18n/README.es.md +154 -154
- package/i18n/README.fr.md +134 -134
- package/i18n/README.hi.md +129 -129
- package/i18n/README.ja.md +174 -174
- package/i18n/README.ko.md +136 -136
- package/i18n/README.pt-BR.md +131 -131
- package/i18n/README.ru.md +129 -129
- package/i18n/README.zh-CN.md +133 -133
- package/openapi/ai.yaml +715 -715
- package/openapi/auth.yaml +1244 -1244
- package/openapi/email.yaml +158 -0
- package/openapi/functions.yaml +475 -475
- package/openapi/health.yaml +29 -29
- package/openapi/logs.yaml +223 -223
- package/openapi/metadata.yaml +177 -177
- package/openapi/realtime.yaml +699 -0
- package/openapi/records.yaml +381 -381
- package/openapi/secrets.yaml +370 -370
- package/openapi/storage.yaml +875 -875
- package/openapi/tables.yaml +463 -463
- package/package.json +97 -97
- package/shared-schemas/package.json +31 -31
- package/shared-schemas/src/ai.schema.ts +63 -59
- package/shared-schemas/src/auth-api.schema.ts +352 -339
- package/shared-schemas/src/auth.schema.ts +1 -1
- package/shared-schemas/src/database-api.schema.ts +32 -1
- package/shared-schemas/src/database.schema.ts +39 -0
- package/shared-schemas/src/docs.schema.ts +26 -0
- package/shared-schemas/src/email-api.schema.ts +30 -0
- package/shared-schemas/src/index.ts +4 -0
- package/shared-schemas/src/metadata.schema.ts +9 -0
- package/shared-schemas/src/realtime-api.schema.ts +111 -0
- package/shared-schemas/src/realtime.schema.ts +143 -0
- package/shared-schemas/tsconfig.json +21 -21
- package/tsconfig.json +7 -7
- package/zeabur/README.md +13 -13
- package/zeabur/template.yml +1032 -1032
- package/.cursor/rules/cursor-rules.mdc +0 -94
- package/frontend/src/features/database/hooks/useFullMetadata.ts +0 -18
- package/test-gemini.sh +0 -35
- package/test-usage-admin.sh +0 -57
- package/test-usage.sh +0 -50
- /package/frontend/src/features/ai/{page → pages}/AIPage.tsx +0 -0
- /package/frontend/src/features/auth/{page → pages}/AuthMethodsPage.tsx +0 -0
- /package/frontend/src/features/auth/{page → pages}/ConfigurationPage.tsx +0 -0
- /package/frontend/src/features/dashboard/{page → pages}/DashboardPage.tsx +0 -0
- /package/frontend/src/features/database/{page → pages}/SQLEditorPage.tsx +0 -0
- /package/frontend/src/features/database/{page → pages}/TemplatesPage.tsx +0 -0
- /package/frontend/src/features/login/{page → pages}/CloudLoginPage.tsx +0 -0
- /package/frontend/src/features/login/{page → pages}/LoginPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/LogsPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/MCPLogsPage.tsx +0 -0
|
@@ -1,264 +1,264 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Test script for universal storage API with presigned URL support
|
|
4
|
-
# Tests both the "backend decides" approach for S3 and local storage
|
|
5
|
-
|
|
6
|
-
set -e
|
|
7
|
-
|
|
8
|
-
API_URL="http://localhost:7130"
|
|
9
|
-
BUCKET_NAME="test-universal-bucket"
|
|
10
|
-
TEST_FILE="test-upload.txt"
|
|
11
|
-
|
|
12
|
-
# Colors for output
|
|
13
|
-
GREEN='\033[0;32m'
|
|
14
|
-
RED='\033[0;31m'
|
|
15
|
-
YELLOW='\033[1;33m'
|
|
16
|
-
NC='\033[0m' # No Color
|
|
17
|
-
|
|
18
|
-
echo "=== Universal Storage API Test ==="
|
|
19
|
-
echo ""
|
|
20
|
-
|
|
21
|
-
# Create test file
|
|
22
|
-
echo "This is a test file for universal storage upload" > $TEST_FILE
|
|
23
|
-
FILE_SIZE=$(stat -f%z "$TEST_FILE" 2>/dev/null || stat -c%s "$TEST_FILE" 2>/dev/null)
|
|
24
|
-
|
|
25
|
-
# Step 1: Login to get auth token
|
|
26
|
-
echo "1. Logging in..."
|
|
27
|
-
LOGIN_RESPONSE=$(curl -s -X POST $API_URL/api/auth/admin/sessions \
|
|
28
|
-
-H 'Content-Type: application/json' \
|
|
29
|
-
-d '{
|
|
30
|
-
"email": "admin@example.com",
|
|
31
|
-
"password": "change-this-password"
|
|
32
|
-
}')
|
|
33
|
-
|
|
34
|
-
TOKEN=$(echo $LOGIN_RESPONSE | jq -r '.accessToken')
|
|
35
|
-
if [ "$TOKEN" == "null" ] || [ -z "$TOKEN" ]; then
|
|
36
|
-
echo -e "${RED}✗ Failed to login${NC}"
|
|
37
|
-
echo $LOGIN_RESPONSE | jq .
|
|
38
|
-
exit 1
|
|
39
|
-
fi
|
|
40
|
-
echo -e "${GREEN}✓ Logged in successfully${NC}"
|
|
41
|
-
|
|
42
|
-
# Step 2: Create bucket
|
|
43
|
-
echo ""
|
|
44
|
-
echo "2. Creating bucket '$BUCKET_NAME'..."
|
|
45
|
-
CREATE_RESPONSE=$(curl -s -X POST $API_URL/api/storage/buckets \
|
|
46
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
47
|
-
-H 'Content-Type: application/json' \
|
|
48
|
-
-d "{
|
|
49
|
-
\"bucketName\": \"$BUCKET_NAME\",
|
|
50
|
-
\"isPublic\": true
|
|
51
|
-
}")
|
|
52
|
-
|
|
53
|
-
echo $CREATE_RESPONSE | jq .
|
|
54
|
-
|
|
55
|
-
# Step 3: Request upload strategy
|
|
56
|
-
echo ""
|
|
57
|
-
echo "3. Requesting upload strategy (backend decides)..."
|
|
58
|
-
UPLOAD_STRATEGY=$(curl -s -X POST $API_URL/api/storage/buckets/$BUCKET_NAME/upload-strategy \
|
|
59
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
60
|
-
-H 'Content-Type: application/json' \
|
|
61
|
-
-d "{
|
|
62
|
-
\"filename\": \"$TEST_FILE\",
|
|
63
|
-
\"contentType\": \"text/plain\",
|
|
64
|
-
\"size\": $FILE_SIZE
|
|
65
|
-
}")
|
|
66
|
-
|
|
67
|
-
echo -e "${YELLOW}Upload Strategy Response:${NC}"
|
|
68
|
-
echo $UPLOAD_STRATEGY | jq .
|
|
69
|
-
|
|
70
|
-
# Extract strategy details
|
|
71
|
-
METHOD=$(echo $UPLOAD_STRATEGY | jq -r '.method')
|
|
72
|
-
UPLOAD_URL=$(echo $UPLOAD_STRATEGY | jq -r '.uploadUrl')
|
|
73
|
-
KEY=$(echo $UPLOAD_STRATEGY | jq -r '.key')
|
|
74
|
-
CONFIRM_REQUIRED=$(echo $UPLOAD_STRATEGY | jq -r '.confirmRequired')
|
|
75
|
-
|
|
76
|
-
if [ "$METHOD" == "null" ] || [ -z "$METHOD" ]; then
|
|
77
|
-
echo -e "${RED}✗ Failed to get upload strategy${NC}"
|
|
78
|
-
exit 1
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
echo ""
|
|
82
|
-
echo -e "${GREEN}✓ Got upload strategy:${NC}"
|
|
83
|
-
echo " - Method: $METHOD"
|
|
84
|
-
echo " - Upload URL: $UPLOAD_URL"
|
|
85
|
-
echo " - Key: $KEY"
|
|
86
|
-
echo " - Confirm Required: $CONFIRM_REQUIRED"
|
|
87
|
-
|
|
88
|
-
# Step 4: Upload based on strategy
|
|
89
|
-
echo ""
|
|
90
|
-
echo "4. Uploading file using $METHOD method..."
|
|
91
|
-
|
|
92
|
-
if [ "$METHOD" == "presigned" ]; then
|
|
93
|
-
echo "Using presigned URL upload (S3)..."
|
|
94
|
-
|
|
95
|
-
# Build multipart form data with fields from strategy
|
|
96
|
-
FIELDS=$(echo $UPLOAD_STRATEGY | jq -r '.fields')
|
|
97
|
-
FORM_FIELDS=""
|
|
98
|
-
|
|
99
|
-
if [ "$FIELDS" != "null" ] && [ "$FIELDS" != "{}" ]; then
|
|
100
|
-
for field in $(echo $FIELDS | jq -r 'to_entries[] | @base64'); do
|
|
101
|
-
_jq() {
|
|
102
|
-
echo ${field} | base64 --decode | jq -r ${1}
|
|
103
|
-
}
|
|
104
|
-
FIELD_NAME=$(_jq '.key')
|
|
105
|
-
FIELD_VALUE=$(_jq '.value')
|
|
106
|
-
FORM_FIELDS="$FORM_FIELDS -F $FIELD_NAME=$FIELD_VALUE"
|
|
107
|
-
done
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
# Upload to S3
|
|
111
|
-
UPLOAD_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST $UPLOAD_URL \
|
|
112
|
-
$FORM_FIELDS \
|
|
113
|
-
-F "file=@$TEST_FILE")
|
|
114
|
-
|
|
115
|
-
HTTP_CODE=$(echo "$UPLOAD_RESPONSE" | tail -n1)
|
|
116
|
-
|
|
117
|
-
if [ "$HTTP_CODE" -ge "200" ] && [ "$HTTP_CODE" -lt "300" ]; then
|
|
118
|
-
echo -e "${GREEN}✓ File uploaded to S3 (HTTP $HTTP_CODE)${NC}"
|
|
119
|
-
else
|
|
120
|
-
echo -e "${RED}✗ S3 upload failed (HTTP $HTTP_CODE)${NC}"
|
|
121
|
-
echo "Response: $(echo "$UPLOAD_RESPONSE" | head -n-1)"
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
elif [ "$METHOD" == "direct" ]; then
|
|
125
|
-
echo "Using direct upload (Local Storage)..."
|
|
126
|
-
|
|
127
|
-
# For local storage, upload directly to backend
|
|
128
|
-
if [[ "$UPLOAD_URL" == /* ]]; then
|
|
129
|
-
# Relative URL, prepend API_URL
|
|
130
|
-
FULL_URL="${API_URL}${UPLOAD_URL}"
|
|
131
|
-
else
|
|
132
|
-
FULL_URL="$UPLOAD_URL"
|
|
133
|
-
fi
|
|
134
|
-
|
|
135
|
-
UPLOAD_RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT "$FULL_URL" \
|
|
136
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
137
|
-
-F "file=@$TEST_FILE")
|
|
138
|
-
|
|
139
|
-
HTTP_CODE=$(echo "$UPLOAD_RESPONSE" | tail -n1)
|
|
140
|
-
|
|
141
|
-
if [ "$HTTP_CODE" -ge "200" ] && [ "$HTTP_CODE" -lt "300" ]; then
|
|
142
|
-
echo -e "${GREEN}✓ File uploaded directly to backend (HTTP $HTTP_CODE)${NC}"
|
|
143
|
-
else
|
|
144
|
-
echo -e "${RED}✗ Direct upload failed (HTTP $HTTP_CODE)${NC}"
|
|
145
|
-
echo "Response: $(echo "$UPLOAD_RESPONSE" | head -n-1)"
|
|
146
|
-
fi
|
|
147
|
-
else
|
|
148
|
-
echo -e "${RED}✗ Unknown upload method: $METHOD${NC}"
|
|
149
|
-
exit 1
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
# Step 5: Confirm upload if required
|
|
153
|
-
if [ "$CONFIRM_REQUIRED" == "true" ]; then
|
|
154
|
-
echo ""
|
|
155
|
-
echo "5. Confirming upload..."
|
|
156
|
-
|
|
157
|
-
CONFIRM_RESPONSE=$(curl -s -X POST $API_URL/api/storage/buckets/$BUCKET_NAME/objects/$KEY/confirm-upload \
|
|
158
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
159
|
-
-H 'Content-Type: application/json' \
|
|
160
|
-
-d "{
|
|
161
|
-
\"size\": $FILE_SIZE,
|
|
162
|
-
\"contentType\": \"text/plain\"
|
|
163
|
-
}")
|
|
164
|
-
|
|
165
|
-
echo -e "${YELLOW}Confirmation Response:${NC}"
|
|
166
|
-
echo $CONFIRM_RESPONSE | jq .
|
|
167
|
-
|
|
168
|
-
CONFIRM_KEY=$(echo $CONFIRM_RESPONSE | jq -r '.key')
|
|
169
|
-
if [ "$CONFIRM_KEY" == "$KEY" ]; then
|
|
170
|
-
echo -e "${GREEN}✓ Upload confirmed successfully${NC}"
|
|
171
|
-
else
|
|
172
|
-
echo -e "${RED}✗ Upload confirmation failed${NC}"
|
|
173
|
-
fi
|
|
174
|
-
else
|
|
175
|
-
echo ""
|
|
176
|
-
echo "5. No confirmation required for $METHOD upload"
|
|
177
|
-
fi
|
|
178
|
-
|
|
179
|
-
# Step 6: Get download URL
|
|
180
|
-
echo ""
|
|
181
|
-
echo "6. Getting download URL..."
|
|
182
|
-
DOWNLOAD_STRATEGY=$(curl -s -X POST $API_URL/api/storage/buckets/$BUCKET_NAME/objects/$KEY/download-strategy \
|
|
183
|
-
-H "Authorization: Bearer $TOKEN" \
|
|
184
|
-
-H 'Content-Type: application/json' \
|
|
185
|
-
-d '{
|
|
186
|
-
"expiresIn": 3600
|
|
187
|
-
}')
|
|
188
|
-
|
|
189
|
-
echo -e "${YELLOW}Download Strategy Response:${NC}"
|
|
190
|
-
echo $DOWNLOAD_STRATEGY | jq .
|
|
191
|
-
|
|
192
|
-
DOWNLOAD_METHOD=$(echo $DOWNLOAD_STRATEGY | jq -r '.method')
|
|
193
|
-
DOWNLOAD_URL=$(echo $DOWNLOAD_STRATEGY | jq -r '.url')
|
|
194
|
-
EXPIRES_AT=$(echo $DOWNLOAD_STRATEGY | jq -r '.expiresAt')
|
|
195
|
-
|
|
196
|
-
echo ""
|
|
197
|
-
echo -e "${GREEN}✓ Got download strategy:${NC}"
|
|
198
|
-
echo " - Method: $DOWNLOAD_METHOD"
|
|
199
|
-
echo " - URL: $DOWNLOAD_URL"
|
|
200
|
-
if [ "$EXPIRES_AT" != "null" ]; then
|
|
201
|
-
echo " - Expires: $EXPIRES_AT"
|
|
202
|
-
fi
|
|
203
|
-
|
|
204
|
-
# Step 7: Test download
|
|
205
|
-
echo ""
|
|
206
|
-
echo "7. Testing download..."
|
|
207
|
-
|
|
208
|
-
if [ "$DOWNLOAD_METHOD" == "presigned" ]; then
|
|
209
|
-
# Direct download from S3 with presigned URL
|
|
210
|
-
curl -s -o downloaded-$TEST_FILE "$DOWNLOAD_URL"
|
|
211
|
-
elif [ "$DOWNLOAD_METHOD" == "direct" ]; then
|
|
212
|
-
# Download from backend
|
|
213
|
-
if [[ "$DOWNLOAD_URL" == /* ]]; then
|
|
214
|
-
FULL_DOWNLOAD_URL="${API_URL}${DOWNLOAD_URL}"
|
|
215
|
-
else
|
|
216
|
-
FULL_DOWNLOAD_URL="$DOWNLOAD_URL"
|
|
217
|
-
fi
|
|
218
|
-
|
|
219
|
-
curl -s -o downloaded-$TEST_FILE "$FULL_DOWNLOAD_URL" \
|
|
220
|
-
-H "Authorization: Bearer $TOKEN"
|
|
221
|
-
fi
|
|
222
|
-
|
|
223
|
-
if cmp -s "$TEST_FILE" "downloaded-$TEST_FILE"; then
|
|
224
|
-
echo -e "${GREEN}✓ Downloaded file matches original${NC}"
|
|
225
|
-
else
|
|
226
|
-
echo -e "${RED}✗ Downloaded file doesn't match${NC}"
|
|
227
|
-
fi
|
|
228
|
-
|
|
229
|
-
# Step 8: List objects
|
|
230
|
-
echo ""
|
|
231
|
-
echo "8. Listing objects in bucket..."
|
|
232
|
-
LIST_RESPONSE=$(curl -s $API_URL/api/storage/buckets/$BUCKET_NAME/objects \
|
|
233
|
-
-H "Authorization: Bearer $TOKEN")
|
|
234
|
-
|
|
235
|
-
OBJECT_COUNT=$(echo $LIST_RESPONSE | jq '.objects | length')
|
|
236
|
-
echo -e "${GREEN}✓ Found $OBJECT_COUNT object(s) in bucket${NC}"
|
|
237
|
-
|
|
238
|
-
# Cleanup
|
|
239
|
-
echo ""
|
|
240
|
-
echo "9. Cleaning up..."
|
|
241
|
-
|
|
242
|
-
# Delete the uploaded object
|
|
243
|
-
DELETE_RESPONSE=$(curl -s -X DELETE $API_URL/api/storage/buckets/$BUCKET_NAME/objects/$KEY \
|
|
244
|
-
-H "Authorization: Bearer $TOKEN")
|
|
245
|
-
echo " - Deleted object: $KEY"
|
|
246
|
-
|
|
247
|
-
# Delete the bucket
|
|
248
|
-
DELETE_BUCKET_RESPONSE=$(curl -s -X DELETE $API_URL/api/storage/buckets/$BUCKET_NAME \
|
|
249
|
-
-H "Authorization: Bearer $TOKEN")
|
|
250
|
-
echo " - Deleted bucket: $BUCKET_NAME"
|
|
251
|
-
|
|
252
|
-
# Remove test files
|
|
253
|
-
rm -f $TEST_FILE downloaded-$TEST_FILE
|
|
254
|
-
|
|
255
|
-
echo ""
|
|
256
|
-
echo "=== Test Complete ==="
|
|
257
|
-
echo ""
|
|
258
|
-
echo -e "${YELLOW}Summary:${NC}"
|
|
259
|
-
echo "- The backend successfully returned upload/download strategies"
|
|
260
|
-
echo "- Upload method was: $METHOD"
|
|
261
|
-
echo "- Download method was: $DOWNLOAD_METHOD"
|
|
262
|
-
echo "- For S3: Uses presigned URLs (direct to S3)"
|
|
263
|
-
echo "- For Local: Uses direct upload to backend"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Test script for universal storage API with presigned URL support
|
|
4
|
+
# Tests both the "backend decides" approach for S3 and local storage
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
API_URL="http://localhost:7130"
|
|
9
|
+
BUCKET_NAME="test-universal-bucket"
|
|
10
|
+
TEST_FILE="test-upload.txt"
|
|
11
|
+
|
|
12
|
+
# Colors for output
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
NC='\033[0m' # No Color
|
|
17
|
+
|
|
18
|
+
echo "=== Universal Storage API Test ==="
|
|
19
|
+
echo ""
|
|
20
|
+
|
|
21
|
+
# Create test file
|
|
22
|
+
echo "This is a test file for universal storage upload" > $TEST_FILE
|
|
23
|
+
FILE_SIZE=$(stat -f%z "$TEST_FILE" 2>/dev/null || stat -c%s "$TEST_FILE" 2>/dev/null)
|
|
24
|
+
|
|
25
|
+
# Step 1: Login to get auth token
|
|
26
|
+
echo "1. Logging in..."
|
|
27
|
+
LOGIN_RESPONSE=$(curl -s -X POST $API_URL/api/auth/admin/sessions \
|
|
28
|
+
-H 'Content-Type: application/json' \
|
|
29
|
+
-d '{
|
|
30
|
+
"email": "admin@example.com",
|
|
31
|
+
"password": "change-this-password"
|
|
32
|
+
}')
|
|
33
|
+
|
|
34
|
+
TOKEN=$(echo $LOGIN_RESPONSE | jq -r '.accessToken')
|
|
35
|
+
if [ "$TOKEN" == "null" ] || [ -z "$TOKEN" ]; then
|
|
36
|
+
echo -e "${RED}✗ Failed to login${NC}"
|
|
37
|
+
echo $LOGIN_RESPONSE | jq .
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
echo -e "${GREEN}✓ Logged in successfully${NC}"
|
|
41
|
+
|
|
42
|
+
# Step 2: Create bucket
|
|
43
|
+
echo ""
|
|
44
|
+
echo "2. Creating bucket '$BUCKET_NAME'..."
|
|
45
|
+
CREATE_RESPONSE=$(curl -s -X POST $API_URL/api/storage/buckets \
|
|
46
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
47
|
+
-H 'Content-Type: application/json' \
|
|
48
|
+
-d "{
|
|
49
|
+
\"bucketName\": \"$BUCKET_NAME\",
|
|
50
|
+
\"isPublic\": true
|
|
51
|
+
}")
|
|
52
|
+
|
|
53
|
+
echo $CREATE_RESPONSE | jq .
|
|
54
|
+
|
|
55
|
+
# Step 3: Request upload strategy
|
|
56
|
+
echo ""
|
|
57
|
+
echo "3. Requesting upload strategy (backend decides)..."
|
|
58
|
+
UPLOAD_STRATEGY=$(curl -s -X POST $API_URL/api/storage/buckets/$BUCKET_NAME/upload-strategy \
|
|
59
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
60
|
+
-H 'Content-Type: application/json' \
|
|
61
|
+
-d "{
|
|
62
|
+
\"filename\": \"$TEST_FILE\",
|
|
63
|
+
\"contentType\": \"text/plain\",
|
|
64
|
+
\"size\": $FILE_SIZE
|
|
65
|
+
}")
|
|
66
|
+
|
|
67
|
+
echo -e "${YELLOW}Upload Strategy Response:${NC}"
|
|
68
|
+
echo $UPLOAD_STRATEGY | jq .
|
|
69
|
+
|
|
70
|
+
# Extract strategy details
|
|
71
|
+
METHOD=$(echo $UPLOAD_STRATEGY | jq -r '.method')
|
|
72
|
+
UPLOAD_URL=$(echo $UPLOAD_STRATEGY | jq -r '.uploadUrl')
|
|
73
|
+
KEY=$(echo $UPLOAD_STRATEGY | jq -r '.key')
|
|
74
|
+
CONFIRM_REQUIRED=$(echo $UPLOAD_STRATEGY | jq -r '.confirmRequired')
|
|
75
|
+
|
|
76
|
+
if [ "$METHOD" == "null" ] || [ -z "$METHOD" ]; then
|
|
77
|
+
echo -e "${RED}✗ Failed to get upload strategy${NC}"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
echo ""
|
|
82
|
+
echo -e "${GREEN}✓ Got upload strategy:${NC}"
|
|
83
|
+
echo " - Method: $METHOD"
|
|
84
|
+
echo " - Upload URL: $UPLOAD_URL"
|
|
85
|
+
echo " - Key: $KEY"
|
|
86
|
+
echo " - Confirm Required: $CONFIRM_REQUIRED"
|
|
87
|
+
|
|
88
|
+
# Step 4: Upload based on strategy
|
|
89
|
+
echo ""
|
|
90
|
+
echo "4. Uploading file using $METHOD method..."
|
|
91
|
+
|
|
92
|
+
if [ "$METHOD" == "presigned" ]; then
|
|
93
|
+
echo "Using presigned URL upload (S3)..."
|
|
94
|
+
|
|
95
|
+
# Build multipart form data with fields from strategy
|
|
96
|
+
FIELDS=$(echo $UPLOAD_STRATEGY | jq -r '.fields')
|
|
97
|
+
FORM_FIELDS=""
|
|
98
|
+
|
|
99
|
+
if [ "$FIELDS" != "null" ] && [ "$FIELDS" != "{}" ]; then
|
|
100
|
+
for field in $(echo $FIELDS | jq -r 'to_entries[] | @base64'); do
|
|
101
|
+
_jq() {
|
|
102
|
+
echo ${field} | base64 --decode | jq -r ${1}
|
|
103
|
+
}
|
|
104
|
+
FIELD_NAME=$(_jq '.key')
|
|
105
|
+
FIELD_VALUE=$(_jq '.value')
|
|
106
|
+
FORM_FIELDS="$FORM_FIELDS -F $FIELD_NAME=$FIELD_VALUE"
|
|
107
|
+
done
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# Upload to S3
|
|
111
|
+
UPLOAD_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST $UPLOAD_URL \
|
|
112
|
+
$FORM_FIELDS \
|
|
113
|
+
-F "file=@$TEST_FILE")
|
|
114
|
+
|
|
115
|
+
HTTP_CODE=$(echo "$UPLOAD_RESPONSE" | tail -n1)
|
|
116
|
+
|
|
117
|
+
if [ "$HTTP_CODE" -ge "200" ] && [ "$HTTP_CODE" -lt "300" ]; then
|
|
118
|
+
echo -e "${GREEN}✓ File uploaded to S3 (HTTP $HTTP_CODE)${NC}"
|
|
119
|
+
else
|
|
120
|
+
echo -e "${RED}✗ S3 upload failed (HTTP $HTTP_CODE)${NC}"
|
|
121
|
+
echo "Response: $(echo "$UPLOAD_RESPONSE" | head -n-1)"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
elif [ "$METHOD" == "direct" ]; then
|
|
125
|
+
echo "Using direct upload (Local Storage)..."
|
|
126
|
+
|
|
127
|
+
# For local storage, upload directly to backend
|
|
128
|
+
if [[ "$UPLOAD_URL" == /* ]]; then
|
|
129
|
+
# Relative URL, prepend API_URL
|
|
130
|
+
FULL_URL="${API_URL}${UPLOAD_URL}"
|
|
131
|
+
else
|
|
132
|
+
FULL_URL="$UPLOAD_URL"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
UPLOAD_RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT "$FULL_URL" \
|
|
136
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
137
|
+
-F "file=@$TEST_FILE")
|
|
138
|
+
|
|
139
|
+
HTTP_CODE=$(echo "$UPLOAD_RESPONSE" | tail -n1)
|
|
140
|
+
|
|
141
|
+
if [ "$HTTP_CODE" -ge "200" ] && [ "$HTTP_CODE" -lt "300" ]; then
|
|
142
|
+
echo -e "${GREEN}✓ File uploaded directly to backend (HTTP $HTTP_CODE)${NC}"
|
|
143
|
+
else
|
|
144
|
+
echo -e "${RED}✗ Direct upload failed (HTTP $HTTP_CODE)${NC}"
|
|
145
|
+
echo "Response: $(echo "$UPLOAD_RESPONSE" | head -n-1)"
|
|
146
|
+
fi
|
|
147
|
+
else
|
|
148
|
+
echo -e "${RED}✗ Unknown upload method: $METHOD${NC}"
|
|
149
|
+
exit 1
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Step 5: Confirm upload if required
|
|
153
|
+
if [ "$CONFIRM_REQUIRED" == "true" ]; then
|
|
154
|
+
echo ""
|
|
155
|
+
echo "5. Confirming upload..."
|
|
156
|
+
|
|
157
|
+
CONFIRM_RESPONSE=$(curl -s -X POST $API_URL/api/storage/buckets/$BUCKET_NAME/objects/$KEY/confirm-upload \
|
|
158
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
159
|
+
-H 'Content-Type: application/json' \
|
|
160
|
+
-d "{
|
|
161
|
+
\"size\": $FILE_SIZE,
|
|
162
|
+
\"contentType\": \"text/plain\"
|
|
163
|
+
}")
|
|
164
|
+
|
|
165
|
+
echo -e "${YELLOW}Confirmation Response:${NC}"
|
|
166
|
+
echo $CONFIRM_RESPONSE | jq .
|
|
167
|
+
|
|
168
|
+
CONFIRM_KEY=$(echo $CONFIRM_RESPONSE | jq -r '.key')
|
|
169
|
+
if [ "$CONFIRM_KEY" == "$KEY" ]; then
|
|
170
|
+
echo -e "${GREEN}✓ Upload confirmed successfully${NC}"
|
|
171
|
+
else
|
|
172
|
+
echo -e "${RED}✗ Upload confirmation failed${NC}"
|
|
173
|
+
fi
|
|
174
|
+
else
|
|
175
|
+
echo ""
|
|
176
|
+
echo "5. No confirmation required for $METHOD upload"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Step 6: Get download URL
|
|
180
|
+
echo ""
|
|
181
|
+
echo "6. Getting download URL..."
|
|
182
|
+
DOWNLOAD_STRATEGY=$(curl -s -X POST $API_URL/api/storage/buckets/$BUCKET_NAME/objects/$KEY/download-strategy \
|
|
183
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
184
|
+
-H 'Content-Type: application/json' \
|
|
185
|
+
-d '{
|
|
186
|
+
"expiresIn": 3600
|
|
187
|
+
}')
|
|
188
|
+
|
|
189
|
+
echo -e "${YELLOW}Download Strategy Response:${NC}"
|
|
190
|
+
echo $DOWNLOAD_STRATEGY | jq .
|
|
191
|
+
|
|
192
|
+
DOWNLOAD_METHOD=$(echo $DOWNLOAD_STRATEGY | jq -r '.method')
|
|
193
|
+
DOWNLOAD_URL=$(echo $DOWNLOAD_STRATEGY | jq -r '.url')
|
|
194
|
+
EXPIRES_AT=$(echo $DOWNLOAD_STRATEGY | jq -r '.expiresAt')
|
|
195
|
+
|
|
196
|
+
echo ""
|
|
197
|
+
echo -e "${GREEN}✓ Got download strategy:${NC}"
|
|
198
|
+
echo " - Method: $DOWNLOAD_METHOD"
|
|
199
|
+
echo " - URL: $DOWNLOAD_URL"
|
|
200
|
+
if [ "$EXPIRES_AT" != "null" ]; then
|
|
201
|
+
echo " - Expires: $EXPIRES_AT"
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
# Step 7: Test download
|
|
205
|
+
echo ""
|
|
206
|
+
echo "7. Testing download..."
|
|
207
|
+
|
|
208
|
+
if [ "$DOWNLOAD_METHOD" == "presigned" ]; then
|
|
209
|
+
# Direct download from S3 with presigned URL
|
|
210
|
+
curl -s -o downloaded-$TEST_FILE "$DOWNLOAD_URL"
|
|
211
|
+
elif [ "$DOWNLOAD_METHOD" == "direct" ]; then
|
|
212
|
+
# Download from backend
|
|
213
|
+
if [[ "$DOWNLOAD_URL" == /* ]]; then
|
|
214
|
+
FULL_DOWNLOAD_URL="${API_URL}${DOWNLOAD_URL}"
|
|
215
|
+
else
|
|
216
|
+
FULL_DOWNLOAD_URL="$DOWNLOAD_URL"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
curl -s -o downloaded-$TEST_FILE "$FULL_DOWNLOAD_URL" \
|
|
220
|
+
-H "Authorization: Bearer $TOKEN"
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
if cmp -s "$TEST_FILE" "downloaded-$TEST_FILE"; then
|
|
224
|
+
echo -e "${GREEN}✓ Downloaded file matches original${NC}"
|
|
225
|
+
else
|
|
226
|
+
echo -e "${RED}✗ Downloaded file doesn't match${NC}"
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# Step 8: List objects
|
|
230
|
+
echo ""
|
|
231
|
+
echo "8. Listing objects in bucket..."
|
|
232
|
+
LIST_RESPONSE=$(curl -s $API_URL/api/storage/buckets/$BUCKET_NAME/objects \
|
|
233
|
+
-H "Authorization: Bearer $TOKEN")
|
|
234
|
+
|
|
235
|
+
OBJECT_COUNT=$(echo $LIST_RESPONSE | jq '.objects | length')
|
|
236
|
+
echo -e "${GREEN}✓ Found $OBJECT_COUNT object(s) in bucket${NC}"
|
|
237
|
+
|
|
238
|
+
# Cleanup
|
|
239
|
+
echo ""
|
|
240
|
+
echo "9. Cleaning up..."
|
|
241
|
+
|
|
242
|
+
# Delete the uploaded object
|
|
243
|
+
DELETE_RESPONSE=$(curl -s -X DELETE $API_URL/api/storage/buckets/$BUCKET_NAME/objects/$KEY \
|
|
244
|
+
-H "Authorization: Bearer $TOKEN")
|
|
245
|
+
echo " - Deleted object: $KEY"
|
|
246
|
+
|
|
247
|
+
# Delete the bucket
|
|
248
|
+
DELETE_BUCKET_RESPONSE=$(curl -s -X DELETE $API_URL/api/storage/buckets/$BUCKET_NAME \
|
|
249
|
+
-H "Authorization: Bearer $TOKEN")
|
|
250
|
+
echo " - Deleted bucket: $BUCKET_NAME"
|
|
251
|
+
|
|
252
|
+
# Remove test files
|
|
253
|
+
rm -f $TEST_FILE downloaded-$TEST_FILE
|
|
254
|
+
|
|
255
|
+
echo ""
|
|
256
|
+
echo "=== Test Complete ==="
|
|
257
|
+
echo ""
|
|
258
|
+
echo -e "${YELLOW}Summary:${NC}"
|
|
259
|
+
echo "- The backend successfully returned upload/download strategies"
|
|
260
|
+
echo "- Upload method was: $METHOD"
|
|
261
|
+
echo "- Download method was: $DOWNLOAD_METHOD"
|
|
262
|
+
echo "- For S3: Uses presigned URLs (direct to S3)"
|
|
263
|
+
echo "- For Local: Uses direct upload to backend"
|
|
264
264
|
echo "- The SDK can use the same code for both backends!"
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
-- Table: test_users2
|
|
2
|
-
CREATE TABLE IF NOT EXISTS test_users (
|
|
3
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
4
|
-
nickname text,
|
|
5
|
-
avatar_url text,
|
|
6
|
-
birthday date,
|
|
7
|
-
extra jsonb,
|
|
8
|
-
created_at timestamp with time zone DEFAULT now(),
|
|
9
|
-
updated_at timestamp with time zone DEFAULT now()
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
CREATE POLICY "Enable read access for all users" ON test_users FOR SELECT TO public USING (true);
|
|
13
|
-
CREATE POLICY "Enable update for users based on user_id" ON test_users FOR UPDATE TO authenticated USING ((uid() = id));
|
|
14
|
-
|
|
15
|
-
-- Sample data for test_users
|
|
16
|
-
INSERT INTO test_users (nickname, avatar_url, birthday) VALUES
|
|
17
|
-
('John Doe', 'https://example.com/avatar1.jpg', '1990-01-01'),
|
|
1
|
+
-- Table: test_users2
|
|
2
|
+
CREATE TABLE IF NOT EXISTS test_users (
|
|
3
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
4
|
+
nickname text,
|
|
5
|
+
avatar_url text,
|
|
6
|
+
birthday date,
|
|
7
|
+
extra jsonb,
|
|
8
|
+
created_at timestamp with time zone DEFAULT now(),
|
|
9
|
+
updated_at timestamp with time zone DEFAULT now()
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
CREATE POLICY "Enable read access for all users" ON test_users FOR SELECT TO public USING (true);
|
|
13
|
+
CREATE POLICY "Enable update for users based on user_id" ON test_users FOR UPDATE TO authenticated USING ((uid() = id));
|
|
14
|
+
|
|
15
|
+
-- Sample data for test_users
|
|
16
|
+
INSERT INTO test_users (nickname, avatar_url, birthday) VALUES
|
|
17
|
+
('John Doe', 'https://example.com/avatar1.jpg', '1990-01-01'),
|
|
18
18
|
('Jane Smith', 'https://example.com/avatar2.jpg', '1985-05-15');
|