diff --git a/README.md b/README.md
index dbe5b902..4b09226e 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,16 @@ There are a couple of ways to run this project depending on if you have a .NET I
### Environment Specific Steps
+Local media uses SeaweedFS. With Docker Compose installed, run from the repo root:
+
+```sh
+docker compose up -d
+```
+
+The local bucket is created automatically at `http://localhost:8333`.
+The development launch profiles supply local-only credentials.
+Media persists across restarts; `docker compose down --volumes` deletes it.
+
- Create an `Umbraco.sqlite.db` file in the `./SgfDevs/umbraco/Data` directory
- Mac OS/Linux `mkdir -p ./SgfDevs/umbraco/Data && touch ./SgfDevs/umbraco/Data/Umbraco.sqlite.db`
- Windows `New-Item -ItemType Directory -Force -Path .\SgfDevs\umbraco\Data; New-Item -ItemType File -Force -Path .\SgfDevs\umbraco\Data\Umbraco.sqlite.db`
@@ -20,7 +30,7 @@ There are a couple of ways to run this project depending on if you have a .NET I
- Navigate to the SgfDevs project folder `cd SgfDevs`
- Use the `dotnet user-secrets` command to set your connection string
- `dotnet user-secrets set "ConnectionStrings:umbracoDbDSN" "Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True;Default Timeout=60"`
-- `dotnet run`
+- `dotnet run --launch-profile Umbraco.Web.UI`
- Open the URL that's printed in the console in your browser
#### .NET IDE e.g. Rider or Visual Studio (Windows)
diff --git a/SgfDevs.Tests/appsettings-schema.Umbraco.StorageProviders.AzureBlob.json b/SgfDevs.Tests/appsettings-schema.Umbraco.StorageProviders.AzureBlob.json
deleted file mode 100755
index 96bd03f3..00000000
--- a/SgfDevs.Tests/appsettings-schema.Umbraco.StorageProviders.AzureBlob.json
+++ /dev/null
@@ -1,170 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-04/schema#",
- "title": "UmbracoStorageProvidersAzureBlobSchema",
- "type": "object",
- "description": "Defines the appsettings JSON schema for Umbraco Storage Providers - Azure Blob Storage configuration.",
- "properties": {
- "Umbraco": {
- "description": "Configuration container for all Umbraco products.",
- "oneOf": [
- {
- "$ref": "#/definitions/UmbracoDefinition"
- }
- ]
- }
- },
- "definitions": {
- "UmbracoDefinition": {
- "type": "object",
- "description": "Represents the configuration container for all Umbraco products.",
- "properties": {
- "Storage": {
- "description": "Configuration of Umbraco Storage Providers.",
- "oneOf": [
- {
- "$ref": "#/definitions/StorageDefinition"
- }
- ]
- }
- }
- },
- "StorageDefinition": {
- "type": "object",
- "description": "Represents the configuration of Umbraco Storage Providers.",
- "properties": {
- "AzureBlob": {
- "type": "object",
- "description": "Configuration of Umbraco Storage Providers - Azure Blob Storage, keyed by file system name (e.g. \"Media\").",
- "additionalProperties": {
- "$ref": "#/definitions/AzureBlobFileSystemOptions"
- }
- }
- }
- },
- "AzureBlobFileSystemOptions": {
- "type": "object",
- "description": "The Azure Blob File System options.",
- "required": [
- "ConnectionString",
- "ContainerName",
- "VirtualPath"
- ],
- "properties": {
- "ConnectionString": {
- "type": "string",
- "description": "Gets or sets the storage account connection string.",
- "minLength": 1
- },
- "ContainerName": {
- "type": "string",
- "description": "Gets or sets the container name.",
- "minLength": 1
- },
- "ContainerRootPath": {
- "type": [
- "null",
- "string"
- ],
- "description": "Gets or sets the root path of the container."
- },
- "VirtualPath": {
- "type": "string",
- "description": "Gets or sets the virtual path.",
- "minLength": 1
- },
- "Cache": {
- "description": "Gets or sets the in-memory cache settings applied to blob metadata lookups by the read-only file provider.",
- "oneOf": [
- {
- "$ref": "#/definitions/AzureBlobFileSystemCacheOptions"
- }
- ]
- },
- "Retry": {
- "description": "Gets or sets the retry and timeout settings applied to the default Blob client.",
- "oneOf": [
- {
- "$ref": "#/definitions/AzureBlobFileSystemRetryOptions"
- }
- ]
- }
- }
- },
- "AzureBlobFileSystemCacheOptions": {
- "type": "object",
- "description": "Cache settings applied to blob metadata lookups performed by the read-only file provider.",
- "properties": {
- "Enabled": {
- "type": "boolean",
- "description": "Gets or sets a value indicating whether blob metadata caching is enabled.",
- "default": true
- },
- "HitDuration": {
- "type": "string",
- "description": "Gets or sets how long a successful metadata lookup is cached.",
- "format": "duration",
- "default": "00:00:30"
- },
- "MissDuration": {
- "type": "string",
- "description": "Gets or sets how long a not-found metadata result is cached.",
- "format": "duration",
- "default": "00:00:05"
- }
- }
- },
- "AzureBlobFileSystemRetryOptions": {
- "type": "object",
- "description": "Retry and timeout settings applied to the default BlobContainerClient created from AzureBlobFileSystemOptions.",
- "properties": {
- "MaxRetries": {
- "type": "integer",
- "description": "Gets or sets the maximum number of retry attempts before giving up.",
- "format": "int32",
- "default": 2,
- "maximum": 2147483647.0,
- "minimum": 0.0
- },
- "NetworkTimeout": {
- "type": "string",
- "description": "Gets or sets the timeout applied to an individual network operation.",
- "format": "duration",
- "default": "00:00:30"
- },
- "Mode": {
- "description": "Gets or sets the approach to use for calculating retry delays.",
- "default": "Exponential",
- "oneOf": [
- {
- "$ref": "#/definitions/RetryMode"
- }
- ]
- },
- "Delay": {
- "type": "string",
- "description": "Gets or sets the delay between retry attempts for a fixed approach or the delay on which to base\ncalculations for a backoff-based approach.",
- "format": "duration",
- "default": "00:00:00.8000000"
- },
- "MaxDelay": {
- "type": "string",
- "description": "Gets or sets the maximum permissible delay between retry attempts when using a backoff approach.",
- "format": "duration",
- "default": "00:00:05"
- }
- }
- },
- "RetryMode": {
- "type": "string",
- "description": "",
- "x-enumNames": [
- "Fixed",
- "Exponential"
- ],
- "enum": [
- "Fixed",
- "Exponential"
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/SgfDevs.Tests/appsettings-schema.json b/SgfDevs.Tests/appsettings-schema.json
index dff39b51..0acb60fb 100644
--- a/SgfDevs.Tests/appsettings-schema.json
+++ b/SgfDevs.Tests/appsettings-schema.json
@@ -12,9 +12,6 @@
},
{
"$ref": "appsettings-schema.Umbraco.StorageProviders.json#"
- },
- {
- "$ref": "appsettings-schema.Umbraco.StorageProviders.AzureBlob.json#"
}
]
}
\ No newline at end of file
diff --git a/SgfDevs/Program.cs b/SgfDevs/Program.cs
index ae6a10de..1b935544 100644
--- a/SgfDevs/Program.cs
+++ b/SgfDevs/Program.cs
@@ -32,16 +32,6 @@
.AddDeliveryApi()
.AddComposers();
-var blobStorageKey = builder.Configuration["SGFDevs:AzureBlobStorageKey"];
-if (!string.IsNullOrEmpty(blobStorageKey))
-{
- umbracoBuilder.AddAzureBlobMediaFileSystem(options =>
- {
- options.ConnectionString = $"DefaultEndpointsProtocol=https;AccountName=sgfdevs;AccountKey={blobStorageKey};EndpointSuffix=core.windows.net";
- options.ContainerName = "website";
- });
-}
-
if (!string.IsNullOrEmpty(builder.Configuration["Umbraco:Storage:Cdn:Url"]))
{
umbracoBuilder.AddCdnMediaUrlProvider();
diff --git a/SgfDevs/Properties/launchSettings.json b/SgfDevs/Properties/launchSettings.json
index f22e43ac..5378773e 100644
--- a/SgfDevs/Properties/launchSettings.json
+++ b/SgfDevs/Properties/launchSettings.json
@@ -4,14 +4,20 @@
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "AWS_ACCESS_KEY_ID": "sgf-dev-local",
+ "AWS_SECRET_ACCESS_KEY": "sgf-dev-local-password",
+ "AWS_SESSION_TOKEN": ""
}
},
"Umbraco.Web.UI": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "AWS_ACCESS_KEY_ID": "sgf-dev-local",
+ "AWS_SECRET_ACCESS_KEY": "sgf-dev-local-password",
+ "AWS_SESSION_TOKEN": ""
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:44307;http://localhost:10600"
diff --git a/SgfDevs/SgfDevs.csproj b/SgfDevs/SgfDevs.csproj
index ea26e713..ec1a424d 100644
--- a/SgfDevs/SgfDevs.csproj
+++ b/SgfDevs/SgfDevs.csproj
@@ -3,6 +3,8 @@
net10.0
+
+
@@ -13,7 +15,7 @@
-
+
diff --git a/SgfDevs/appsettings-schema.Umbraco.StorageProviders.AzureBlob.json b/SgfDevs/appsettings-schema.Umbraco.StorageProviders.AzureBlob.json
deleted file mode 100755
index 96bd03f3..00000000
--- a/SgfDevs/appsettings-schema.Umbraco.StorageProviders.AzureBlob.json
+++ /dev/null
@@ -1,170 +0,0 @@
-{
- "$schema": "http://json-schema.org/draft-04/schema#",
- "title": "UmbracoStorageProvidersAzureBlobSchema",
- "type": "object",
- "description": "Defines the appsettings JSON schema for Umbraco Storage Providers - Azure Blob Storage configuration.",
- "properties": {
- "Umbraco": {
- "description": "Configuration container for all Umbraco products.",
- "oneOf": [
- {
- "$ref": "#/definitions/UmbracoDefinition"
- }
- ]
- }
- },
- "definitions": {
- "UmbracoDefinition": {
- "type": "object",
- "description": "Represents the configuration container for all Umbraco products.",
- "properties": {
- "Storage": {
- "description": "Configuration of Umbraco Storage Providers.",
- "oneOf": [
- {
- "$ref": "#/definitions/StorageDefinition"
- }
- ]
- }
- }
- },
- "StorageDefinition": {
- "type": "object",
- "description": "Represents the configuration of Umbraco Storage Providers.",
- "properties": {
- "AzureBlob": {
- "type": "object",
- "description": "Configuration of Umbraco Storage Providers - Azure Blob Storage, keyed by file system name (e.g. \"Media\").",
- "additionalProperties": {
- "$ref": "#/definitions/AzureBlobFileSystemOptions"
- }
- }
- }
- },
- "AzureBlobFileSystemOptions": {
- "type": "object",
- "description": "The Azure Blob File System options.",
- "required": [
- "ConnectionString",
- "ContainerName",
- "VirtualPath"
- ],
- "properties": {
- "ConnectionString": {
- "type": "string",
- "description": "Gets or sets the storage account connection string.",
- "minLength": 1
- },
- "ContainerName": {
- "type": "string",
- "description": "Gets or sets the container name.",
- "minLength": 1
- },
- "ContainerRootPath": {
- "type": [
- "null",
- "string"
- ],
- "description": "Gets or sets the root path of the container."
- },
- "VirtualPath": {
- "type": "string",
- "description": "Gets or sets the virtual path.",
- "minLength": 1
- },
- "Cache": {
- "description": "Gets or sets the in-memory cache settings applied to blob metadata lookups by the read-only file provider.",
- "oneOf": [
- {
- "$ref": "#/definitions/AzureBlobFileSystemCacheOptions"
- }
- ]
- },
- "Retry": {
- "description": "Gets or sets the retry and timeout settings applied to the default Blob client.",
- "oneOf": [
- {
- "$ref": "#/definitions/AzureBlobFileSystemRetryOptions"
- }
- ]
- }
- }
- },
- "AzureBlobFileSystemCacheOptions": {
- "type": "object",
- "description": "Cache settings applied to blob metadata lookups performed by the read-only file provider.",
- "properties": {
- "Enabled": {
- "type": "boolean",
- "description": "Gets or sets a value indicating whether blob metadata caching is enabled.",
- "default": true
- },
- "HitDuration": {
- "type": "string",
- "description": "Gets or sets how long a successful metadata lookup is cached.",
- "format": "duration",
- "default": "00:00:30"
- },
- "MissDuration": {
- "type": "string",
- "description": "Gets or sets how long a not-found metadata result is cached.",
- "format": "duration",
- "default": "00:00:05"
- }
- }
- },
- "AzureBlobFileSystemRetryOptions": {
- "type": "object",
- "description": "Retry and timeout settings applied to the default BlobContainerClient created from AzureBlobFileSystemOptions.",
- "properties": {
- "MaxRetries": {
- "type": "integer",
- "description": "Gets or sets the maximum number of retry attempts before giving up.",
- "format": "int32",
- "default": 2,
- "maximum": 2147483647.0,
- "minimum": 0.0
- },
- "NetworkTimeout": {
- "type": "string",
- "description": "Gets or sets the timeout applied to an individual network operation.",
- "format": "duration",
- "default": "00:00:30"
- },
- "Mode": {
- "description": "Gets or sets the approach to use for calculating retry delays.",
- "default": "Exponential",
- "oneOf": [
- {
- "$ref": "#/definitions/RetryMode"
- }
- ]
- },
- "Delay": {
- "type": "string",
- "description": "Gets or sets the delay between retry attempts for a fixed approach or the delay on which to base\ncalculations for a backoff-based approach.",
- "format": "duration",
- "default": "00:00:00.8000000"
- },
- "MaxDelay": {
- "type": "string",
- "description": "Gets or sets the maximum permissible delay between retry attempts when using a backoff approach.",
- "format": "duration",
- "default": "00:00:05"
- }
- }
- },
- "RetryMode": {
- "type": "string",
- "description": "",
- "x-enumNames": [
- "Fixed",
- "Exponential"
- ],
- "enum": [
- "Fixed",
- "Exponential"
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/SgfDevs/appsettings-schema.json b/SgfDevs/appsettings-schema.json
index 0e5a0454..4e4bcd5c 100755
--- a/SgfDevs/appsettings-schema.json
+++ b/SgfDevs/appsettings-schema.json
@@ -3889,9 +3889,6 @@
},
{
"$ref": "appsettings-schema.Umbraco.StorageProviders.json#"
- },
- {
- "$ref": "appsettings-schema.Umbraco.StorageProviders.AzureBlob.json#"
}
]
}
\ No newline at end of file
diff --git a/SgfDevs/appsettings.Development.json b/SgfDevs/appsettings.Development.json
index d98c770c..a1197d33 100644
--- a/SgfDevs/appsettings.Development.json
+++ b/SgfDevs/appsettings.Development.json
@@ -1,5 +1,9 @@
{
- "$schema": "./umbraco/config/appsettings-schema.json",
+ "$schema": "./appsettings-schema.json",
+ "AWS": {
+ "ServiceURL": "http://localhost:8333",
+ "ForcePathStyle": true
+ },
"Serilog": {
"MinimumLevel": {
"Default": "Information"
@@ -22,6 +26,13 @@
]
},
"Umbraco": {
+ "Storage": {
+ "AWSS3": {
+ "Media": {
+ "BucketName": "sgf-dev-local"
+ }
+ }
+ },
"CMS": {
"Content": {
"MacroErrors": "Throw"
diff --git a/SgfDevs/appsettings.json b/SgfDevs/appsettings.json
index 4f0caa62..639017b4 100644
--- a/SgfDevs/appsettings.json
+++ b/SgfDevs/appsettings.json
@@ -2,7 +2,6 @@
"$schema": "./appsettings-schema.json",
"SGFDevs": {
"EventSyncEnabled": false,
- "AzureBlobStorageKey": "",
"NewsletterEndpoint": "",
"NewsletterListId": "",
"EventTimeZoneId": "America/Chicago",
@@ -59,7 +58,23 @@
"umbracoDbDSN": "",
"umbracoDbDSN_ProviderName": "Microsoft.Data.Sqlite"
},
+ "AWS": {
+ "Region": "us-east-2"
+ },
"Umbraco": {
+ "Storage": {
+ "AWSS3": {
+ "Media": {
+ "BucketName": "",
+ "Region": "us-east-2",
+ "MediaBucketPrefix": "media",
+ "CacheBucketPrefix": "cache",
+ "CacheRetention": {
+ "Enabled": false
+ }
+ }
+ }
+ },
"CMS": {
"Global": {
"Id": "eb41b267-4c02-43fd-b315-cee566710902",
diff --git a/SgfDevs/appsettings.json.bak b/SgfDevs/appsettings.json.bak
index 4f1c64c6..0af5dd82 100644
--- a/SgfDevs/appsettings.json.bak
+++ b/SgfDevs/appsettings.json.bak
@@ -1,8 +1,5 @@
{
"$schema": "./appsettings-schema.json",
- "SGFDevs": {
- "AzureBlobStorageKey": ""
- },
"Serilog": {
"MinimumLevel": {
"Default": "Information",
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 00000000..da57c16f
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,17 @@
+name: sgf-dev-local
+
+services:
+ seaweedfs:
+ image: chrislusf/seaweedfs:4.48@sha256:4e61d15fd35994cb1e43e1e553dff106794841fd9a99ade2fc8c8bfce4d7872d
+ command: [mini, -dir=/data]
+ environment:
+ AWS_ACCESS_KEY_ID: sgf-dev-local
+ AWS_SECRET_ACCESS_KEY: sgf-dev-local-password
+ S3_BUCKET: sgf-dev-local
+ ports:
+ - "127.0.0.1:8333:8333"
+ volumes:
+ - seaweedfs-data:/data
+
+volumes:
+ seaweedfs-data: