global: make ready for self hosting
This exposes a few more env vars for configuration & bypasses the Subscription backend so each user is Pro by default. This is required because there won't be any mechanism for the user to upgrade. We'll also have to disable a few things on the client side to avoid confusion.
This commit is contained in:
@@ -17,10 +17,15 @@ You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !DEBUG
|
||||
using System.Net;
|
||||
#endif
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Streetwriters.Common;
|
||||
using System.Linq;
|
||||
|
||||
namespace Notesnook.API
|
||||
{
|
||||
@@ -28,8 +33,6 @@ namespace Notesnook.API
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
DotNetEnv.Env.TraversePath().Load();
|
||||
|
||||
IHost host = CreateHostBuilder(args).Build();
|
||||
await host.RunAsync();
|
||||
}
|
||||
@@ -43,16 +46,7 @@ namespace Notesnook.API
|
||||
.UseKestrel((options) =>
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = long.MaxValue;
|
||||
#if DEBUG
|
||||
options.ListenAnyIP(int.Parse(Servers.NotesnookAPI.Port));
|
||||
#else
|
||||
options.ListenAnyIP(443, listenerOptions =>
|
||||
{
|
||||
listenerOptions.UseHttps(Servers.OriginSSLCertificate);
|
||||
});
|
||||
options.ListenAnyIP(80);
|
||||
options.Listen(IPAddress.Parse(Servers.NotesnookAPI.Hostname), int.Parse(Servers.NotesnookAPI.Port));
|
||||
#endif
|
||||
options.ListenAnyIP(Servers.NotesnookAPI.Port);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -41,7 +42,6 @@ namespace Notesnook.API.Services
|
||||
|
||||
public S3Service()
|
||||
{
|
||||
|
||||
var config = new AmazonS3Config
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
@@ -29,6 +29,7 @@ using Notesnook.API.Models.Responses;
|
||||
using Streetwriters.Common;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Extensions;
|
||||
using Streetwriters.Common.Interfaces;
|
||||
using Streetwriters.Common.Messages;
|
||||
using Streetwriters.Common.Models;
|
||||
using Streetwriters.Data.Interfaces;
|
||||
@@ -73,14 +74,17 @@ namespace Notesnook.API.Services
|
||||
Salt = GetSalt()
|
||||
});
|
||||
|
||||
await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage
|
||||
if (!Constants.IS_SELF_HOSTED)
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
Provider = SubscriptionProvider.STREETWRITERS,
|
||||
Type = SubscriptionType.BASIC,
|
||||
UserId = response.UserId,
|
||||
StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
});
|
||||
await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
Provider = SubscriptionProvider.STREETWRITERS,
|
||||
Type = SubscriptionType.BASIC,
|
||||
UserId = response.UserId,
|
||||
StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
});
|
||||
}
|
||||
|
||||
await Slogger<UserService>.Info(nameof(CreateUserAsync), "New user created.", JsonSerializer.Serialize(response));
|
||||
}
|
||||
@@ -90,31 +94,49 @@ namespace Notesnook.API.Services
|
||||
UserResponse response = await httpClient.ForwardAsync<UserResponse>(this.HttpContextAccessor, $"{Servers.IdentityServer.ToString()}/account", HttpMethod.Get);
|
||||
if (!response.Success) return response;
|
||||
|
||||
SubscriptionResponse subscriptionResponse = await httpClient.ForwardAsync<SubscriptionResponse>(this.HttpContextAccessor, $"{Servers.SubscriptionServer}/subscriptions", HttpMethod.Get);
|
||||
if (repair && subscriptionResponse.StatusCode == 404)
|
||||
ISubscription subscription = null;
|
||||
if (Constants.IS_SELF_HOSTED)
|
||||
{
|
||||
await Slogger<UserService>.Error(nameof(GetUserAsync), "Repairing user subscription.", JsonSerializer.Serialize(response));
|
||||
// user was partially created. We should continue the process here.
|
||||
await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage
|
||||
subscription = new Subscription
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
Provider = SubscriptionProvider.STREETWRITERS,
|
||||
Type = SubscriptionType.TRIAL,
|
||||
UserId = response.UserId,
|
||||
StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
ExpiryTime = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds()
|
||||
});
|
||||
// just a dummy object
|
||||
subscriptionResponse.Subscription = new Subscription
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
Provider = SubscriptionProvider.STREETWRITERS,
|
||||
Type = SubscriptionType.TRIAL,
|
||||
Type = SubscriptionType.PREMIUM,
|
||||
UserId = response.UserId,
|
||||
StartDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
ExpiryDate = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds()
|
||||
// this date doesn't matter as the subscription is static.
|
||||
ExpiryDate = DateTimeOffset.UtcNow.AddYears(1).ToUnixTimeMilliseconds()
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
SubscriptionResponse subscriptionResponse = await httpClient.ForwardAsync<SubscriptionResponse>(this.HttpContextAccessor, $"{Servers.SubscriptionServer}/subscriptions", HttpMethod.Get);
|
||||
if (repair && subscriptionResponse.StatusCode == 404)
|
||||
{
|
||||
await Slogger<UserService>.Error(nameof(GetUserAsync), "Repairing user subscription.", JsonSerializer.Serialize(response));
|
||||
// user was partially created. We should continue the process here.
|
||||
await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.CreateSubscriptionTopic, new CreateSubscriptionMessage
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
Provider = SubscriptionProvider.STREETWRITERS,
|
||||
Type = SubscriptionType.TRIAL,
|
||||
UserId = response.UserId,
|
||||
StartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
ExpiryTime = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds()
|
||||
});
|
||||
// just a dummy object
|
||||
subscriptionResponse.Subscription = new Subscription
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
Provider = SubscriptionProvider.STREETWRITERS,
|
||||
Type = SubscriptionType.TRIAL,
|
||||
UserId = response.UserId,
|
||||
StartDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
ExpiryDate = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeMilliseconds()
|
||||
};
|
||||
}
|
||||
subscription = subscriptionResponse.Subscription;
|
||||
}
|
||||
|
||||
var userSettings = await Repositories.UsersSettings.FindOneAsync((u) => u.UserId == response.UserId);
|
||||
if (repair && userSettings == null)
|
||||
@@ -130,7 +152,7 @@ namespace Notesnook.API.Services
|
||||
}
|
||||
response.AttachmentsKey = userSettings.AttachmentsKey;
|
||||
response.Salt = userSettings.Salt;
|
||||
response.Subscription = subscriptionResponse.Subscription;
|
||||
response.Subscription = subscription;
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -153,11 +175,14 @@ namespace Notesnook.API.Services
|
||||
Repositories.Attachments.DeleteByUserId(userId);
|
||||
Repositories.UsersSettings.Delete((u) => u.UserId == userId);
|
||||
|
||||
await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.DeleteSubscriptionTopic, new DeleteSubscriptionMessage
|
||||
if (!Constants.IS_SELF_HOSTED)
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
UserId = userId
|
||||
});
|
||||
await WampServers.SubscriptionServer.PublishMessageAsync(WampServers.SubscriptionServer.Topics.DeleteSubscriptionTopic, new DeleteSubscriptionMessage
|
||||
{
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
UserId = userId
|
||||
});
|
||||
}
|
||||
|
||||
await WampServers.MessengerServer.PublishMessageAsync(WampServers.MessengerServer.Topics.SendSSETopic, new SendSSEMessage
|
||||
{
|
||||
|
||||
@@ -73,7 +73,11 @@ namespace Notesnook.API
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var dbSettings = Configuration.GetSection("MongoDbSettings").Get<DbSettings>();
|
||||
var dbSettings = new DbSettings
|
||||
{
|
||||
ConnectionString = Constants.MONGODB_CONNECTION_STRING,
|
||||
DatabaseName = Constants.MONGODB_DATABASE_NAME
|
||||
};
|
||||
services.AddSingleton<IDbSettings>(dbSettings);
|
||||
|
||||
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
@@ -22,13 +22,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Interfaces;
|
||||
using Streetwriters.Common.Messages;
|
||||
using Streetwriters.Common.Models;
|
||||
|
||||
namespace Streetwriters.Common
|
||||
{
|
||||
public class Clients
|
||||
{
|
||||
private static IClient Notesnook = new Client
|
||||
private static Client Notesnook = new Client
|
||||
{
|
||||
Id = "notesnook",
|
||||
Name = "Notesnook",
|
||||
@@ -36,20 +37,34 @@ namespace Streetwriters.Common
|
||||
SenderName = Constants.NOTESNOOK_SENDER_NAME,
|
||||
Type = ApplicationType.NOTESNOOK,
|
||||
AppId = ApplicationType.NOTESNOOK,
|
||||
AccountRecoveryRedirectURL = $"{Constants.NOTESNOOK_APP_HOST}/account/verified",
|
||||
EmailConfirmedRedirectURL = $"{Constants.NOTESNOOK_APP_HOST}/account/recovery",
|
||||
OnEmailConfirmed = async (userId) =>
|
||||
{
|
||||
await WampServers.MessengerServer.PublishMessageAsync(WampServers.MessengerServer.Topics.SendSSETopic, new SendSSEMessage
|
||||
{
|
||||
UserId = userId,
|
||||
Message = new Message
|
||||
{
|
||||
Type = "emailConfirmed",
|
||||
Data = null
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public static Dictionary<string, IClient> ClientsMap = new Dictionary<string, IClient>
|
||||
public static Dictionary<string, Client> ClientsMap = new Dictionary<string, Client>
|
||||
{
|
||||
{ "notesnook", Notesnook }
|
||||
};
|
||||
|
||||
public static IClient FindClientById(string id)
|
||||
public static Client FindClientById(string id)
|
||||
{
|
||||
if (!IsValidClient(id)) return null;
|
||||
return ClientsMap[id];
|
||||
}
|
||||
|
||||
public static IClient FindClientByAppId(ApplicationType appId)
|
||||
public static Client FindClientByAppId(ApplicationType appId)
|
||||
{
|
||||
switch (appId)
|
||||
{
|
||||
|
||||
@@ -23,25 +23,47 @@ namespace Streetwriters.Common
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
public static bool IS_SELF_HOSTED => Environment.GetEnvironmentVariable("SELF_HOSTED") == "1";
|
||||
|
||||
// S3 related
|
||||
public static string S3_ACCESS_KEY = Environment.GetEnvironmentVariable("S3_ACCESS_KEY");
|
||||
public static string S3_ACCESS_KEY_ID = Environment.GetEnvironmentVariable("S3_ACCESS_KEY_ID");
|
||||
public static string S3_SERVICE_URL = Environment.GetEnvironmentVariable("S3_HOST");
|
||||
public static string S3_REGION = Environment.GetEnvironmentVariable("S3_HOST");
|
||||
public static string S3_ACCESS_KEY => Environment.GetEnvironmentVariable("S3_ACCESS_KEY");
|
||||
public static string S3_ACCESS_KEY_ID => Environment.GetEnvironmentVariable("S3_ACCESS_KEY_ID");
|
||||
public static string S3_SERVICE_URL => Environment.GetEnvironmentVariable("S3_SERVICE_URL");
|
||||
public static string S3_REGION => Environment.GetEnvironmentVariable("S3_REGION");
|
||||
|
||||
// SMTP settings
|
||||
public static string SMTP_USERNAME = Environment.GetEnvironmentVariable("SMTP_USERNAME");
|
||||
public static string SMTP_PASSWORD = Environment.GetEnvironmentVariable("SMTP_PASSWORD");
|
||||
public static string SMTP_HOST = Environment.GetEnvironmentVariable("SMTP_HOST");
|
||||
public static string SMTP_PORT = Environment.GetEnvironmentVariable("SMTP_PORT");
|
||||
public static string SMTP_REPLYTO_NAME = Environment.GetEnvironmentVariable("SMTP_REPLYTO_NAME");
|
||||
public static string SMTP_REPLYTO_EMAIL = Environment.GetEnvironmentVariable("SMTP_REPLYTO_EMAIL");
|
||||
public static string NOTESNOOK_SENDER_EMAIL = Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL");
|
||||
public static string NOTESNOOK_SENDER_NAME = Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_NAME");
|
||||
public static string SMTP_USERNAME => Environment.GetEnvironmentVariable("SMTP_USERNAME");
|
||||
public static string SMTP_PASSWORD => Environment.GetEnvironmentVariable("SMTP_PASSWORD");
|
||||
public static string SMTP_HOST => Environment.GetEnvironmentVariable("SMTP_HOST");
|
||||
public static string SMTP_PORT => Environment.GetEnvironmentVariable("SMTP_PORT");
|
||||
public static string SMTP_REPLYTO_NAME => Environment.GetEnvironmentVariable("SMTP_REPLYTO_NAME");
|
||||
public static string SMTP_REPLYTO_EMAIL => Environment.GetEnvironmentVariable("SMTP_REPLYTO_EMAIL");
|
||||
public static string NOTESNOOK_SENDER_EMAIL => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_EMAIL");
|
||||
public static string NOTESNOOK_SENDER_NAME => Environment.GetEnvironmentVariable("NOTESNOOK_SENDER_NAME");
|
||||
|
||||
public static string NOTESNOOK_API_SECRET = Environment.GetEnvironmentVariable("NOTESNOOK_API_SECRET");
|
||||
public static string NOTESNOOK_APP_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_APP_HOST");
|
||||
public static string NOTESNOOK_API_SECRET => Environment.GetEnvironmentVariable("NOTESNOOK_API_SECRET");
|
||||
|
||||
// MessageBird is used for SMS sending
|
||||
public static string MESSAGEBIRD_ACCESS_KEY = Environment.GetEnvironmentVariable("MESSAGEBIRD_ACCESS_KEY");
|
||||
public static string MESSAGEBIRD_ACCESS_KEY => Environment.GetEnvironmentVariable("MESSAGEBIRD_ACCESS_KEY");
|
||||
|
||||
// internal
|
||||
public static string ORIGIN_CERT_PATH => Environment.GetEnvironmentVariable("ORIGIN_CERT_PATH");
|
||||
public static string ORIGIN_CERT_KEY_PATH => Environment.GetEnvironmentVariable("ORIGIN_CERT_KEY_PATH");
|
||||
public static string MONGODB_CONNECTION_STRING => Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING");
|
||||
public static string MONGODB_DATABASE_NAME => Environment.GetEnvironmentVariable("MONGODB_DATABASE_NAME");
|
||||
|
||||
// Server discovery
|
||||
public static int NOTESNOOK_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_PORT"));
|
||||
public static string NOTESNOOK_SERVER_HOST => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_HOST");
|
||||
public static string NOTESNOOK_SERVER_DOMAIN => Environment.GetEnvironmentVariable("NOTESNOOK_SERVER_DOMAIN");
|
||||
|
||||
public static int IDENTITY_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("IDENTITY_SERVER_PORT"));
|
||||
public static string IDENTITY_SERVER_HOST => Environment.GetEnvironmentVariable("IDENTITY_SERVER_HOST");
|
||||
public static string IDENTITY_SERVER_DOMAIN => Environment.GetEnvironmentVariable("IDENTITY_SERVER_DOMAIN");
|
||||
|
||||
public static int SSE_SERVER_PORT => int.Parse(Environment.GetEnvironmentVariable("SSE_SERVER_PORT"));
|
||||
public static string SSE_SERVER_HOST => Environment.GetEnvironmentVariable("SSE_SERVER_HOST");
|
||||
public static string SSE_SERVER_DOMAIN => Environment.GetEnvironmentVariable("SSE_SERVER_DOMAIN");
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Streetwriters.Common.Enums;
|
||||
|
||||
namespace Streetwriters.Common.Interfaces
|
||||
@@ -29,5 +31,8 @@ namespace Streetwriters.Common.Interfaces
|
||||
ApplicationType AppId { get; set; }
|
||||
string SenderEmail { get; set; }
|
||||
string SenderName { get; set; }
|
||||
string EmailConfirmedRedirectURL { get; }
|
||||
string AccountRecoveryRedirectURL { get; }
|
||||
Func<string, Task> OnEmailConfirmed { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@ You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using Streetwriters.Common.Enums;
|
||||
@@ -35,5 +37,9 @@ namespace Streetwriters.Common.Models
|
||||
public ApplicationType AppId { get; set; }
|
||||
public string SenderEmail { get; set; }
|
||||
public string SenderName { get; set; }
|
||||
public string EmailConfirmedRedirectURL { get; set; }
|
||||
public string AccountRecoveryRedirectURL { get; set; }
|
||||
|
||||
public Func<string, Task> OnEmailConfirmed { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Streetwriters.Common
|
||||
{
|
||||
public class Server
|
||||
{
|
||||
public string Port { get; set; }
|
||||
public int Port { get; set; }
|
||||
public bool IsSecure { get; set; }
|
||||
public string Hostname { get; set; }
|
||||
public string Domain { get; set; }
|
||||
@@ -38,7 +38,7 @@ namespace Streetwriters.Common
|
||||
var url = "";
|
||||
url += IsSecure ? "https" : "http";
|
||||
url += $"://{Hostname}";
|
||||
url += IsSecure ? "" : $":{Port}";
|
||||
url += IsSecure || Port == 80 ? "" : $":{Port}";
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Streetwriters.Common
|
||||
var url = "";
|
||||
url += IsSecure ? "ws" : "ws";
|
||||
url += $"://{Hostname}";
|
||||
url += $":{Port}";
|
||||
url += Port == 80 ? "" : $":{Port}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@@ -77,74 +77,39 @@ namespace Streetwriters.Common
|
||||
public readonly static string HOST = GetLocalIPv4(NetworkInterfaceType.Ethernet);
|
||||
public static Server S3Server { get; } = new()
|
||||
{
|
||||
Port = "4568",
|
||||
Port = 4568,
|
||||
Hostname = HOST,
|
||||
IsSecure = false,
|
||||
Domain = HOST
|
||||
};
|
||||
#else
|
||||
private readonly static string HOST = "localhost";
|
||||
public readonly static X509Certificate2 OriginSSLCertificate = X509Certificate2.CreateFromPemFile(Environment.GetEnvironmentVariable("ORIGIN_CERT_PATH"), Environment.GetEnvironmentVariable("ORIGIN_CERT_KEY_PATH"));
|
||||
public readonly static X509Certificate2 OriginSSLCertificate = string.IsNullOrEmpty(Constants.ORIGIN_CERT_PATH) || string.IsNullOrEmpty(Constants.ORIGIN_CERT_KEY_PATH) ? null : X509Certificate2.CreateFromPemFile(Constants.ORIGIN_CERT_PATH, Environment.GetEnvironmentVariable(Constants.ORIGIN_CERT_KEY_PATH));
|
||||
#endif
|
||||
public static Server NotesnookAPI { get; } = new()
|
||||
{
|
||||
Domain = "api.notesnook.com",
|
||||
Port = "5264",
|
||||
#if DEBUG
|
||||
IsSecure = false,
|
||||
Hostname = HOST,
|
||||
#else
|
||||
IsSecure = true,
|
||||
Hostname = "10.0.0.5",
|
||||
#endif
|
||||
Domain = Constants.NOTESNOOK_SERVER_DOMAIN,
|
||||
Port = Constants.NOTESNOOK_SERVER_PORT,
|
||||
Hostname = Constants.NOTESNOOK_SERVER_HOST,
|
||||
};
|
||||
|
||||
public static Server MessengerServer { get; } = new()
|
||||
{
|
||||
Domain = "events.streetwriters.co",
|
||||
Port = "7264",
|
||||
#if DEBUG
|
||||
IsSecure = false,
|
||||
Hostname = HOST,
|
||||
#else
|
||||
IsSecure = true,
|
||||
Hostname = "10.0.0.6",
|
||||
#endif
|
||||
Domain = Constants.SSE_SERVER_DOMAIN,
|
||||
Port = Constants.SSE_SERVER_PORT,
|
||||
Hostname = Constants.SSE_SERVER_HOST,
|
||||
};
|
||||
|
||||
public static Server IdentityServer { get; } = new()
|
||||
{
|
||||
Domain = "auth.streetwriters.co",
|
||||
IsSecure = false,
|
||||
Port = "8264",
|
||||
#if DEBUG
|
||||
Hostname = HOST,
|
||||
#else
|
||||
Hostname = "10.0.0.4",
|
||||
#endif
|
||||
Domain = Constants.IDENTITY_SERVER_DOMAIN,
|
||||
Port = Constants.IDENTITY_SERVER_PORT,
|
||||
Hostname = Constants.IDENTITY_SERVER_HOST,
|
||||
};
|
||||
|
||||
public static Server SubscriptionServer { get; } = new()
|
||||
{
|
||||
Domain = "subscriptions.streetwriters.co",
|
||||
IsSecure = false,
|
||||
Port = "9264",
|
||||
#if DEBUG
|
||||
Hostname = HOST,
|
||||
#else
|
||||
Hostname = "10.0.0.4",
|
||||
#endif
|
||||
};
|
||||
public static Server PaymentsServer { get; } = new()
|
||||
{
|
||||
Domain = "payments.streetwriters.co",
|
||||
IsSecure = false,
|
||||
Port = "6264",
|
||||
#if DEBUG
|
||||
Hostname = HOST,
|
||||
#else
|
||||
Hostname = "10.0.0.4",
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -29,13 +28,10 @@ using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoDB.Bson;
|
||||
using Streetwriters.Common;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Messages;
|
||||
using Streetwriters.Common.Models;
|
||||
using Streetwriters.Identity.Enums;
|
||||
using Streetwriters.Identity.Handlers;
|
||||
using Streetwriters.Identity.Interfaces;
|
||||
using Streetwriters.Identity.Models;
|
||||
using static IdentityServer4.IdentityServerConstants;
|
||||
@@ -80,16 +76,15 @@ namespace Streetwriters.Identity.Controllers
|
||||
var result = await UserManager.ConfirmEmailAsync(user, code);
|
||||
if (!result.Succeeded) return BadRequest(result.Errors.ToErrors());
|
||||
|
||||
foreach (var handler in ClientHandlers.Handlers)
|
||||
|
||||
if (await UserManager.IsInRoleAsync(user, client.Id))
|
||||
{
|
||||
if (await UserManager.IsInRoleAsync(user, client.Id))
|
||||
{
|
||||
await handler.Value.OnEmailConfirmed(userId);
|
||||
// if (client.WelcomeEmailTemplateId != null)
|
||||
// await EmailSender.SendWelcomeEmailAsync(user.Email, client);
|
||||
}
|
||||
await client.OnEmailConfirmed(userId);
|
||||
// if (client.WelcomeEmailTemplateId != null)
|
||||
// await EmailSender.SendWelcomeEmailAsync(user.Email, client);
|
||||
}
|
||||
var redirectUrl = $"{ClientHandlers.GetClientHandler(client.Type)?.EmailConfirmedRedirectURL}?userId={userId}";
|
||||
|
||||
var redirectUrl = $"{client.EmailConfirmedRedirectURL}?userId={userId}";
|
||||
return RedirectPermanent(redirectUrl);
|
||||
}
|
||||
// case TokenType.CHANGE_EMAIL:
|
||||
@@ -111,7 +106,7 @@ namespace Streetwriters.Identity.Controllers
|
||||
return BadRequest("Invalid token.");
|
||||
|
||||
var authorizationCode = await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "PasswordResetAuthorizationCode");
|
||||
var redirectUrl = $"{ClientHandlers.GetClientHandler(client.Type)?.AccountRecoveryRedirectURL}?userId={userId}&code={authorizationCode}";
|
||||
var redirectUrl = $"{client.AccountRecoveryRedirectURL}?userId={userId}&code={authorizationCode}";
|
||||
return RedirectPermanent(redirectUrl);
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -17,6 +17,7 @@ You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
@@ -30,6 +31,7 @@ using Streetwriters.Common.Models;
|
||||
using Streetwriters.Identity.Enums;
|
||||
using Streetwriters.Identity.Interfaces;
|
||||
using Streetwriters.Identity.Models;
|
||||
using Streetwriters.Identity.Services;
|
||||
|
||||
namespace Streetwriters.Identity.Controllers
|
||||
{
|
||||
@@ -97,7 +99,8 @@ namespace Streetwriters.Identity.Controllers
|
||||
var user = await UserManager.FindByEmailAsync(form.Email);
|
||||
|
||||
await UserManager.AddToRoleAsync(user, client.Id);
|
||||
// await UserManager.AddClaimAsync(user, new Claim("verified", "false"));
|
||||
if (Constants.IS_SELF_HOSTED)
|
||||
await UserManager.AddClaimAsync(user, UserService.SubscriptionTypeToClaim(client.Id, Common.Enums.SubscriptionType.PREMIUM));
|
||||
|
||||
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var callbackUrl = Url.TokenLink(user.Id.ToString(), code, client.Id, TokenType.CONFRIM_EMAIL, Request.Scheme);
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2022 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the Affero GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
Affero GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Streetwriters.Common;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Identity.Interfaces;
|
||||
|
||||
namespace Streetwriters.Identity.Handlers
|
||||
{
|
||||
public class ClientHandlers
|
||||
{
|
||||
public static Dictionary<ApplicationType, IAppHandler> Handlers { get; set; } = new Dictionary<ApplicationType, IAppHandler>
|
||||
{
|
||||
{ ApplicationType.NOTESNOOK, new NotesnookHandler() }
|
||||
};
|
||||
|
||||
public static IAppHandler GetClientHandler(ApplicationType type)
|
||||
{
|
||||
return Handlers[type];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2022 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the Affero GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
Affero GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Streetwriters.Common;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Messages;
|
||||
using Streetwriters.Identity.Interfaces;
|
||||
|
||||
namespace Streetwriters.Identity.Handlers
|
||||
{
|
||||
public class NotesnookHandler : IAppHandler
|
||||
{
|
||||
public string Host { get; }
|
||||
public string EmailConfirmedRedirectURL { get; }
|
||||
public string AccountRecoveryRedirectURL { get; }
|
||||
|
||||
public NotesnookHandler()
|
||||
{
|
||||
#if DEBUG
|
||||
Host = "http://localhost:3000";
|
||||
#else
|
||||
Host = "https://app.notesnook.com";
|
||||
#endif
|
||||
EmailConfirmedRedirectURL = $"{this.Host}/account/verified";
|
||||
AccountRecoveryRedirectURL = $"{this.Host}/account/recovery";
|
||||
}
|
||||
public async Task OnEmailConfirmed(string userId)
|
||||
{
|
||||
await WampServers.MessengerServer.PublishMessageAsync(WampServers.MessengerServer.Topics.SendSSETopic, new SendSSEMessage
|
||||
{
|
||||
UserId = userId,
|
||||
Message = new Message
|
||||
{
|
||||
Type = "emailConfirmed",
|
||||
Data = null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook Sync Server project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2022 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the Affero GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
Affero GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Streetwriters.Identity.Interfaces
|
||||
{
|
||||
public interface IAppHandler
|
||||
{
|
||||
string Host { get; }
|
||||
string EmailConfirmedRedirectURL { get; }
|
||||
string AccountRecoveryRedirectURL { get; }
|
||||
Task OnEmailConfirmed(string userId);
|
||||
}
|
||||
}
|
||||
@@ -21,16 +21,9 @@ using System.Threading.Tasks;
|
||||
using Streetwriters.Common.Messages;
|
||||
using Streetwriters.Common.Models;
|
||||
using Streetwriters.Common;
|
||||
using System.Text.Json;
|
||||
using Streetwriters.Data.Repositories;
|
||||
using Streetwriters.Data.Interfaces;
|
||||
using Streetwriters.Common.Interfaces;
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Streetwriters.Common.Enums;
|
||||
using System.Security.Claims;
|
||||
using System.Linq;
|
||||
using Streetwriters.Identity.Interfaces;
|
||||
using Streetwriters.Identity.Services;
|
||||
|
||||
namespace Streetwriters.Identity.MessageHandlers
|
||||
|
||||
@@ -17,21 +17,12 @@ You should have received a copy of the Affero GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Messages;
|
||||
using Streetwriters.Common.Interfaces;
|
||||
using Streetwriters.Common.Models;
|
||||
using Streetwriters.Common;
|
||||
using System.Text.Json;
|
||||
using System.IO;
|
||||
using Streetwriters.Data.Repositories;
|
||||
using Streetwriters.Data.Interfaces;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Linq;
|
||||
using IdentityServer4.Stores;
|
||||
using Streetwriters.Identity.Interfaces;
|
||||
|
||||
namespace Streetwriters.Identity.MessageHandlers
|
||||
{
|
||||
|
||||
@@ -33,8 +33,6 @@ namespace Streetwriters.Identity
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
DotNetEnv.Env.TraversePath().Load();
|
||||
|
||||
IHost host = CreateHostBuilder(args).Build();
|
||||
await host.RunAsync();
|
||||
}
|
||||
@@ -48,7 +46,13 @@ namespace Streetwriters.Identity
|
||||
})
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>().UseUrls(Servers.IdentityServer.ToString());
|
||||
webBuilder
|
||||
.UseStartup<Startup>()
|
||||
.UseKestrel((options) =>
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = long.MaxValue;
|
||||
options.ListenAnyIP(Servers.IdentityServer.Port);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,11 @@ namespace Streetwriters.Identity.Services
|
||||
public class EmailSender : IEmailSender, IAsyncDisposable
|
||||
{
|
||||
NNGnuPGContext NNGnuPGContext { get; set; }
|
||||
SmtpClient mailClient;
|
||||
public EmailSender(IConfiguration configuration)
|
||||
{
|
||||
NNGnuPGContext = new NNGnuPGContext(configuration.GetSection("PgpKeySettings"));
|
||||
mailClient = new SmtpClient();
|
||||
}
|
||||
|
||||
EmailTemplate Email2FATemplate = new EmailTemplate
|
||||
@@ -90,12 +92,6 @@ namespace Streetwriters.Identity.Services
|
||||
Subject = "Failed login attempt on your {{app_name}} account",
|
||||
};
|
||||
|
||||
SmtpClient mailClient;
|
||||
public EmailSender()
|
||||
{
|
||||
mailClient = new SmtpClient();
|
||||
}
|
||||
|
||||
public async Task Send2FACodeEmailAsync(string email, string code, IClient client)
|
||||
{
|
||||
var template = new EmailTemplate
|
||||
@@ -179,34 +175,40 @@ namespace Streetwriters.Identity.Services
|
||||
|
||||
private async Task SendEmailAsync(string email, IEmailTemplate template, IClient client)
|
||||
{
|
||||
if (!mailClient.IsConnected)
|
||||
{
|
||||
if (int.TryParse(Constants.SMTP_PORT, out int port))
|
||||
{
|
||||
await mailClient.ConnectAsync(Constants.SMTP_HOST, port, MailKit.Security.SecureSocketOptions.StartTls);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("SMTP_PORT is not a valid integer value.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!mailClient.IsAuthenticated)
|
||||
await mailClient.AuthenticateAsync(Constants.SMTP_USERNAME, Constants.SMTP_PASSWORD);
|
||||
|
||||
var message = new MimeMessage();
|
||||
var sender = new MailboxAddress(client.SenderName, client.SenderEmail);
|
||||
message.From.Add(sender);
|
||||
message.To.Add(new MailboxAddress("", email));
|
||||
message.Subject = await Template.Parse(template.Subject).RenderAsync(template.Data);
|
||||
|
||||
if (!string.IsNullOrEmpty(Constants.SMTP_REPLYTO_NAME) && !string.IsNullOrEmpty(Constants.SMTP_REPLYTO_EMAIL))
|
||||
message.ReplyTo.Add(new MailboxAddress(Constants.SMTP_REPLYTO_NAME, Constants.SMTP_REPLYTO_EMAIL));
|
||||
|
||||
message.Body = await GetEmailBodyAsync(template, client, sender);
|
||||
|
||||
await mailClient.SendAsync(message);
|
||||
}
|
||||
|
||||
private async Task<MimeEntity> GetEmailBodyAsync(IEmailTemplate template, IClient client, MailboxAddress sender)
|
||||
{
|
||||
var builder = new BodyBuilder();
|
||||
try
|
||||
{
|
||||
if (!mailClient.IsConnected)
|
||||
{
|
||||
if (int.TryParse(Constants.SMTP_PORT, out int port))
|
||||
{
|
||||
await mailClient.ConnectAsync(Constants.SMTP_HOST, port, MailKit.Security.SecureSocketOptions.StartTls);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("SMTP_PORT is not a valid integer value.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!mailClient.IsAuthenticated)
|
||||
await mailClient.AuthenticateAsync(Constants.SMTP_USERNAME, Constants.SMTP_PASSWORD);
|
||||
|
||||
var message = new MimeMessage();
|
||||
var sender = new MailboxAddress(client.SenderName, client.SenderEmail);
|
||||
message.From.Add(sender);
|
||||
message.To.Add(new MailboxAddress("", email));
|
||||
message.Subject = await Template.Parse(template.Subject).RenderAsync(template.Data);
|
||||
|
||||
if (!string.IsNullOrEmpty(Constants.SMTP_REPLYTO_NAME) && !string.IsNullOrEmpty(Constants.SMTP_REPLYTO_EMAIL))
|
||||
message.ReplyTo.Add(new MailboxAddress(Constants.SMTP_REPLYTO_NAME, Constants.SMTP_REPLYTO_EMAIL));
|
||||
|
||||
var builder = new BodyBuilder();
|
||||
|
||||
builder.TextBody = await Template.Parse(template.Text).RenderAsync(template.Data);
|
||||
builder.HtmlBody = await Template.Parse(template.Html).RenderAsync(template.Data);
|
||||
|
||||
@@ -222,17 +224,16 @@ namespace Streetwriters.Identity.Services
|
||||
outputStream.Seek(0, SeekOrigin.Begin);
|
||||
builder.Attachments.Add($"{client.Id}_pub.asc", Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(outputStream.ToArray())));
|
||||
}
|
||||
message.Body = MultipartSigned.Create(NNGnuPGContext, sender, DigestAlgorithm.Sha256, builder.ToMessageBody());
|
||||
return MultipartSigned.Create(NNGnuPGContext, sender, DigestAlgorithm.Sha256, builder.ToMessageBody());
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Body = builder.ToMessageBody();
|
||||
return builder.ToMessageBody();
|
||||
}
|
||||
await mailClient.SendAsync(message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (PrivateKeyNotFoundException)
|
||||
{
|
||||
Console.Error.WriteLine(ex.Message);
|
||||
return builder.ToMessageBody();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace Streetwriters.Identity.Services
|
||||
private Client client;
|
||||
public SMSSender()
|
||||
{
|
||||
client = Client.CreateDefault(Constants.MESSAGEBIRD_ACCESS_KEY);
|
||||
if (!string.IsNullOrEmpty(Constants.MESSAGEBIRD_ACCESS_KEY))
|
||||
client = Client.CreateDefault(Constants.MESSAGEBIRD_ACCESS_KEY);
|
||||
}
|
||||
|
||||
public string SendOTP(string number, IClient app)
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Streetwriters.Identity
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var connectionString = Configuration["MongoDbSettings:ConnectionString"];
|
||||
var connectionString = Constants.MONGODB_CONNECTION_STRING;
|
||||
|
||||
services.AddTransient<IEmailSender, EmailSender>();
|
||||
services.AddTransient<ISMSSender, SMSSender>();
|
||||
|
||||
@@ -45,16 +45,7 @@ namespace Streetwriters.Messenger
|
||||
.UseKestrel((options) =>
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = long.MaxValue;
|
||||
#if DEBUG
|
||||
options.ListenAnyIP(int.Parse(Servers.MessengerServer.Port));
|
||||
#else
|
||||
options.ListenAnyIP(443, listenerOptions =>
|
||||
{
|
||||
listenerOptions.UseHttps(Servers.OriginSSLCertificate);
|
||||
});
|
||||
options.ListenAnyIP(80);
|
||||
options.Listen(IPAddress.Parse(Servers.MessengerServer.Hostname), int.Parse(Servers.MessengerServer.Port));
|
||||
#endif
|
||||
options.ListenAnyIP(Servers.MessengerServer.Port);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user