fix: azurite and refactoring

This commit is contained in:
Charles GTE
2026-06-20 19:35:40 +02:00
parent 3018515abf
commit baab060628
10 changed files with 137 additions and 5 deletions
+8
View File
@@ -46,3 +46,11 @@ export-keycloak:
@docker rm -f kc-exporter >/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"
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
+24
View File
@@ -41,5 +41,29 @@ services:
- "8025:8025"
- "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:
postgres-data:
azurite-data:
networks:
portabase:
name: portabase_network
external: true
+51
View File
@@ -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"
+46
View File
@@ -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"
+1 -1
View File
@@ -10,9 +10,9 @@ import {NextcloudChannelConfigSchema} from "@/features/channel/notifications/nex
import {PushoverChannelConfigSchema} from "@/features/channel/notifications/pushover.schema";
import {S3ChannelConfigSchema} from "@/features/channel/storages/s3.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 {TeamsChannelConfigSchema} from "@/features/channel/notifications/teams.schema";
import {BlobChannelConfigSchema} from "@/features/channel/storages/az-blob.schema";
const BaseChannelFormSchema = z.object({
+1 -1
View File
@@ -43,7 +43,7 @@ import {
} from "@/features/channel/storages/google-drive/google-drive.form";
import {
StorageBlobForm
} from "@/features/channel/storages/Blob.form";
} from "@/features/channel/storages/az-blob.form";
export type ChannelKind = "notification" | "storage";
@@ -5,7 +5,10 @@ export const BlobChannelConfigSchema = z.object({
accountKey: z.string().optional(),
connectionString: z.string().optional(),
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(
(data) => data.accountKey || data.connectionString,
{message: "Either account key or connection string is required"}
+1 -1
View File
@@ -6,7 +6,7 @@ import {
import {uploadLocal, getLocal, deleteLocal, pingLocal, copyLocal} from './local';
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 {
copyGoogleDrive,
deleteGoogleDrive,