Thanks for the response @rickfrankel
I updated but sadly didnt work
Here is the startup file
using Abp.AspNetCore;
using Abp.AspNetCore.Mvc.Antiforgery;
using Abp.AspNetCore.SignalR.Hubs;
using Abp.AspNetZeroCore.Web.Authentication.JwtBearer;
using Abp.Castle.Logging.Log4Net;
using Abp.Extensions;
using Abp.Hangfire;
using Abp.PlugIns;
using Abp.Timing;
using Castle.Facilities.Logging;
using GraphQL.Server;
using GraphQL.Server.Ui.Playground;
using Hangfire;
using HealthChecks.UI.Client;
using IdentityServer4.Configuration;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.SignalR;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Owl.reCAPTCHA;
using Stripe;
using SynapWare.Authorization;
using SynapWare.BlobStorage;
using SynapWare.Configuration;
using SynapWare.Configure;
using SynapWare.CosmosDB;
using SynapWare.EntityFrameworkCore;
using SynapWare.Identity;
using SynapWare.Schemas;
using SynapWare.Web.Chat.SignalR;
using SynapWare.Web.Common;
using SynapWare.Web.HealthCheck;
using SynapWare.Web.IdentityServer;
using SynapWare.Web.Swagger;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using HealthChecksUISettings = HealthChecks.UI.Configuration.Settings;
using ILoggerFactory = Microsoft.Extensions.Logging.ILoggerFactory;
namespace SynapWare.Web.Startup
{
public class Startup
{
private const string DefaultCorsPolicyName = "localhost";
private readonly IConfigurationRoot _appConfiguration;
private readonly IWebHostEnvironment _hostingEnvironment;
public Startup(IWebHostEnvironment env)
{
_hostingEnvironment = env;
_appConfiguration = env.GetAppConfiguration();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//set time zone
Clock.Provider = ClockProviders.Utc;
// Add CosmosDb. This verifies database and collections existence.
ConfigureCosmosDB(services);
//MVC
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
}).AddNewtonsoftJson();
services
.AddSignalR(options => { options.EnableDetailedErrors = true; })
.AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
{
new ServiceEndpoint(_appConfiguration["Azure:SignalR:PrimaryConnectionString"], EndpointType.Primary, "Name1"),
new ServiceEndpoint(_appConfiguration["Azure:SignalR:SecondaryConnectionString"], EndpointType.Secondary, "Name2"),
});
//Configure CORS for angular2 UI
services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
//App:CorsOrigins in appsettings.json can contain more than one address with splitted by comma.
builder
.WithOrigins(
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
_appConfiguration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
if (bool.Parse(_appConfiguration["KestrelServer:IsEnabled"]))
{
ConfigureKestrel(services);
}
IdentityRegistrar.Register(services);
AuthConfigurer.Configure(services, _appConfiguration);
//Identity server
if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
{
IdentityServerRegistrar.Register(services, _appConfiguration, options =>
options.UserInteraction = new UserInteractionOptions()
{
LoginUrl = "/UI/Login",
LogoutUrl = "/UI/LogOut",
ErrorUrl = "/Error"
});
}
if (WebConsts.SwaggerUiEnabled)
{
//Swagger - Enable this line and the related lines in Configure method to enable swagger UI
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo() { Title = "SynapWare API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.ParameterFilter<SwaggerEnumParameterFilter>();
options.SchemaFilter<SwaggerEnumSchemaFilter>();
options.OperationFilter<SwaggerOperationIdFilter>();
options.OperationFilter<SwaggerOperationFilter>();
options.CustomDefaultSchemaIdSelector();
}).AddSwaggerGenNewtonsoftSupport();
}
//Recaptcha
services.AddreCAPTCHAV3(x =>
{
x.SiteKey = _appConfiguration["Recaptcha:SiteKey"];
x.SiteSecret = _appConfiguration["Recaptcha:SecretKey"];
});
if (WebConsts.HangfireDashboardEnabled)
{
//Hangfire(Enable to use Hangfire instead of default job manager)
services.AddHangfire(config =>
{
config.UseSqlServerStorage(_appConfiguration.GetConnectionString("Default"));
});
}
if (WebConsts.GraphQL.Enabled)
{
services.AddAndConfigureGraphQL();
}
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
services.AddAbpZeroHealthCheck();
var healthCheckUISection = _appConfiguration.GetSection("HealthChecks")?.GetSection("HealthChecksUI");
if (bool.Parse(healthCheckUISection["HealthChecksUIEnabled"]))
{
services.Configure<HealthChecksUISettings>(settings =>
{
healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
});
services.AddHealthChecksUI();
}
}
services.AddScoped<IDeviceDataRepository, DeviceDataRepository>();
services.AddScoped<IBlobRepository>(factory =>
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_appConfiguration["ConnectionStrings:DeviceBlobStorage"]);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(_appConfiguration["BlobStorage:DeviceContainerName"]);
return new AzureBlobRepository(container);
});
//Configure Abp and Dependency Injection
return services.AddAbp<SynapWareWebHostModule>(options =>
{
//Configure Log4Net logging
options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig(_hostingEnvironment.IsDevelopment()
? "log4net.config"
: "log4net.Production.config")
);
options.PlugInSources.AddFolder(Path.Combine(_hostingEnvironment.WebRootPath, "Plugins"), SearchOption.AllDirectories);
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
//Initializes ABP framework.
app.UseAbp(options =>
{
options.UseAbpRequestLocalization = false; //used below: UseAbpRequestLocalization
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseStatusCodePagesWithRedirects("~/Error?statusCode={0}");
app.UseExceptionHandler("/Error");
}
app.Use(async (context, next) => { await next(); if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) { context.Request.Path = "/index.html"; await next(); } });
app.UseStaticFiles();
app.UseRouting();
app.UseCors(DefaultCorsPolicyName); //Enable CORS!
app.UseAuthentication();
app.UseJwtTokenMiddleware();
if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
{
app.UseJwtTokenMiddleware("IdentityBearer");
app.UseIdentityServer();
}
app.UseAuthorization();
using (var scope = app.ApplicationServices.CreateScope())
{
if (scope.ServiceProvider.GetService<DatabaseCheckHelper>().Exist(_appConfiguration["ConnectionStrings:Default"]))
{
app.UseAbpRequestLocalization();
}
}
if (WebConsts.HangfireDashboardEnabled)
{
//Hangfire dashboard &server(Enable to use Hangfire instead of default job manager)
app.UseHangfireDashboard(WebConsts.HangfireDashboardEndPoint, new DashboardOptions
{
Authorization = new[] { new AbpHangfireAuthorizationFilter(AppPermissions.Pages_Administration_HangfireDashboard) }
});
app.UseHangfireServer();
}
if (bool.Parse(_appConfiguration["Payment:Stripe:IsActive"]))
{
StripeConfiguration.ApiKey = _appConfiguration["Payment:Stripe:SecretKey"];
}
if (WebConsts.GraphQL.Enabled)
{
app.UseGraphQL<MainSchema>();
if (WebConsts.GraphQL.PlaygroundEnabled)
{
app.UseGraphQLPlayground(
new GraphQLPlaygroundOptions()); //to explorer API navigate https://*DOMAIN*/ui/playground
}
}
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<AbpCommonHub>(AppConsts.SignalRAbpCommonHubPath);
endpoints.MapHub<ChatHub>(AppConsts.SignalRChatHubPath);
endpoints.MapHub<TestChatHub>("/chat");
endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
}
});
//app.UseAzureSignalR(endpoints =>
//{
// endpoints.MapHub<AbpCommonHub>(AppConsts.SignalRAbpCommonHubPath);
// endpoints.MapHub<ChatHub>(AppConsts.SignalRChatHubPath);
// endpoints.MapHub<ChatHub>("/chat");
//});
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
{
if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksUI:HealthChecksUIEnabled"]))
{
app.UseHealthChecksUI();
}
}
if (WebConsts.SwaggerUiEnabled)
{
// Enable middleware to serve generated Swagger as a JSON endpoint
app.UseSwagger();
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint(_appConfiguration["App:SwaggerEndPoint"], "SynapWare API V1");
options.IndexStream = () => Assembly.GetExecutingAssembly()
.GetManifestResourceStream("SynapWare.Web.wwwroot.swagger.ui.index.html");
options.InjectBaseUrl(_appConfiguration["App:ServerRootAddress"]);
}); //URL: /swagger
}
}
private void ConfigureKestrel(IServiceCollection services)
{
services.Configure<Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions>(options =>
{
options.Listen(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 443),
listenOptions =>
{
var certPassword = _appConfiguration.GetValue<string>("Kestrel:Certificates:Default:Password");
var certPath = _appConfiguration.GetValue<string>("Kestrel:Certificates:Default:Path");
var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(certPath, certPassword);
listenOptions.UseHttps(new HttpsConnectionAdapterOptions()
{
ServerCertificate = cert
});
});
});
}
private void ConfigureCosmosDB(IServiceCollection services)
{
//https://github.com/Azure-Samples/PartitionedRepository
//To connect to multiple collections see example above
var serviceEndpoint = new Uri(_appConfiguration.GetValue<string>("CosmosDb:ServiceEndpoint"), UriKind.Absolute);
var authKey = _appConfiguration.GetValue<string>("CosmosDb:authKey");
var databaseName = _appConfiguration.GetValue<string>("CosmosDb:DatabaseName");
var collectionNames = new List<string>
{
_appConfiguration.GetValue<string>("CosmosDb:DeviceDataCollectionName"),
_appConfiguration.GetValue<string>("CosmosDb:LatestDeviceDataCollectionName")
};
var documentClient = new DocumentClient(serviceEndpoint, authKey, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
documentClient.OpenAsync().Wait();
var cosmosDbClientFactory = new CosmosDbClientFactory(databaseName, collectionNames, documentClient);
cosmosDbClientFactory.EnsureDbSetupAsync().Wait();
services.AddSingleton<ICosmosDbClientFactory>(cosmosDbClientFactory);
}
}
}
So, good news! It works if go back to default loginview.xaml. Must be something to do with my styling. Will report back asap
EDIT : Syncfusion related. Only does this on a physical iOS device, Android and iOS emulator works 100%. Working - License related :/ Sorry about that
Thanks @ismcagdas,
I dont make use of DataTemplates on the login page.
Setup iOS dev on my new iMac and same error; only an issue on a physical device (iPhone 11 pro, 14.3 if it matters at all)
My LoginView.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="LoginPage"
xmlns:base="clr-namespace:SynapWare.ViewModels.Base;assembly=SynapWare.Mobile.Shared"
xmlns:extensions="clr-namespace:SynapWare.Extensions.MarkupExtensions;assembly=SynapWare.Mobile.Shared"
xmlns:behaviors="clr-namespace:SynapWare.Behaviors;assembly=SynapWare.Mobile.Shared"
xmlns:controls="clr-namespace:SynapWare.Controls;assembly=SynapWare.Mobile.Shared"
xmlns:buttons="clr-namespace:Syncfusion.XForms.Buttons;assembly=Syncfusion.Buttons.XForms"
x:Class="SynapWare.Views.LoginView"
BackgroundColor="{StaticResource LoginBackgroundColor}"
base:ViewManager.AutoWireViewModel="true"
NavigationPage.HasNavigationBar="False"
Title="{extensions:Translate LogIn}">
<ContentPage.ToolbarItems>
<controls:HideableToolbarItem
Order="Primary"
Text="{extensions:Translate ChangeTenant}"
Command="{Binding ChangeTenantCommand}"
IsVisible="{Binding IsMultiTenancyEnabled}"
ParentPage="{x:Reference LoginPage}" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<ScrollView>
<Grid RowDefinitions="0.10*, 150, auto, auto, 0.20*" ColumnDefinitions="*, 18*, *"
RowSpacing="0"
ColumnSpacing="0">
<Image Grid.Row="1"
Grid.Column="1"
Aspect="AspectFit"
Source="{extensions:ImageSource 'SynapWare.UI.Assets.Images.Synap_Black_Teal.png'}">
</Image>
<!--<Frame Grid.Row="2"
Grid.Column="1"
IsVisible="{Binding IsMultiTenancyEnabled}"
Style="{StaticResource RoundedFrame}">
<Grid Padding="0" ColumnSpacing="5" RowSpacing="0" HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0"
Grid.Column="0"
LineBreakMode="WordWrap"
Text="{extensions:Translate CurrentTenant}" />
<Label Grid.Row="0"
Grid.Column="1"
LineBreakMode="WordWrap"
Text="{Binding CurrentTenancyNameOrDefault}"
Style="{StaticResource ActiveLabel}"/>
</Grid>
</Frame>-->
<Frame Grid.Row="3" BackgroundColor="White" BorderColor="White"
Grid.Column="1"
Style="{StaticResource RoundedFrame}">
<StackLayout VerticalOptions="StartAndExpand" Spacing="0">
<Label Text="{extensions:Translate UserNameOrEmail}" TextColor="{StaticResource PrimaryColor}" />
<Entry Text="{Binding UserName, Mode=TwoWay}"
Keyboard="{StaticResource NoCapitalizationKeyboard}"
x:Name="UsernameEntry"/>
<Label Text="{extensions:Translate Password}" Margin="0,10,0,0" TextColor="{StaticResource PrimaryColor}" />
<Entry IsPassword="True"
Text="{Binding Password, Mode=TwoWay}"
Keyboard="{StaticResource NoCapitalizationKeyboard}"
x:Name="PasswordEntry"/>
<Label Text="{extensions:Translate ForgotPassword}"
FontSize="Micro"
HorizontalOptions="EndAndExpand"
TextColor="{StaticResource PrimaryColor}"
Margin="0,10,0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ForgotPasswordCommand}" />
</Label.GestureRecognizers>
</Label>
<buttons:SfButton Text="{extensions:Translate LogIn}" x:Name="LoginButton" Margin="0,20,0,0"
IsEnabled="{Binding IsLoginEnabled}"
Style="{StaticResource ActionButton}"
Command="{Binding LoginUserCommand}"
CornerRadius="25"/>
<!--<Label Text="{extensions:Translate EmailActivation}"
HorizontalOptions="StartAndExpand"
TextColor="{StaticResource PrimaryColor}"
Margin="0,10,0,0">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EmailActivationCommand}" />
</Label.GestureRecognizers>
</Label>-->
<!--<Picker Title="{extensions:Translate Languages}"
Margin="0,10,0,0"
ItemsSource="{Binding Languages}"
ItemDisplayBinding="{Binding DisplayName}"
SelectedItem="{Binding SelectedLanguage}"/>-->
</StackLayout>
</Frame>
</Grid>
</ScrollView>
</ContentPage.Content>
<ContentPage.Behaviors>
<behaviors:EventHandlerBehavior EventName="Appearing">
<behaviors:ActionCollection>
<behaviors:InvokeCommandAction Command="{Binding PageAppearingCommand}" />
</behaviors:ActionCollection>
</behaviors:EventHandlerBehavior>
</ContentPage.Behaviors>
</ContentPage>
Hey @rickfrankel,
For some reason I'm still communicating locally and its not using Azure Signal R's service? https://support.aspnetzero.com/QA/Questions/9976/Replace-Signal-R-with-Azure-Signal-R
Do you have any idea why this is happening?
https://support.aspnetzero.com/QA/Questions/9493/SignalR-404-on-Azure-Multiple-App-Servers#answer-adf4666b-0f04-9740-3a44-39f7119bbbde
This is why I'm confused, its the same as this....
Hey,
What config are you talking about?
services.AddSignalR().AddAzureSignalR(_appConfiguration.GetValue<string>("Azure:SignalR:ConnectionString"));
My setup is now setup like this
"Azure": {
"SignalR": {
"ConnectionString": "Endpoint=https://synap-staging-signalr-service.service.signalr.net;AccessKey=abcdef=;Version=1.0;"
}
},
However its still using localhost?
Even adding the exact Azure Signal R Service sample code (const connection) shows localhost too (via the console.log)
Any idea why its using localhost rather than the actual Azure service? I thought it was automatic?
So making progress - web host is changed to use Azure Signal R and all looks good
However, how would I access IApplicationbuilder in Azure Functions in order to map the endpoint (if you know of course). Could I get it via AbpModule? Just code below showing how you guys do it in an ASPNET Core application
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<AbpCommonHub>(AppConsts.SignalRAbpCommonHubPath);
}
A host error has occurred during startup operation '39caf9b3-b3fa-472c-a351-6cc74d272ece'. [2020-12-14T11:48:02.356Z] func: Invalid host services. Microsoft.Azure.WebJobs.Script.WebHost: The following service registrations did not match the expected services: [2020-12-14T11:48:02.358Z] [Invalid] ServiceType: Microsoft.Extensions.Hosting.IHostedService, Lifetime: Singleton, ImplementationType: Microsoft.Azure.SignalR.HeartBeat, Microsoft.Azure.SignalR, Version=1.6.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60. Value cannot be null. (Parameter 'provider')
Thanks, I see from https://support.aspnetzero.com/QA/Questions/9129/Signal-R-send-custom-messages-to-Client-Side-using-azure-functionBlob-Triggered you say its supported from 8.6 and up. Is there any documentation to help me get going faster from ANZ?
Hey,
If it means anything, the chat works perfectly. Pushes through perfectly.
Is it not problematic that Im triggering the push from a 3rd party application (Azure Function CosmosDB trigger)?
using (var obj = Startup.iocManager.ResolveAsDisposable<AzFunctionAbpExecuter>())
{
await obj.Object.AddNewDeviceEventAppNotifier(deviceData);
}
public class AzFunctionAbpExecuter : ITransientDependency
{
private readonly IRepository<Tenant> _tenantRepository;
private readonly IAppNotifier _appNotifier;
public AzFunctionAbpExecuter(IRepository<Tenant> tenantRepository, IAppNotifier appNotifier)
{
_tenantRepository = tenantRepository;
_appNotifier = appNotifier;
}
public async Task AddNewDeviceEventAppNotifier(SynapWare.CosmosDB.DeviceData data)
{
await _appNotifier.NewDeviceEventAsync(data);
}
}
public async Task NewDeviceEventAsync(DeviceData deviceData)
{
var notificationData = new LocalizableMessageNotificationData(
new LocalizableString(
"NewDeviceEventNotificationMessage",
SynapWareConsts.LocalizationSourceName
)
);
notificationData["deviceSerial"] = deviceData.Serial;
if (deviceData.aimEventData != null)
{
notificationData["eventType"] = deviceData.aimEventData.deviceEventDataType.ToString();
}
UserIdentifier user = new UserIdentifier(null, userId: 1);
await _notificationPublisher.PublishAsync(AppNotificationNames.NewDeviceEvent, notificationData, userIds: new[] { user });
}