open source Notesnook API

This commit is contained in:
Abdullah Atta
2022-12-28 16:20:25 +05:00
parent cf406454cd
commit d2217adce7
85 changed files with 4137 additions and 0 deletions
@@ -0,0 +1,14 @@
using System;
using System.Text.Json.Serialization;
namespace Streetwriters.Common.Attributes
{
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
public class JsonInterfaceConverterAttribute : JsonConverterAttribute
{
public JsonInterfaceConverterAttribute(Type converterType)
: base(converterType)
{
}
}
}
+93
View File
@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
using Streetwriters.Common.Models;
namespace Streetwriters.Common
{
public class Clients
{
private static IClient Notesnook = new Client
{
Id = "notesnook",
Name = "Notesnook",
ProductIds = new string[]
{
"com.streetwriters.notesnook",
"org.streetwriters.notesnook",
"com.streetwriters.notesnook.sub.mo",
"com.streetwriters.notesnook.sub.yr",
"com.streetwriters.notesnook.sub.mo.15",
"com.streetwriters.notesnook.sub.yr.15",
"com.streetwriters.notesnook.sub.yr.trialoffer",
"com.streetwriters.notesnook.sub.mo.trialoffer",
"com.streetwriters.notesnook.sub.mo.tier1",
"com.streetwriters.notesnook.sub.yr.tier1",
"com.streetwriters.notesnook.sub.mo.tier2",
"com.streetwriters.notesnook.sub.yr.tier2",
"com.streetwriters.notesnook.sub.mo.tier3",
"com.streetwriters.notesnook.sub.yr.tier3",
"9822", // dev
"648884", // monthly tier 1
"658759", // yearly tier 1
"763942", // monthly tier 2
"763945", // yearly tier 2
"763943", // monthly tier 3
"763944", // yearly tier 3
},
SenderEmail = "support@notesnook.com",
SenderName = "Notesnook",
Type = ApplicationType.NOTESNOOK,
AppId = ApplicationType.NOTESNOOK,
WelcomeEmailTemplateId = "d-87768b3ee17d41fdbe4bcf0eb2583682"
};
public static Dictionary<string, IClient> ClientsMap = new Dictionary<string, IClient>
{
{ "notesnook", Notesnook }
};
public static IClient FindClientById(string id)
{
if (!IsValidClient(id)) return null;
return ClientsMap[id];
}
public static IClient FindClientByAppId(ApplicationType appId)
{
switch (appId)
{
case ApplicationType.NOTESNOOK:
return ClientsMap["notesnook"];
}
return null;
}
public static IClient FindClientByProductId(string productId)
{
foreach (var client in ClientsMap)
{
if (client.Value.ProductIds.Contains(productId)) return client.Value;
}
return null;
}
public static bool IsValidClient(string id)
{
return ClientsMap.ContainsKey(id);
}
public static SubscriptionProvider? PlatformToSubscriptionProvider(string platform)
{
return platform switch
{
"ios" => SubscriptionProvider.APPLE,
"android" => SubscriptionProvider.GOOGLE,
"web" => SubscriptionProvider.PADDLE,
_ => null,
};
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Streetwriters.Common.Converters
{
/// <summary>
/// Converts simple interface into an object (assumes that there is only one class of TInterface)
/// </summary>
/// <typeparam name="TInterface">Interface type</typeparam>
/// <typeparam name="TClass">Class type</typeparam>
public class InterfaceConverter<TInterface, TClass> : JsonConverter<TInterface> where TClass : TInterface
{
public override TInterface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<TClass>(ref reader, options);
}
public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
{
switch (value)
{
case null:
JsonSerializer.Serialize(writer, null, options);
break;
default:
{
var type = value.GetType();
JsonSerializer.Serialize(writer, value, type, options);
break;
}
}
}
}
}
@@ -0,0 +1,7 @@
namespace Streetwriters.Common.Enums
{
public enum ApplicationType
{
NOTESNOOK = 0
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Streetwriters.Common.Enums
{
public class MFAMethods
{
public static string Email => "email";
public static string SMS => "sms";
public static string App => "app";
public static string RecoveryCode => "recoveryCode";
}
}
@@ -0,0 +1,10 @@
namespace Streetwriters.Common.Enums
{
public enum SubscriptionProvider
{
STREETWRITERS = 0,
APPLE = 1,
GOOGLE = 2,
PADDLE = 3
}
}
@@ -0,0 +1,12 @@
namespace Streetwriters.Common.Enums
{
public enum SubscriptionType
{
BASIC = 0,
TRIAL = 1,
BETA = 2,
PREMIUM = 5,
PREMIUM_EXPIRED = 6,
PREMIUM_CANCELED = 7
}
}
@@ -0,0 +1,45 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using WampSharp.AspNetCore.WebSockets.Server;
using WampSharp.Binding;
using WampSharp.V2;
using WampSharp.V2.Realm;
namespace Streetwriters.Common.Extensions
{
public static class AppBuilderExtensions
{
public static IApplicationBuilder UseWamp<T>(this IApplicationBuilder app, WampServer<T> server, Action<IWampHostedRealm, WampServer<T>> action) where T : new()
{
WampHost host = new WampHost();
app.Map(server.Endpoint, builder =>
{
builder.UseWebSockets();
host.RegisterTransport(new AspNetCoreWebSocketTransport(builder),
new JTokenJsonBinding(),
new JTokenMsgpackBinding());
});
host.Open();
action.Invoke(host.RealmContainer.GetRealmByName(server.Realm), server);
return app;
}
public static T GetService<T>(this IApplicationBuilder app)
{
return app.ApplicationServices.GetRequiredService<T>();
}
public static T GetScopedService<T>(this IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.CreateScope())
{
return scope.ServiceProvider.GetRequiredService<T>();
}
}
}
}
@@ -0,0 +1,53 @@
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Extensions
{
public static class HttpClientExtensions
{
public static async Task<T> SendRequestAsync<T>(this HttpClient httpClient, string url, IHeaderDictionary headers, HttpMethod method, HttpContent content = null) where T : IResponse, new()
{
var request = new HttpRequestMessage(method, url);
if (method != HttpMethod.Get && method != HttpMethod.Delete)
{
request.Content = content;
}
foreach (var header in headers)
{
if (header.Key == "Content-Type" || header.Key == "Content-Length")
{
if (request.Content != null)
request.Content.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable());
continue;
}
request.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable());
}
var response = await httpClient.SendAsync(request);
if (response.Content.Headers.ContentLength > 0)
{
var res = await response.Content.ReadFromJsonAsync<T>();
res.Success = response.IsSuccessStatusCode;
res.StatusCode = (int)response.StatusCode;
return res;
}
else
{
return new T { Success = response.IsSuccessStatusCode, StatusCode = (int)response.StatusCode };
}
}
public static Task<T> ForwardAsync<T>(this HttpClient httpClient, IHttpContextAccessor accessor, string url, HttpMethod method) where T : IResponse, new()
{
var httpContext = accessor.HttpContext;
var content = new StreamContent(httpContext.Request.BodyReader.AsStream());
return httpClient.SendRequestAsync<T>(url, httpContext.Request.Headers, method, content);
}
}
}
@@ -0,0 +1,24 @@
namespace Microsoft.Extensions.DependencyInjection.CorsServiceCollectionExtensions
{
public static class ServiceCollectionServiceExtensions
{
public static IServiceCollection AddCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("notesnook", (b) =>
{
#if DEBUG
b.AllowAnyOrigin();
#else
b.WithOrigins("http://localhost:3000", "http://192.168.10.29:3000", "https://app.notesnook.com", "https://beta.notesnook.com", "https://budi.streetwriters.co", "http://localhost:9876");
#endif
b.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
return services;
}
}
}
@@ -0,0 +1,72 @@
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
namespace System
{
public static class StringExtensions
{
public static string ToSha256(this string rawData, int maxLength = 12)
{
// Create a SHA256
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
return ToHex(bytes, 0, maxLength);
}
}
public static byte[] CompressBrotli(this string input)
{
var raw = Encoding.Default.GetBytes(input);
using (MemoryStream memory = new MemoryStream())
{
using (BrotliStream brotli = new BrotliStream(memory, CompressionLevel.Optimal))
{
brotli.Write(raw, 0, raw.Length);
}
return memory.ToArray();
}
}
public static string DecompressBrotli(this byte[] compressed)
{
using (BrotliStream stream = new BrotliStream(new MemoryStream(compressed), CompressionMode.Decompress))
{
const int size = 4096;
byte[] buffer = new byte[size];
using (MemoryStream memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
}
while (count > 0);
return Encoding.Default.GetString(memory.ToArray());
}
}
}
private static string ToHex(byte[] bytes, int startIndex, int length)
{
char[] c = new char[length * 2];
byte b;
for (int bx = startIndex, cx = startIndex; bx < length; ++bx, ++cx)
{
b = ((byte)(bytes[bx] >> 4));
c[cx] = (char)(b > 9 ? b + 0x37 + 0x20 : b + 0x30);
b = ((byte)(bytes[bx] & 0x0F));
c[++cx] = (char)(b > 9 ? b + 0x37 + 0x20 : b + 0x30);
}
return new string(c);
}
}
}
@@ -0,0 +1,23 @@
using System;
using Microsoft.AspNetCore.Builder;
using Streetwriters.Common.Interfaces;
using WampSharp.AspNetCore.WebSockets.Server;
using WampSharp.Binding;
using WampSharp.V2;
using WampSharp.V2.Realm;
namespace Streetwriters.Common.Extensions
{
public static class WampRealmExtensions
{
public static IDisposable Subscribe<T>(this IWampHostedRealm realm, string topicName, Action<T> onNext)
{
return realm.Services.GetSubject<T>(topicName).Subscribe<T>(onNext);
}
public static IDisposable Subscribe<T>(this IWampHostedRealm realm, string topicName, IMessageHandler<T> handler)
{
return realm.Services.GetSubject<T>(topicName).Subscribe<T>(async (message) => await handler.Process(message));
}
}
}
@@ -0,0 +1,28 @@
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Streetwriters.Common.Messages;
using WampSharp.V2;
using WampSharp.V2.Client;
namespace Streetwriters.Common.Helpers
{
public class WampHelper
{
public static async Task<IWampRealmProxy> OpenWampChannelAsync<T>(string server, string realmName)
{
DefaultWampChannelFactory channelFactory = new DefaultWampChannelFactory();
IWampChannel channel = channelFactory.CreateJsonChannel(server, realmName);
await channel.Open();
return channel.RealmProxy;
}
public static void PublishMessage<T>(IWampRealmProxy realm, string topicName, T message)
{
var subject = realm.Services.GetSubject<T>(topicName);
subject.OnNext(message);
}
}
}
@@ -0,0 +1,16 @@
using Streetwriters.Common.Enums;
namespace Streetwriters.Common.Interfaces
{
public interface IClient
{
string Id { get; set; }
string Name { get; set; }
string[] ProductIds { get; set; }
ApplicationType Type { get; set; }
ApplicationType AppId { get; set; }
string SenderEmail { get; set; }
string SenderName { get; set; }
string WelcomeEmailTemplateId { get; set; }
}
}
@@ -0,0 +1,10 @@
namespace Streetwriters.Common.Interfaces
{
public interface IDocument
{
string Id
{
get; set;
}
}
}
@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Interfaces
{
public interface IMessageHandler<T>
{
Task Process(T message);
}
}
+13
View File
@@ -0,0 +1,13 @@
using System.Collections.Generic;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Models;
namespace Streetwriters.Common.Interfaces
{
public interface IOffer : IDocument
{
ApplicationType AppId { get; set; }
string PromoCode { get; set; }
PromoCode[] Codes { get; set; }
}
}
@@ -0,0 +1,8 @@
namespace Streetwriters.Common.Interfaces
{
public interface IResponse
{
bool Success { get; set; }
int StatusCode { get; set; }
}
}
@@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using Streetwriters.Common.Attributes;
using Streetwriters.Common.Converters;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Models;
namespace Streetwriters.Common.Interfaces
{
[JsonInterfaceConverter(typeof(InterfaceConverter<ISubscription, Subscription>))]
public interface ISubscription : IDocument
{
string UserId { get; set; }
ApplicationType AppId { get; set; }
SubscriptionProvider Provider { get; set; }
long StartDate { get; set; }
long ExpiryDate { get; set; }
SubscriptionType Type { get; set; }
string OrderId { get; set; }
string SubscriptionId { get; set; }
}
}
+38
View File
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
using Streetwriters.Common.Models;
namespace Streetwriters.Common
{
public class Slogger<T>
{
public static Task Info(string scope, params string[] messages)
{
return Write(Format("info", scope, messages));
}
public static Task Error(string scope, params string[] messages)
{
return Write(Format("error", scope, messages));
}
private static string Format(string level, string scope, params string[] messages)
{
var date = DateTime.UtcNow.ToString("MM-dd-yyyy HH:mm:ss");
var messageText = string.Join(" ", messages);
return $"[{date}] | {level} | <{scope}> {messageText}";
}
private static Task Write(string line)
{
var logDirectory = Path.GetFullPath("./logs");
if (!Directory.Exists(logDirectory))
Directory.CreateDirectory(logDirectory);
var path = Path.Join(logDirectory, typeof(T).FullName + "-" + DateTime.UtcNow.ToString("MM-dd-yyyy") + ".log");
return File.AppendAllLinesAsync(path, new string[1] { line });
}
}
}
@@ -0,0 +1,44 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Messages
{
public class CreateSubscriptionMessage
{
[JsonPropertyName("userId")]
public string UserId { get; set; }
[JsonPropertyName("provider")]
public SubscriptionProvider Provider { get; set; }
[JsonPropertyName("appId")]
public ApplicationType AppId { get; set; }
[JsonPropertyName("type")]
public SubscriptionType Type { get; set; }
[JsonPropertyName("start")]
public long StartTime { get; set; }
[JsonPropertyName("expiry")]
public long ExpiryTime { get; set; }
[JsonPropertyName("orderId")]
public string OrderId { get; set; }
[JsonPropertyName("updateURL")]
public string UpdateURL { get; set; }
[JsonPropertyName("cancelURL")]
public string CancelURL { get; set; }
[JsonPropertyName("subscriptionId")]
public string SubscriptionId { get; set; }
[JsonPropertyName("productId")]
public string ProductId { get; set; }
public bool Extend { get; set; }
}
}
@@ -0,0 +1,16 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Messages
{
public class DeleteSubscriptionMessage
{
[JsonPropertyName("userId")]
public string UserId { get; set; }
[JsonPropertyName("appId")]
public ApplicationType AppId { get; set; }
}
}
@@ -0,0 +1,13 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Messages
{
public class DeleteUserMessage
{
[JsonPropertyName("userId")]
public string UserId { get; set; }
}
}
@@ -0,0 +1,29 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Messages
{
public class Message
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("data")]
public string Data { get; set; }
}
public class SendSSEMessage
{
[JsonPropertyName("sendToAll")]
public bool SendToAll { get; set; }
[JsonPropertyName("userId")]
public string UserId { get; set; }
[JsonPropertyName("message")]
public Message Message { get; set; }
[JsonPropertyName("originTokenId")]
public string OriginTokenId { get; set; }
}
}
+22
View File
@@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Models
{
public class Client : IClient
{
public string Id { get; set; }
public string Name { get; set; }
public string[] ProductIds { get; set; }
public ApplicationType Type { get; set; }
public ApplicationType AppId { get; set; }
public string SenderEmail { get; set; }
public string SenderName { get; set; }
public string WelcomeEmailTemplateId { get; set; }
}
}
@@ -0,0 +1,12 @@
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Models
{
public class SubscriptionResponse : Response
{
[JsonPropertyName("subscription")]
public ISubscription Subscription { get; set; }
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Streetwriters.Common.Models
{
public class MFAConfig
{
public bool IsEnabled { get; set; }
public string PrimaryMethod { get; set; }
public string SecondaryMethod { get; set; }
public int RemainingValidCodes { get; set; }
}
}
+36
View File
@@ -0,0 +1,36 @@
// Streetwriters.Common.Models.Offer
using System.Collections.Generic;
using System.Text.Json.Serialization;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
using Streetwriters.Data.Attributes;
namespace Streetwriters.Common.Models
{
[BsonCollection("subscriptions", "offers")]
public class Offer : IOffer
{
public Offer()
{
Id = ObjectId.GenerateNewId().ToString();
}
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("appId")]
public ApplicationType AppId { get; set; }
[JsonPropertyName("promoCode")]
public string PromoCode { get; set; }
[JsonPropertyName("codes")]
public PromoCode[] Codes { get; set; }
}
}
+21
View File
@@ -0,0 +1,21 @@
// Streetwriters.Common.Models.Offer
using System.Collections.Generic;
using System.Text.Json.Serialization;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Models
{
public class PromoCode
{
[JsonPropertyName("provider")]
public SubscriptionProvider Provider { get; set; }
[JsonPropertyName("code")]
public string Code { get; set; }
}
}
+13
View File
@@ -0,0 +1,13 @@
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Streetwriters.Common.Interfaces;
namespace Streetwriters.Common.Models
{
public class Response : IResponse
{
[JsonIgnore]
public bool Success { get; set; }
public int StatusCode { get; set; }
}
}
+124
View File
@@ -0,0 +1,124 @@
using AspNetCore.Identity.Mongo.Model;
using Streetwriters.Data.Attributes;
namespace Streetwriters.Common.Models
{
[BsonCollection("identity", "roles")]
public class Role : MongoRole
{
// [DataMember(Name = "email")]
// [BsonElement("email")]
// public string Email
// {
// get; set;
// }
// [DataMember(Name = "isEmailConfirmed")]
// [BsonElement("isEmailConfirmed")]
// public bool IsEmailConfirmed { get; set; }
// [DataMember(Name = "username")]
// [BsonElement("username")]
// public string Username
// {
// get; set;
// }
// [BsonId]
// [BsonRepresentation(BsonType.ObjectId)]
// public string Id
// {
// get; set;
// }
// [IgnoreDataMember]
// [BsonElement("passwordHash")]
// public string PasswordHash
// {
// get; set;
// }
// [DataMember(Name = "salt")]
// public string Salt
// {
// get; set;
// }
}
/*
public class Picture
{
[DataMember(Name = "thumbnail")]
public string Thumbnail
{
get; set;
}
[DataMember(Name = "full")]
public string Full
{
get; set;
}
}
public class Streetwriters
{
[DataMember(Name = "fullName")]
public string FullName
{
get; set;
}
[DataMember(Name = "biography")]
[StringLength(240)]
public string Biography
{
get; set;
}
[DataMember(Name = "favoriteWords")]
public string FavoriteWords
{
get; set;
}
[DataMember(Name = "profilePicture")]
public Picture ProfilePicture
{
get; set;
}
[DataMember(Name = "followers")]
public string[] Followers
{
get; set;
}
[DataMember(Name = "following")]
public string[] Following
{
get; set;
}
[DataMember(Name = "website")]
[Url]
public string Website
{
get; set;
}
[DataMember(Name = "instagram")]
public string Instagram
{
get; set;
}
[DataMember(Name = "twitter")]
public string Twitter
{
get; set;
}
} */
}
@@ -0,0 +1,63 @@
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Streetwriters.Common.Enums;
using Streetwriters.Common.Interfaces;
using Streetwriters.Data.Attributes;
namespace Streetwriters.Common.Models
{
[BsonCollection("subscriptions", "subscriptions")]
public class Subscription : ISubscription
{
public Subscription()
{
Id = ObjectId.GenerateNewId().ToString();
}
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("userId")]
public string UserId { get; set; }
[JsonIgnore]
public string OrderId { get; set; }
[JsonIgnore]
public string SubscriptionId { get; set; }
[BsonRepresentation(BsonType.Int32)]
[JsonPropertyName("appId")]
public ApplicationType AppId { get; set; }
[JsonPropertyName("start")]
public long StartDate { get; set; }
[JsonPropertyName("expiry")]
public long ExpiryDate { get; set; }
[BsonRepresentation(BsonType.Int32)]
[JsonPropertyName("provider")]
public SubscriptionProvider Provider { get; set; }
[BsonRepresentation(BsonType.Int32)]
[JsonPropertyName("type")]
public SubscriptionType Type { get; set; }
[JsonPropertyName("cancelURL")]
public string CancelURL { get; set; }
[JsonPropertyName("updateURL")]
public string UpdateURL { get; set; }
[JsonPropertyName("productId")]
public string ProductId { get; set; }
[JsonIgnore]
public int TrialExtensionCount { get; set; }
}
}
+12
View File
@@ -0,0 +1,12 @@
using AspNetCore.Identity.Mongo.Model;
using Streetwriters.Data.Attributes;
namespace Streetwriters.Common.Models
{
[BsonCollection("identity", "users")]
public class User : MongoUser
{
}
}
+23
View File
@@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
namespace Streetwriters.Common.Models
{
public class UserModel
{
[JsonPropertyName("id")]
public string UserId { get; set; }
[JsonPropertyName("email")]
public string Email { get; set; }
[JsonPropertyName("phoneNumber")]
public string PhoneNumber { get; set; }
[JsonPropertyName("isEmailConfirmed")]
public bool IsEmailConfirmed { get; set; }
[JsonPropertyName("mfa")]
public MFAConfig MFA { get; set; }
}
}
+130
View File
@@ -0,0 +1,130 @@
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
namespace Streetwriters.Common
{
public class Server
{
public string Port { get; set; }
public bool IsSecure { get; set; }
public string Hostname { get; set; }
public string Domain { get; set; }
public override string ToString()
{
var url = "";
url += IsSecure ? "https" : "http";
url += $"://{Hostname}";
url += IsSecure ? "" : $":{Port}";
return url;
}
public string WS()
{
var url = "";
url += IsSecure ? "ws" : "ws";
url += $"://{Hostname}";
url += $":{Port}";
return url;
}
}
public class Servers
{
#if DEBUG
public static string GetLocalIPv4(NetworkInterfaceType _type)
{
var interfaces = NetworkInterface.GetAllNetworkInterfaces();
string output = "";
foreach (NetworkInterface item in interfaces)
{
if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
{
foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
output = ip.Address.ToString();
}
}
}
}
return output;
}
public readonly static string HOST = GetLocalIPv4(NetworkInterfaceType.Ethernet);
public static Server S3Server { get; } = new()
{
Port = "4568",
Hostname = HOST,
IsSecure = false,
Domain = HOST
};
#else
private readonly static string HOST = "localhost";
public readonly static X509Certificate2 OriginSSLCertificate = X509Certificate2.CreateFromPemFile("/home/notesnook/.ssl/CF_Origin_Streetwriters.pem", "/home/notesnook/.ssl/CF_Origin_Streetwriters.key");
#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
};
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
};
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
};
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
};
}
}
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.2.0" />
<PackageReference Include="WampSharp.Default" Version="20.1.1" />
<PackageReference Include="WampSharp.AspNetCore.WebSockets.Server" Version="20.1.1" />
<PackageReference Include="WampSharp.NewtonsoftMsgpack" Version="20.1.1" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="AspNetCore.Identity.Mongo" Version="8.3.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Streetwriters.Data\Streetwriters.Data.csproj" />
</ItemGroup>
</Project>
+100
View File
@@ -0,0 +1,100 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Streetwriters.Common.Helpers;
using WampSharp.V2.Client;
namespace Streetwriters.Common
{
public class WampServer<T> where T : new()
{
private readonly ConcurrentDictionary<string, IWampRealmProxy> Channels = new();
public string Endpoint { get; set; }
public string Address { get; set; }
public T Topics { get; set; } = new T();
public string Realm { get; set; }
public async Task PublishMessageAsync<V>(string topic, V message)
{
try
{
IWampRealmProxy channel;
if (Channels.ContainsKey(topic))
channel = Channels[topic];
else
{
channel = await WampHelper.OpenWampChannelAsync<V>(this.Address, this.Realm);
Channels.TryAdd(topic, channel);
}
if (!channel.Monitor.IsConnected)
{
Channels.TryRemove(topic, out IWampRealmProxy value);
await PublishMessageAsync<V>(topic, message);
return;
}
WampHelper.PublishMessage<V>(channel, topic, message);
}
catch (Exception ex)
{
await Slogger<WampServer<T>>.Error(nameof(PublishMessageAsync), ex.ToString());
throw ex;
}
}
}
public class WampServers
{
public static WampServer<MessengerServerTopics> MessengerServer { get; } = new WampServer<MessengerServerTopics>
{
Endpoint = "/wamp",
Address = $"{Servers.MessengerServer.WS()}/wamp",
Realm = "messages",
};
public static WampServer<SubscriptionServerTopics> SubscriptionServer { get; } = new WampServer<SubscriptionServerTopics>
{
Endpoint = "/wamp",
Address = $"{Servers.SubscriptionServer.WS()}/wamp",
Realm = "messages",
};
public static WampServer<IdentityServerTopics> IdentityServer { get; } = new WampServer<IdentityServerTopics>
{
Endpoint = "/wamp",
Address = $"{Servers.IdentityServer.WS()}/wamp",
Realm = "messages",
};
public static WampServer<NotesnookServerTopics> NotesnookServer { get; } = new WampServer<NotesnookServerTopics>
{
Endpoint = "/wamp",
Address = $"{Servers.NotesnookAPI.WS()}/wamp",
Realm = "messages",
};
}
public class MessengerServerTopics
{
public string SendSSETopic => "com.streetwriters.sse.send";
}
public class SubscriptionServerTopics
{
public string CreateSubscriptionTopic => "com.streetwriters.subscriptions.create";
public string DeleteSubscriptionTopic => "com.streetwriters.subscriptions.delete";
}
public class IdentityServerTopics
{
public string CreateSubscriptionTopic => "com.streetwriters.subscriptions.create";
public string DeleteSubscriptionTopic => "com.streetwriters.subscriptions.delete";
}
public class NotesnookServerTopics
{
public string DeleteUserTopic => "com.streetwriters.notesnook.user.delete";
}
}