From ad2720fffb03da24391599f71f67ffbb69ade210 Mon Sep 17 00:00:00 2001 From: benjaminramey Date: Sun, 3 Nov 2019 00:45:25 +0000 Subject: [PATCH] Reset to safe template with azure deploy option --- README.md | 3 + RELEASE_NOTES.md | 2 +- arm-template.json | 117 +++++++++++++ build.fsx | 85 +++++++++ paket.dependencies | 6 + paket.lock | 339 +++++++++++++++++++++++++++++++++++- src/Client/Client.fs | 119 ++++++++----- src/Client/index.html | 2 +- src/Server/Server.fs | 26 ++- src/Server/paket.references | 2 + src/Shared/Shared.fs | 2 +- 11 files changed, 651 insertions(+), 52 deletions(-) create mode 100644 arm-template.json diff --git a/README.md b/README.md index 4555a9d..c2c934c 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ fake build -t Run ``` +You can use the included `arm-template.json` file and `build.fsx` script to deploy you application as an Azure Web App. Consult the [official template documentation](https://safe-stack.github.io/docs/template-appservice/) to learn more. + + ## SAFE Stack Documentation You will find more documentation about the used F# components at the following places: diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 2bf5c79..e4a253f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,2 +1,2 @@ -### 0.0.1 - 2019-10-05 +### 0.0.1 - 2019-11-02 * Initial release diff --git a/arm-template.json b/arm-template.json new file mode 100644 index 0000000..fca6a2c --- /dev/null +++ b/arm-template.json @@ -0,0 +1,117 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environment": { + "type": "string" + }, + "location": { + "type": "string" + }, + "pricingTier": { + "type": "string" + } + }, + "variables": { + "environment": "[toLower(parameters('environment'))]", + "prefix": "[concat('safe-', variables('environment'))]", + "appServicePlan": "[concat(variables('prefix'), '-web-host')]", + "web": "[concat(variables('prefix'), '-web')]", + "storage": "[concat('safe', variables('environment'), 'storage')]", + "insights": "[concat(variables('prefix'), '-insights')]" + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "kind": "web", + "name": "[variables('insights')]", + "location": "[parameters('location')]", + "apiVersion": "2014-04-01", + "scale": null, + "tags": { + "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('web'))]": "Resource", + "displayName": "AppInsightsComponent" + }, + "properties": { + "name": "[variables('insights')]" + } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "kind": "Storage", + "name": "[variables('storage')]", + "apiVersion": "2017-10-01", + "location": "[parameters('location')]", + "tags": {} + }, + { + "type": "Microsoft.Web/serverfarms", + "sku": { + "name": "[parameters('pricingTier')]" + }, + "name": "[variables('appServicePlan')]", + "apiVersion": "2016-09-01", + "location": "[parameters('location')]", + "properties": { + "name": "[variables('appserviceplan')]", + "perSiteScaling": false, + "reserved": false + } + }, + { + "type": "Microsoft.Web/sites", + "name": "[variables('web')]", + "apiVersion": "2016-08-01", + "location": "[parameters('location')]", + "properties": { + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlan'))]", + "siteConfig": { + "appSettings": [ + { + "name": "public_path", + "value": "./public" + }, + { + "name": "APPINSIGHTS_INSTRUMENTATIONKEY", + "value": "[reference(concat('Microsoft.Insights/components/', variables('insights'))).InstrumentationKey]" + }, + { + "name": "STORAGE_CONNECTIONSTRING", + "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts/', variables('storage')), '2017-10-01').keys[0].value)]" + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlan'))]", + "[resourceId('Microsoft.Storage/storageAccounts/', variables('storage'))]", + "[resourceId('Microsoft.Insights/components/', variables('insights'))]" + ], + "resources": [ + { + "apiVersion": "2016-08-01", + "name": "Microsoft.ApplicationInsights.AzureWebSites", + "type": "siteextensions", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('web'))]" + ], + "properties": {} + } + ] + } + ], + "outputs": { + "webAppName": { + "type": "string", + "value": "[variables('web')]" + }, + "webAppPassword": { + "type": "string", + "value": "[list(resourceId('Microsoft.Web/sites/config', variables('web'), 'publishingcredentials'), '2014-06-01').properties.publishingPassword]" + } + } +} \ No newline at end of file diff --git a/build.fsx b/build.fsx index ffd94a9..2abec3b 100644 --- a/build.fsx +++ b/build.fsx @@ -11,6 +11,10 @@ open System open Fake.Core open Fake.DotNet open Fake.IO +#load @"paket-files/build/CompositionalIT/fshelpers/src/FsHelpers/ArmHelper/ArmHelper.fs" +open Cit.Helpers.Arm +open Cit.Helpers.Arm.Parameters +open Microsoft.Azure.Management.ResourceManager.Fluent.Core Target.initEnvironment () @@ -110,6 +114,84 @@ Target.create "Run" (fun _ -> +Target.create "Bundle" (fun _ -> + let serverDir = deployDir + let publicDir = Path.combine deployDir "public" + + let publishArgs = sprintf "publish -c Release -o \"%s\"" serverDir + runDotNet publishArgs serverPath + + Shell.copyDir publicDir clientDeployPath FileFilter.allFiles +) + +type ArmOutput = + { WebAppName : ParameterValue + WebAppPassword : ParameterValue } +let mutable deploymentOutputs : ArmOutput option = None + +Target.create "ArmTemplate" (fun _ -> + let environment = Environment.environVarOrDefault "environment" (Guid.NewGuid().ToString().ToLower().Split '-' |> Array.head) + let armTemplate = @"arm-template.json" + let resourceGroupName = "safe-" + environment + + let authCtx = + // You can safely replace these with your own subscription and client IDs hard-coded into this script. + let subscriptionId = try Environment.environVar "subscriptionId" |> Guid.Parse with _ -> failwith "Invalid Subscription ID. This should be your Azure Subscription ID." + let clientId = try Environment.environVar "clientId" |> Guid.Parse with _ -> failwith "Invalid Client ID. This should be the Client ID of an application registered in Azure with permission to create resources in your subscription." + let tenantId = + try Environment.environVarOrNone "tenantId" |> Option.map Guid.Parse + with _ -> failwith "Invalid TenantId ID. This should be the Tenant ID of an application registered in Azure with permission to create resources in your subscription." + + Trace.tracefn "Deploying template '%s' to resource group '%s' in subscription '%O'..." armTemplate resourceGroupName subscriptionId + subscriptionId + |> authenticateDevice Trace.trace { ClientId = clientId; TenantId = tenantId } + |> Async.RunSynchronously + + let deployment = + let location = Environment.environVarOrDefault "location" Region.EuropeWest.Name + let pricingTier = Environment.environVarOrDefault "pricingTier" "F1" + { DeploymentName = "SAFE-template-deploy" + ResourceGroup = New(resourceGroupName, Region.Create location) + ArmTemplate = IO.File.ReadAllText armTemplate + Parameters = + Simple + [ "environment", ArmString environment + "location", ArmString location + "pricingTier", ArmString pricingTier ] + DeploymentMode = Incremental } + + deployment + |> deployWithProgress authCtx + |> Seq.iter(function + | DeploymentInProgress (state, operations) -> Trace.tracefn "State is %s, completed %d operations." state operations + | DeploymentError (statusCode, message) -> Trace.traceError <| sprintf "DEPLOYMENT ERROR: %s - '%s'" statusCode message + | DeploymentCompleted d -> deploymentOutputs <- d) +) + +open Fake.IO.Globbing.Operators +open System.Net + +// https://github.com/SAFE-Stack/SAFE-template/issues/120 +// https://stackoverflow.com/a/6994391/3232646 +type TimeoutWebClient() = + inherit WebClient() + override this.GetWebRequest uri = + let request = base.GetWebRequest uri + request.Timeout <- 30 * 60 * 1000 + request + +Target.create "AppService" (fun _ -> + let zipFile = "deploy.zip" + IO.File.Delete zipFile + Zip.zip deployDir zipFile !!(deployDir + @"\**\**") + + let appName = deploymentOutputs.Value.WebAppName.value + let appPassword = deploymentOutputs.Value.WebAppPassword.value + + let destinationUri = sprintf "https://%s.scm.azurewebsites.net/api/zipdeploy" appName + let client = new TimeoutWebClient(Credentials = NetworkCredential("$" + appName, appPassword)) + Trace.tracefn "Uploading %s to %s" zipFile destinationUri + client.UploadData(destinationUri, IO.File.ReadAllBytes zipFile) |> ignore) @@ -118,6 +200,9 @@ open Fake.Core.TargetOperators "Clean" ==> "InstallClient" ==> "Build" + ==> "Bundle" + ==> "ArmTemplate" + ==> "AppService" "Clean" diff --git a/paket.dependencies b/paket.dependencies index 066bdef..06557b0 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -6,6 +6,8 @@ group Server nuget FSharp.Core nuget Saturn nuget Thoth.Json.Giraffe + nuget Microsoft.ApplicationInsights.AspNetCore ~> 2.2 + nuget WindowsAzure.Storage group Client source https://api.nuget.org/v3/index.json @@ -32,4 +34,8 @@ group Build nuget Fake.Core.Target nuget Fake.DotNet.Cli nuget Fake.IO.FileSystem + nuget Fake.Core.Environment + nuget Fake.Core.Trace + nuget Fake.IO.Zip + github CompositionalIT/fshelpers src/FsHelpers/ArmHelper/ArmHelper.fs diff --git a/paket.lock b/paket.lock index 5487aae..a6f4db4 100644 --- a/paket.lock +++ b/paket.lock @@ -96,6 +96,12 @@ NUGET FSharp.Core (>= 4.3.4) System.Diagnostics.FileVersionInfo (>= 4.3) System.IO.FileSystem.Watcher (>= 4.3) + Fake.IO.Zip (5.15) + Fake.Core.String (>= 5.15) + Fake.IO.FileSystem (>= 5.15) + FSharp.Core (>= 4.3.4) + System.IO.Compression (>= 4.3) + System.IO.Compression.ZipFile (>= 4.3) Fake.Net.Http (5.15) Fake.Core.Trace (>= 5.15) FSharp.Core (>= 4.3.4) @@ -107,6 +113,12 @@ NUGET FSharp.Core (>= 4.2.3) System.Reactive (>= 4.0) FSharp.Core (4.6.2) + Microsoft.Azure.Management.ResourceManager.Fluent (1.4.1) + Microsoft.Rest.ClientRuntime.Azure (>= 3.3.10) + Microsoft.Rest.ClientRuntime.Azure.Authentication (>= 2.3.1) + NETStandard.Library (>= 1.6.1) + System.Net.NetworkInformation (>= 4.3) + System.Reflection.TypeExtensions (>= 4.3) Microsoft.Build (16.0.461) Microsoft.Build.Framework (16.0.461) System.Runtime.Serialization.Primitives (>= 4.1.1) @@ -128,8 +140,28 @@ NUGET Microsoft.Win32.Registry (>= 4.3) System.Collections.Immutable (>= 1.5) System.Text.Encoding.CodePages (>= 4.0.1) + Microsoft.IdentityModel.Clients.ActiveDirectory (3.19.8) + NETStandard.Library (>= 1.6) + System.Runtime.Serialization.Json (>= 4.3) + System.Runtime.Serialization.Primitives (>= 4.3) + Microsoft.IdentityModel.Logging (5.5) + Microsoft.IdentityModel.Tokens (5.5) + Microsoft.IdentityModel.Logging (>= 5.5) + Newtonsoft.Json (>= 10.0.1) + System.Security.Cryptography.Cng (>= 4.5) Microsoft.NETCore.Platforms (2.2.1) Microsoft.NETCore.Targets (2.1) + Microsoft.Rest.ClientRuntime (2.3.20) + Newtonsoft.Json (>= 10.0.3) + Microsoft.Rest.ClientRuntime.Azure (3.3.19) + Microsoft.Rest.ClientRuntime (>= 2.3.19 < 3.0) + Newtonsoft.Json (>= 10.0.3) + Microsoft.Rest.ClientRuntime.Azure.Authentication (2.3.2) + Microsoft.IdentityModel.Clients.ActiveDirectory (>= 3.13.9 < 4.0) + Microsoft.IdentityModel.Tokens (>= 5.1.2 < 6.0) + Microsoft.Rest.ClientRuntime (>= 2.3.9 < 3.0) + NETStandard.Library (>= 1.6.1) + Newtonsoft.Json (>= 9.0.1) Microsoft.Win32.Primitives (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -173,6 +205,9 @@ NUGET runtime.native.System (4.3.1) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) + runtime.native.System.IO.Compression (4.3.2) + Microsoft.NETCore.Platforms (>= 1.1.1) + Microsoft.NETCore.Targets (>= 1.1.3) runtime.native.System.Net.Http (4.3.1) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) @@ -304,6 +339,32 @@ NUGET System.Runtime (>= 4.3) System.Text.Encoding (>= 4.3) System.Threading.Tasks (>= 4.3) + System.IO.Compression (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + runtime.native.System (>= 4.3) + runtime.native.System.IO.Compression (>= 4.3) + System.Buffers (>= 4.3) + System.Collections (>= 4.3) + System.Diagnostics.Debug (>= 4.3) + System.IO (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Runtime.Handles (>= 4.3) + System.Runtime.InteropServices (>= 4.3) + System.Text.Encoding (>= 4.3) + System.Threading (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.IO.Compression.ZipFile (4.3) + System.Buffers (>= 4.3) + System.IO (>= 4.3) + System.IO.Compression (>= 4.3) + System.IO.FileSystem (>= 4.3) + System.IO.FileSystem.Primitives (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Text.Encoding (>= 4.3) System.IO.FileSystem (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -398,11 +459,42 @@ NUGET System.Text.Encoding (>= 4.3) System.Threading (>= 4.3) System.Threading.Tasks (>= 4.3) + System.Net.NetworkInformation (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + Microsoft.Win32.Primitives (>= 4.3) + runtime.native.System (>= 4.3) + System.Collections (>= 4.3) + System.Diagnostics.Tracing (>= 4.3) + System.Globalization (>= 4.3) + System.IO (>= 4.3) + System.IO.FileSystem (>= 4.3) + System.IO.FileSystem.Primitives (>= 4.3) + System.Linq (>= 4.3) + System.Net.Primitives (>= 4.3) + System.Net.Sockets (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Runtime.Handles (>= 4.3) + System.Runtime.InteropServices (>= 4.3) + System.Security.Principal.Windows (>= 4.3) + System.Threading (>= 4.3) + System.Threading.Overlapped (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.Threading.Thread (>= 4.3) + System.Threading.ThreadPool (>= 4.3) System.Net.Primitives (4.3.1) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) System.Runtime (>= 4.3.1) System.Runtime.Handles (>= 4.3) + System.Net.Sockets (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + Microsoft.NETCore.Targets (>= 1.1) + System.IO (>= 4.3) + System.Net.Primitives (>= 4.3) + System.Runtime (>= 4.3) + System.Threading.Tasks (>= 4.3) System.Numerics.Vectors (4.5) System.ObjectModel (4.3) System.Collections (>= 4.3) @@ -410,6 +502,32 @@ NUGET System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Threading (>= 4.3) + System.Private.DataContractSerialization (4.3) + System.Collections (>= 4.3) + System.Collections.Concurrent (>= 4.3) + System.Diagnostics.Debug (>= 4.3) + System.Globalization (>= 4.3) + System.IO (>= 4.3) + System.Linq (>= 4.3) + System.Reflection (>= 4.3) + System.Reflection.Emit.ILGeneration (>= 4.3) + System.Reflection.Emit.Lightweight (>= 4.3) + System.Reflection.Extensions (>= 4.3) + System.Reflection.Primitives (>= 4.3) + System.Reflection.TypeExtensions (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Runtime.Serialization.Primitives (>= 4.3) + System.Text.Encoding (>= 4.3) + System.Text.Encoding.Extensions (>= 4.3) + System.Text.RegularExpressions (>= 4.3) + System.Threading (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.Xml.ReaderWriter (>= 4.3) + System.Xml.XDocument (>= 4.3) + System.Xml.XmlDocument (>= 4.3) + System.Xml.XmlSerializer (>= 4.3) System.Reactive (4.1.5) System.Runtime.InteropServices.WindowsRuntime (>= 4.3) System.Threading.Tasks.Extensions (>= 4.5.2) @@ -506,6 +624,10 @@ NUGET System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) + System.Runtime.Serialization.Json (4.3) + System.IO (>= 4.3) + System.Private.DataContractSerialization (>= 4.3) + System.Runtime (>= 4.3) System.Runtime.Serialization.Primitives (4.3) System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) @@ -670,6 +792,24 @@ NUGET System.Text.Encoding (>= 4.3) System.Threading (>= 4.3) System.Xml.ReaderWriter (>= 4.3) + System.Xml.XmlSerializer (4.3) + System.Collections (>= 4.3) + System.Globalization (>= 4.3) + System.IO (>= 4.3) + System.Linq (>= 4.3) + System.Reflection (>= 4.3) + System.Reflection.Emit (>= 4.3) + System.Reflection.Emit.ILGeneration (>= 4.3) + System.Reflection.Extensions (>= 4.3) + System.Reflection.Primitives (>= 4.3) + System.Reflection.TypeExtensions (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Text.RegularExpressions (>= 4.3) + System.Threading (>= 4.3) + System.Xml.ReaderWriter (>= 4.3) + System.Xml.XmlDocument (>= 4.3) System.Xml.XPath (4.3) System.Collections (>= 4.3) System.Diagnostics.Debug (>= 4.3) @@ -701,7 +841,11 @@ NUGET System.Xml.ReaderWriter (>= 4.3) System.Xml.XmlDocument (>= 4.3) System.Xml.XPath (>= 4.3) - +GITHUB + remote: CompositionalIT/fshelpers + src/FsHelpers/ArmHelper/ArmHelper.fs (2e1ba955ffcff32a3aa2638d3dcc4f4057cf7186) + Microsoft.Azure.Management.ResourceManager.Fluent (1.4.1) + Microsoft.Rest.ClientRuntime.Azure.Authentication (2.3.2) GROUP Client STORAGE: NONE RESTRICTION: == netstandard2.0 @@ -799,6 +943,63 @@ NUGET System.Xml.XmlSerializer (>= 4.3) TaskBuilder.fs (>= 2.1) Utf8Json (>= 1.3.7) + Microsoft.ApplicationInsights (2.9.1) + NETStandard.Library (>= 1.6.1) + System.Diagnostics.DiagnosticSource (>= 4.5) + System.Diagnostics.StackTrace (>= 4.3) + System.Net.Requests (>= 4.3) + System.Threading.Thread (>= 4.3) + Microsoft.ApplicationInsights.AspNetCore (2.6.1) + Microsoft.ApplicationInsights (>= 2.9.1) + Microsoft.ApplicationInsights.DependencyCollector (>= 2.9.1) + Microsoft.ApplicationInsights.PerfCounterCollector (>= 2.9.1) + Microsoft.ApplicationInsights.WindowsServer (>= 2.9.1) + Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel (>= 2.9.1) + Microsoft.AspNetCore.Hosting (>= 1.0.2) + Microsoft.Extensions.Configuration (>= 1.0.2) + Microsoft.Extensions.Configuration.Json (>= 1.0.2) + Microsoft.Extensions.DiagnosticAdapter (>= 1.1) + Microsoft.Extensions.Logging.Abstractions (>= 1.0.2) + NETStandard.Library (>= 1.6.1) + System.Diagnostics.DiagnosticSource (>= 4.5) + System.Net.NameResolution (>= 4.3) + System.Text.Encodings.Web (>= 4.3.1) + Microsoft.ApplicationInsights.DependencyCollector (2.9.1) + Microsoft.ApplicationInsights (>= 2.9.1) + Microsoft.Extensions.DiagnosticAdapter (>= 1.1) + Microsoft.Extensions.PlatformAbstractions (>= 1.1) + NETStandard.Library (>= 1.6.1) + System.Data.SqlClient (>= 4.3.1) + System.Diagnostics.DiagnosticSource (>= 4.5) + System.Diagnostics.StackTrace (>= 4.3) + Microsoft.ApplicationInsights.PerfCounterCollector (2.9.1) + Microsoft.ApplicationInsights (>= 2.9.1) + Microsoft.Extensions.Caching.Memory (>= 1.0) + Microsoft.Extensions.PlatformAbstractions (>= 1.1) + NETStandard.Library (>= 1.6.1) + System.Runtime.Serialization.Json (>= 4.3) + System.Runtime.Serialization.Primitives (>= 4.3) + System.Threading.Thread (>= 4.3) + Microsoft.ApplicationInsights.WindowsServer (2.9.1) + Microsoft.ApplicationInsights (>= 2.9.1) + Microsoft.ApplicationInsights.DependencyCollector (>= 2.9.1) + Microsoft.ApplicationInsights.PerfCounterCollector (>= 2.9.1) + Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel (>= 2.9.1) + NETStandard.Library (>= 1.6.1) + System.Diagnostics.DiagnosticSource (>= 4.5) + System.Runtime.Serialization.Json (>= 4.3) + System.Runtime.Serialization.Primitives (>= 4.3) + Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel (2.9.1) + Microsoft.ApplicationInsights (>= 2.9.1) + System.Diagnostics.Process (>= 4.3) + System.IO.FileSystem.AccessControl (>= 4.3) + System.Net.NetworkInformation (>= 4.3) + System.Net.Requests (>= 4.3) + System.Net.WebHeaderCollection (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Serialization.Primitives (>= 4.3) + System.Security.Cryptography.Algorithms (>= 4.3) + System.Threading.Thread (>= 4.3) Microsoft.AspNetCore (2.2) Microsoft.AspNetCore.Diagnostics (>= 2.2) Microsoft.AspNetCore.HostFiltering (>= 2.2) @@ -1047,6 +1248,8 @@ NUGET Microsoft.Extensions.DependencyInjection (2.2) Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.2) Microsoft.Extensions.DependencyInjection.Abstractions (2.2) + Microsoft.Extensions.DiagnosticAdapter (2.2) + System.Diagnostics.DiagnosticSource (>= 4.5) Microsoft.Extensions.FileProviders.Abstractions (2.2) Microsoft.Extensions.Primitives (>= 2.2) Microsoft.Extensions.FileProviders.Physical (2.2) @@ -1086,6 +1289,9 @@ NUGET Microsoft.Extensions.Configuration.Binder (>= 2.2) Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.2) Microsoft.Extensions.Options (>= 2.2) + Microsoft.Extensions.PlatformAbstractions (1.1) + NETStandard.Library (>= 1.6.1) + System.Reflection.TypeExtensions (>= 4.3) Microsoft.Extensions.Primitives (2.2) System.Memory (>= 4.5.1) System.Runtime.CompilerServices.Unsafe (>= 4.5.1) @@ -1133,6 +1339,10 @@ NUGET System.Buffers (>= 4.5) Microsoft.NETCore.Platforms (2.2.1) Microsoft.NETCore.Targets (2.1) + Microsoft.Win32.Primitives (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + Microsoft.NETCore.Targets (>= 1.1) + System.Runtime (>= 4.3) Microsoft.Win32.Registry (4.5) System.Security.AccessControl (>= 4.5) System.Security.Principal.Windows (>= 4.5) @@ -1148,6 +1358,10 @@ NUGET runtime.native.System (4.3.1) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) + runtime.native.System.Data.SqlClient.sni (4.5) + runtime.win-arm64.runtime.native.System.Data.SqlClient.sni (>= 4.4) + runtime.win-x64.runtime.native.System.Data.SqlClient.sni (>= 4.4) + runtime.win-x86.runtime.native.System.Data.SqlClient.sni (>= 4.4) runtime.native.System.Net.Http (4.3.1) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) @@ -1179,6 +1393,9 @@ NUGET runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) + runtime.win-arm64.runtime.native.System.Data.SqlClient.sni (4.4) + runtime.win-x64.runtime.native.System.Data.SqlClient.sni (4.4) + runtime.win-x86.runtime.native.System.Data.SqlClient.sni (4.4) Saturn (0.8) FSharp.Core (>= 4.2.3) Giraffe (>= 3.4 < 4.0) @@ -1226,6 +1443,11 @@ NUGET System.Runtime.Extensions (>= 4.3) System.Threading (>= 4.3) System.ComponentModel.Annotations (4.5) + System.Data.SqlClient (4.6.1) + Microsoft.Win32.Registry (>= 4.5) + runtime.native.System.Data.SqlClient.sni (>= 4.5) + System.Security.Principal.Windows (>= 4.5) + System.Text.Encoding.CodePages (>= 4.5) System.Diagnostics.Contracts (4.3) System.Runtime (>= 4.3) System.Diagnostics.Debug (4.3) @@ -1233,6 +1455,33 @@ NUGET Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) System.Diagnostics.DiagnosticSource (4.5.1) + System.Diagnostics.Process (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + Microsoft.Win32.Primitives (>= 4.3) + Microsoft.Win32.Registry (>= 4.3) + runtime.native.System (>= 4.3) + System.Collections (>= 4.3) + System.Diagnostics.Debug (>= 4.3) + System.Globalization (>= 4.3) + System.IO (>= 4.3) + System.IO.FileSystem (>= 4.3) + System.IO.FileSystem.Primitives (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Runtime.Handles (>= 4.3) + System.Runtime.InteropServices (>= 4.3) + System.Text.Encoding (>= 4.3) + System.Text.Encoding.Extensions (>= 4.3) + System.Threading (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.Threading.Thread (>= 4.3) + System.Threading.ThreadPool (>= 4.3) + System.Diagnostics.StackTrace (4.3) + System.IO.FileSystem (>= 4.3) + System.Reflection (>= 4.3) + System.Reflection.Metadata (>= 1.4.1) + System.Runtime (>= 4.3) System.Diagnostics.Tools (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -1291,6 +1540,9 @@ NUGET System.Runtime.Handles (>= 4.3) System.Text.Encoding (>= 4.3) System.Threading.Tasks (>= 4.3) + System.IO.FileSystem.AccessControl (4.5) + System.Security.AccessControl (>= 4.5) + System.Security.Principal.Windows (>= 4.5) System.IO.FileSystem.Primitives (4.3) System.Runtime (>= 4.3) System.IO.Pipelines (4.5.3) @@ -1346,11 +1598,76 @@ NUGET System.Text.Encoding (>= 4.3) System.Threading (>= 4.3) System.Threading.Tasks (>= 4.3) + System.Net.NameResolution (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + runtime.native.System (>= 4.3) + System.Collections (>= 4.3) + System.Diagnostics.Tracing (>= 4.3) + System.Globalization (>= 4.3) + System.Net.Primitives (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Runtime.Handles (>= 4.3) + System.Runtime.InteropServices (>= 4.3) + System.Security.Principal.Windows (>= 4.3) + System.Threading (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.Net.NetworkInformation (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + Microsoft.Win32.Primitives (>= 4.3) + runtime.native.System (>= 4.3) + System.Collections (>= 4.3) + System.Diagnostics.Tracing (>= 4.3) + System.Globalization (>= 4.3) + System.IO (>= 4.3) + System.IO.FileSystem (>= 4.3) + System.IO.FileSystem.Primitives (>= 4.3) + System.Linq (>= 4.3) + System.Net.Primitives (>= 4.3) + System.Net.Sockets (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) + System.Runtime.Handles (>= 4.3) + System.Runtime.InteropServices (>= 4.3) + System.Security.Principal.Windows (>= 4.3) + System.Threading (>= 4.3) + System.Threading.Overlapped (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.Threading.Thread (>= 4.3) + System.Threading.ThreadPool (>= 4.3) System.Net.Primitives (4.3.1) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) System.Runtime (>= 4.3.1) System.Runtime.Handles (>= 4.3) + System.Net.Requests (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + System.Collections (>= 4.3) + System.Diagnostics.Debug (>= 4.3) + System.Diagnostics.Tracing (>= 4.3) + System.Globalization (>= 4.3) + System.IO (>= 4.3) + System.Net.Http (>= 4.3) + System.Net.Primitives (>= 4.3) + System.Net.WebHeaderCollection (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Threading (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.Net.Sockets (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + Microsoft.NETCore.Targets (>= 1.1) + System.IO (>= 4.3) + System.Net.Primitives (>= 4.3) + System.Runtime (>= 4.3) + System.Threading.Tasks (>= 4.3) + System.Net.WebHeaderCollection (4.3) + System.Collections (>= 4.3) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Extensions (>= 4.3) System.Numerics.Vectors (4.5) System.ObjectModel (4.3) System.Collections (>= 4.3) @@ -1454,6 +1771,10 @@ NUGET System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) System.Runtime.Extensions (>= 4.3) + System.Runtime.Serialization.Json (4.3) + System.IO (>= 4.3) + System.Private.DataContractSerialization (>= 4.3) + System.Runtime (>= 4.3) System.Runtime.Serialization.Primitives (4.3) System.Resources.ResourceManager (>= 4.3) System.Runtime (>= 4.3) @@ -1568,6 +1889,9 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) + System.Text.Encoding.CodePages (4.5.1) + Microsoft.NETCore.Platforms (>= 2.1.2) + System.Runtime.CompilerServices.Unsafe (>= 4.5.2) System.Text.Encoding.Extensions (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -1579,11 +1903,21 @@ NUGET System.Threading (4.3) System.Runtime (>= 4.3) System.Threading.Tasks (>= 4.3) + System.Threading.Overlapped (4.3) + Microsoft.NETCore.Platforms (>= 1.1) + System.Resources.ResourceManager (>= 4.3) + System.Runtime (>= 4.3) + System.Runtime.Handles (>= 4.3) System.Threading.Tasks (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) System.Threading.Tasks.Extensions (4.5.2) + System.Threading.Thread (4.3) + System.Runtime (>= 4.3) + System.Threading.ThreadPool (4.3) + System.Runtime (>= 4.3) + System.Runtime.Handles (>= 4.3) System.ValueTuple (4.5) System.Xml.ReaderWriter (4.3.1) System.Collections (>= 4.3) @@ -1659,3 +1993,6 @@ NUGET System.Reflection.Emit.Lightweight (>= 4.3) System.Threading.Tasks.Extensions (>= 4.4) System.ValueTuple (>= 4.4) + WindowsAzure.Storage (9.3.3) + NETStandard.Library (>= 1.6.1) + Newtonsoft.Json (>= 10.0.2) diff --git a/src/Client/Client.fs b/src/Client/Client.fs index abf2071..7e359e3 100644 --- a/src/Client/Client.fs +++ b/src/Client/Client.fs @@ -8,58 +8,99 @@ open Fetch.Types open Thoth.Fetch open Fulma open Thoth.Json -open Browser.Types + open Shared -let [] ENTER_KEY = 13. - -let onEnter msg dispatch = - OnKeyDown (fun ev -> - if ev.keyCode = ENTER_KEY then - dispatch msg) - -let targetValue (ev: Event) = - (ev.target :?> HTMLInputElement).value - -type Model = { Run: Run } +// The model holds data that you want to keep track of while the application is running +// in this case, we are keeping track of a counter +// we mark it as optional, because initially it will not be available from the client +// the initial value will be requested from server +type Model = { Counter: Counter option } +// The Msg type defines what events/actions can occur while the application is running +// the state of the application changes *only* in reaction to these events type Msg = - | StartRun - | UpdateName of string + | Increment + | Decrement + | InitialCountLoaded of Counter -//let initialCounter () = Fetch.fetchAs "/api/init" +let initialCounter () = Fetch.fetchAs "/api/init" +// defines the initial state and initial command (= side-effect) of the application let init () : Model * Cmd = - let initialModel = { Run = { Name = "george" } } - initialModel, Cmd.none + let initialModel = { Counter = None } + let loadCountCmd = + Cmd.OfPromise.perform initialCounter () InitialCountLoaded + initialModel, loadCountCmd +// The update function computes the next state of the application based on the current state and the incoming events/messages +// It can also run side-effects (encoded as commands) like calling the server via Http. +// these commands in turn, can dispatch messages to which the update function will react. let update (msg : Msg) (currentModel : Model) : Model * Cmd = - match msg with - | StartRun -> - let nextModel = { currentModel with Run = { Name = currentModel.Run.Name + " saved" } } + match currentModel.Counter, msg with + | Some counter, Increment -> + let nextModel = { currentModel with Counter = Some { Value = counter.Value + 1 } } nextModel, Cmd.none - | UpdateName newName -> - let nextModel = { currentModel with Run = { Name = newName }} + | Some counter, Decrement -> + let nextModel = { currentModel with Counter = Some { Value = counter.Value - 1 } } nextModel, Cmd.none + | _, InitialCountLoaded initialCount-> + let nextModel = { Counter = Some initialCount } + nextModel, Cmd.none + | _ -> currentModel, Cmd.none + + +let safeComponents = + let components = + span [ ] + [ a [ Href "https://github.com/SAFE-Stack/SAFE-template" ] + [ str "SAFE " + str Version.template ] + str ", " + a [ Href "https://saturnframework.github.io" ] [ str "Saturn" ] + str ", " + a [ Href "http://fable.io" ] [ str "Fable" ] + str ", " + a [ Href "https://elmish.github.io" ] [ str "Elmish" ] + str ", " + a [ Href "https://fulma.github.io/Fulma" ] [ str "Fulma" ] + + ] + + span [ ] + [ str "Version " + strong [ ] [ str Version.app ] + str " powered by: " + components ] + +let show = function + | { Counter = Some counter } -> string counter.Value + | { Counter = None } -> "Loading..." + +let button txt onClick = + Button.button + [ Button.IsFullWidth + Button.Color IsPrimary + Button.OnClick onClick ] + [ str txt ] let view (model : Model) (dispatch : Msg -> unit) = - Columns.columns [] - [ Column.column [ Column.Offset (Screen.All, Column.Is3) - Column.Width (Screen.All, Column.Is6) ] - [ Box.box' [] - [ Heading.h1 [] [ str "lunch run" ] - p [] [ str "Start a lunch run today!" ] - Field.div [] - [ Control.div [] - [ Input.text [ Input.Placeholder "lunch run name" - Input.Value model.Run.Name - Input.OnChange (fun ev -> targetValue ev |> UpdateName |> dispatch) - Input.Props [ onEnter StartRun dispatch ] ] - ] - ] - ] - ] - ] + div [] + [ Navbar.navbar [ Navbar.Color IsPrimary ] + [ Navbar.Item.div [ ] + [ Heading.h2 [ ] + [ str "SAFE Template" ] ] ] + + Container.container [] + [ Content.content [ Content.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] + [ Heading.h3 [] [ str ("Press buttons to manipulate counter: " + show model) ] ] + Columns.columns [] + [ Column.column [] [ button "-" (fun _ -> dispatch Decrement) ] + Column.column [] [ button "+" (fun _ -> dispatch Increment) ] ] ] + + Footer.footer [ ] + [ Content.content [ Content.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] + [ safeComponents ] ] ] #if DEBUG open Elmish.Debug diff --git a/src/Client/index.html b/src/Client/index.html index 30314e3..77234f6 100644 --- a/src/Client/index.html +++ b/src/Client/index.html @@ -1,7 +1,7 @@  - lunch run! + SAFE Template diff --git a/src/Server/Server.fs b/src/Server/Server.fs index 0eb432f..6868d94 100644 --- a/src/Server/Server.fs +++ b/src/Server/Server.fs @@ -8,29 +8,37 @@ open Giraffe open Saturn open Shared +open Microsoft.WindowsAzure.Storage let tryGetEnv = System.Environment.GetEnvironmentVariable >> function null | "" -> None | x -> Some x -let publicPath = Path.GetFullPath "../Client/public" +let publicPath = tryGetEnv "public_path" |> Option.defaultValue "../Client/public" |> Path.GetFullPath +let storageAccount = tryGetEnv "STORAGE_CONNECTIONSTRING" |> Option.defaultValue "UseDevelopmentStorage=true" |> CloudStorageAccount.Parse let port = "SERVER_PORT" |> tryGetEnv |> Option.map uint16 |> Option.defaultValue 8085us -// let webApp = router { -// get "/api/init" (fun next ctx -> -// task { -// let counter = {Value = 7} -// return! json counter next ctx -// }) -// } +let webApp = router { + get "/api/init" (fun next ctx -> + task { + let counter = {Value = 42} + return! json counter next ctx + }) +} + +let configureAzure (services:IServiceCollection) = + tryGetEnv "APPINSIGHTS_INSTRUMENTATIONKEY" + |> Option.map services.AddApplicationInsightsTelemetry + |> Option.defaultValue services let app = application { url ("http://0.0.0.0:" + port.ToString() + "/") - //use_router webApp + use_router webApp memory_cache use_static publicPath use_json_serializer(Thoth.Json.Giraffe.ThothSerializer()) + service_config configureAzure use_gzip } diff --git a/src/Server/paket.references b/src/Server/paket.references index 26ab45f..ee879e7 100644 --- a/src/Server/paket.references +++ b/src/Server/paket.references @@ -1,4 +1,6 @@ group Server FSharp.Core Saturn + Microsoft.ApplicationInsights.AspNetCore + WindowsAzure.Storage Thoth.Json.Giraffe diff --git a/src/Shared/Shared.fs b/src/Shared/Shared.fs index b139f9a..cee5a28 100644 --- a/src/Shared/Shared.fs +++ b/src/Shared/Shared.fs @@ -1,5 +1,5 @@ namespace Shared -type Run = { Name : string } +type Counter = { Value : int }