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: