mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: azurite and refactoring
This commit is contained in:
@@ -46,3 +46,11 @@ export-keycloak:
|
|||||||
@docker rm -f kc-exporter >/dev/null 2>&1
|
@docker rm -f kc-exporter >/dev/null 2>&1
|
||||||
@docker compose -f docker-compose.func.yml start keycloak >/dev/null 2>&1
|
@docker compose -f docker-compose.func.yml start keycloak >/dev/null 2>&1
|
||||||
@echo "Keycloak configuration and users exported to seeds/keycloak/*.json"
|
@echo "Keycloak configuration and users exported to seeds/keycloak/*.json"
|
||||||
|
|
||||||
|
seed-blob:
|
||||||
|
@chmod +x seeds/azurite/azurite-seed.sh
|
||||||
|
@bash seeds/azurite/azurite-seed.sh
|
||||||
|
|
||||||
|
list-blob:
|
||||||
|
@chmod +x seeds/azurite/azurite-list.sh
|
||||||
|
@bash seeds/azurite/azurite-list.sh
|
||||||
@@ -41,5 +41,29 @@ services:
|
|||||||
- "8025:8025"
|
- "8025:8025"
|
||||||
- "1025:1025"
|
- "1025:1025"
|
||||||
|
|
||||||
|
azurite:
|
||||||
|
image: mcr.microsoft.com/azure-storage/azurite
|
||||||
|
command: azurite --blobHost 0.0.0.0 --skipApiVersionCheck
|
||||||
|
ports:
|
||||||
|
- "10000:10000"
|
||||||
|
volumes:
|
||||||
|
- azurite-data:/data
|
||||||
|
extra_hosts:
|
||||||
|
- "localhost:host-gateway"
|
||||||
|
networks:
|
||||||
|
- portabase
|
||||||
|
# DefaultEndpointsProtocol=http;
|
||||||
|
# AccountName=devstoreaccount1;
|
||||||
|
# AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;
|
||||||
|
# BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;
|
||||||
|
# QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;
|
||||||
|
# TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres-data:
|
postgres-data:
|
||||||
|
azurite-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
portabase:
|
||||||
|
name: portabase_network
|
||||||
|
external: true
|
||||||
|
|||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# azurite-list.sh — enumerate every container and blob in a local Azurite emulator.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./azurite-list.sh # list containers + blobs
|
||||||
|
# ./azurite-list.sh -v # also print blob size, last-modified, content-type
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;"
|
||||||
|
|
||||||
|
VERBOSE=0
|
||||||
|
[[ "${1:-}" == "-v" || "${1:-}" == "--verbose" ]] && VERBOSE=1
|
||||||
|
|
||||||
|
command -v az >/dev/null 2>&1 || { echo "ERROR: 'az' CLI not found in PATH." >&2; exit 1; }
|
||||||
|
command -v jq >/dev/null 2>&1 || { echo "ERROR: 'jq' not found in PATH." >&2; exit 1; }
|
||||||
|
|
||||||
|
if ! az storage container list --num-results 1 >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: Cannot reach Azurite at 127.0.0.1:10000. Is the container running?" >&2
|
||||||
|
echo " Start it with: docker start azurite" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
containers=$(az storage container list --query "[].name" -o tsv)
|
||||||
|
|
||||||
|
if [[ -z "$containers" ]]; then
|
||||||
|
echo "(no containers found in this emulator)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r container; do
|
||||||
|
echo "📦 container: $container"
|
||||||
|
|
||||||
|
if [[ "$VERBOSE" -eq 1 ]]; then
|
||||||
|
az storage blob list --container-name "$container" \
|
||||||
|
--query "[].{name:name, bytes:properties.contentLength, modified:properties.lastModified, type:properties.contentSettings.contentType}" \
|
||||||
|
-o json \
|
||||||
|
| jq -r '.[] | " • \(.name) [\(.bytes) bytes] \(.type // "?") \(.modified)"'
|
||||||
|
else
|
||||||
|
az storage blob list --container-name "$container" \
|
||||||
|
--query "[].{name:name, bytes:properties.contentLength}" \
|
||||||
|
-o json \
|
||||||
|
| jq -r '.[] | " • \(.name) [\(.bytes) bytes]"'
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=$(az storage blob list --container-name "$container" --query "length(@)" -o tsv)
|
||||||
|
[[ "$count" -eq 0 ]] && echo " (empty)"
|
||||||
|
echo
|
||||||
|
done <<< "$containers"
|
||||||
Executable
+46
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# azurite-seed.sh — seed a local Azurite emulator with a container + sample blob.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./azurite-seed.sh # create container "portabase" + sample blob
|
||||||
|
# ./azurite-seed.sh my-container # override container name
|
||||||
|
#
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;"
|
||||||
|
|
||||||
|
CONTAINER="${1:-portabase}"
|
||||||
|
BLOB_NAME="hello.txt"
|
||||||
|
|
||||||
|
command -v az >/dev/null 2>&1 || { echo "ERROR: 'az' CLI not found in PATH." >&2; exit 1; }
|
||||||
|
|
||||||
|
# Reachability check — surface the real az error instead of guessing "container down".
|
||||||
|
if ! err=$(az storage container list --num-results 1 2>&1 >/dev/null); then
|
||||||
|
echo "ERROR: Azurite query failed:" >&2
|
||||||
|
printf '%s\n' "$err" | sed 's/^/ /' >&2
|
||||||
|
echo " Is Azurite up? Start it with: docker compose up -d azurite" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create container (idempotent — az returns created:false if it already exists).
|
||||||
|
az storage container create --name "$CONTAINER" -o none
|
||||||
|
echo "📦 container ready: $CONTAINER"
|
||||||
|
|
||||||
|
# Upload a sample blob from a temp file.
|
||||||
|
tmp=$(mktemp)
|
||||||
|
trap 'rm -f "$tmp"' EXIT
|
||||||
|
printf 'hello from azurite seed — %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$tmp"
|
||||||
|
|
||||||
|
az storage blob upload \
|
||||||
|
--container-name "$CONTAINER" \
|
||||||
|
--name "$BLOB_NAME" \
|
||||||
|
--file "$tmp" \
|
||||||
|
--overwrite true \
|
||||||
|
--no-progress \
|
||||||
|
-o none
|
||||||
|
echo " • uploaded blob: $BLOB_NAME"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Seed complete. Verify with: make list-blob"
|
||||||
@@ -10,9 +10,9 @@ import {NextcloudChannelConfigSchema} from "@/features/channel/notifications/nex
|
|||||||
import {PushoverChannelConfigSchema} from "@/features/channel/notifications/pushover.schema";
|
import {PushoverChannelConfigSchema} from "@/features/channel/notifications/pushover.schema";
|
||||||
import {S3ChannelConfigSchema} from "@/features/channel/storages/s3.schema";
|
import {S3ChannelConfigSchema} from "@/features/channel/storages/s3.schema";
|
||||||
import {GoogleDriveChannelConfigSchema} from "@/features/channel/storages/google-drive/google-drive.schema";
|
import {GoogleDriveChannelConfigSchema} from "@/features/channel/storages/google-drive/google-drive.schema";
|
||||||
import { BlobChannelConfigSchema } from "@/features/channel/storages/Blob.schema";
|
|
||||||
import {LocalChannelConfigSchema} from "@/features/channel/storages/local.schema";
|
import {LocalChannelConfigSchema} from "@/features/channel/storages/local.schema";
|
||||||
import {TeamsChannelConfigSchema} from "@/features/channel/notifications/teams.schema";
|
import {TeamsChannelConfigSchema} from "@/features/channel/notifications/teams.schema";
|
||||||
|
import {BlobChannelConfigSchema} from "@/features/channel/storages/az-blob.schema";
|
||||||
|
|
||||||
|
|
||||||
const BaseChannelFormSchema = z.object({
|
const BaseChannelFormSchema = z.object({
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import {
|
|||||||
} from "@/features/channel/storages/google-drive/google-drive.form";
|
} from "@/features/channel/storages/google-drive/google-drive.form";
|
||||||
import {
|
import {
|
||||||
StorageBlobForm
|
StorageBlobForm
|
||||||
} from "@/features/channel/storages/Blob.form";
|
} from "@/features/channel/storages/az-blob.form";
|
||||||
|
|
||||||
export type ChannelKind = "notification" | "storage";
|
export type ChannelKind = "notification" | "storage";
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -5,7 +5,10 @@ export const BlobChannelConfigSchema = z.object({
|
|||||||
accountKey: z.string().optional(),
|
accountKey: z.string().optional(),
|
||||||
connectionString: z.string().optional(),
|
connectionString: z.string().optional(),
|
||||||
containerName: z.string().min(1, "Container name is required"),
|
containerName: z.string().min(1, "Container name is required"),
|
||||||
endpointUrl: z.string().url("Endpoint URL must be a valid URL").optional(),
|
endpointUrl: z.preprocess(
|
||||||
|
(v) => (v === "" ? undefined : v),
|
||||||
|
z.string().url("Endpoint URL must be a valid URL").optional(),
|
||||||
|
),
|
||||||
}).refine(
|
}).refine(
|
||||||
(data) => data.accountKey || data.connectionString,
|
(data) => data.accountKey || data.connectionString,
|
||||||
{message: "Either account key or connection string is required"}
|
{message: "Either account key or connection string is required"}
|
||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
|
|
||||||
import {uploadLocal, getLocal, deleteLocal, pingLocal, copyLocal} from './local';
|
import {uploadLocal, getLocal, deleteLocal, pingLocal, copyLocal} from './local';
|
||||||
import {copyS3, deleteS3, getS3, pingS3, uploadS3} from "@/features/channel/storages/s3";
|
import {copyS3, deleteS3, getS3, pingS3, uploadS3} from "@/features/channel/storages/s3";
|
||||||
import {copyBlob, deleteBlob, getBlob, pingBlob, uploadBlob} from "@/features/channel/storages/Blob";
|
import {copyBlob, deleteBlob, getBlob, pingBlob, uploadBlob} from "@/features/channel/storages/az-blob";
|
||||||
import {
|
import {
|
||||||
copyGoogleDrive,
|
copyGoogleDrive,
|
||||||
deleteGoogleDrive,
|
deleteGoogleDrive,
|
||||||
|
|||||||
Reference in New Issue
Block a user