From e47fa443f7544c711f96fbcdc0f4a34f2508ac50 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Fri, 13 Jan 2023 22:06:41 +0000 Subject: [PATCH] --- server/biz/GZCaseBiz.cs | 16 +- server/biz/KeyFactory.cs | 19 +- server/biz/LicenseBiz.cs | 22 +-- server/biz/ProductBiz.cs | 15 +- server/biz/PurchaseBiz.cs | 15 +- server/biz/RavenKeyFactory.cs | 12 +- server/biz/SubscriptionServerBiz.cs | 15 +- server/biz/TrialLicenseRequestBiz.cs | 281 +++++++++++++++++---------- server/util/StringUtil.cs | 24 +++ todo.txt | 5 +- 10 files changed, 228 insertions(+), 196 deletions(-) diff --git a/server/biz/GZCaseBiz.cs b/server/biz/GZCaseBiz.cs index 708fc0b..ac7387a 100644 --- a/server/biz/GZCaseBiz.cs +++ b/server/biz/GZCaseBiz.cs @@ -202,20 +202,10 @@ namespace Sockeye.Biz private async Task ValidateAsync(GZCase proposedObj, GZCase currentObj) { - bool isNew = currentObj == null; + await Task.CompletedTask; + // bool isNew = currentObj == null; - - - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == SockType.GZCase.ToString()); - if (FormCustomization != null) - { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required - - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj); - - } + } diff --git a/server/biz/KeyFactory.cs b/server/biz/KeyFactory.cs index 87af5fc..fe3b36a 100644 --- a/server/biz/KeyFactory.cs +++ b/server/biz/KeyFactory.cs @@ -240,24 +240,7 @@ namespace Sockeye.Biz - private static string GenFetchCode() - { - //sufficient for this purpose - //https://stackoverflow.com/a/1344258/8939 - - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - var stringChars = new char[10]; - var random = new Random(); - - for (int i = 0; i < stringChars.Length; i++) - { - stringChars[i] = chars[random.Next(chars.Length)]; - } - - var finalString = new String(stringChars); - return finalString; - } private static Dictionary LicenseToPluginsArray(License l) @@ -309,7 +292,7 @@ namespace Sockeye.Biz StringBuilder sbKey = new StringBuilder(); StringWriter sw = new StringWriter(sbKey); - l.FetchCode = GenFetchCode(); + l.FetchCode = StringUtil.GenFetchCode(); using (Newtonsoft.Json.JsonWriter w = new Newtonsoft.Json.JsonTextWriter(sw)) { w.Formatting = Newtonsoft.Json.Formatting.Indented; diff --git a/server/biz/LicenseBiz.cs b/server/biz/LicenseBiz.cs index ec2379a..6490fc3 100644 --- a/server/biz/LicenseBiz.cs +++ b/server/biz/LicenseBiz.cs @@ -500,6 +500,12 @@ namespace Sockeye.Biz try { await ct.SaveChangesAsync(); + + //TODO: NOTIFICATION ON NEW KEY + //BUT NOT if REVOKED token is the regto, we don't want them to know, just let AyaNova install it + //this is for non-payment scenarios or chargebacks after license was sent + + } catch (DbUpdateConcurrencyException) { @@ -761,19 +767,7 @@ namespace Sockeye.Biz } - - - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == SockType.License.ToString()); - if (FormCustomization != null) - { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required - - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj); - - } - + await Task.CompletedTask; } @@ -998,7 +992,7 @@ namespace Sockeye.Biz if (ayaEvent == SockEvent.Created || ayaEvent == SockEvent.Modified) { - + } }//end of process notifications diff --git a/server/biz/ProductBiz.cs b/server/biz/ProductBiz.cs index dc877bd..f197fa8 100644 --- a/server/biz/ProductBiz.cs +++ b/server/biz/ProductBiz.cs @@ -202,20 +202,7 @@ namespace Sockeye.Biz private async Task ValidateAsync(Product proposedObj, Product currentObj) { - bool isNew = currentObj == null; - - - - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == SockType.Product.ToString()); - if (FormCustomization != null) - { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required - - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj); - - } + await Task.CompletedTask; } diff --git a/server/biz/PurchaseBiz.cs b/server/biz/PurchaseBiz.cs index 1f2eebd..07e57a0 100644 --- a/server/biz/PurchaseBiz.cs +++ b/server/biz/PurchaseBiz.cs @@ -203,20 +203,7 @@ namespace Sockeye.Biz private async Task ValidateAsync(Purchase proposedObj, Purchase currentObj) { - bool isNew = currentObj == null; - - - - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == SockType.Purchase.ToString()); - if (FormCustomization != null) - { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required - - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj); - - } + await Task.CompletedTask; } diff --git a/server/biz/RavenKeyFactory.cs b/server/biz/RavenKeyFactory.cs index 7c42b26..5d9e568 100644 --- a/server/biz/RavenKeyFactory.cs +++ b/server/biz/RavenKeyFactory.cs @@ -16,10 +16,12 @@ namespace Sockeye.Biz //Key generator controller public static class RavenKeyFactory { - //Sept 2022 decided all trial periods are 7 days, they can keep renewing a new trial by erasing data or request a longer period case by case - public const int TRIAL_PERIOD_DAYS = 7; - - + //Jan 2023 values + public const int TRIAL_KEY_SUBSCRIPTION_PERIOD_DAYS = 7; + public const int TRIAL_KEY_SUBSCRIPTION_CUSTOMER_USERS = 25555;//SEEDING required + public const int TRIAL_KEY_SUBSCRIPTION_MAX_DATA_GB = 20; + public const int TRIAL_KEY_PERPETUAL_PERIOD_DAYS = 14; + public const int TRIAL_KEY_USERS = 5000;//SEEDING required minimum //Unlicensed token private const string UNLICENSED_TOKEN = "UNLICENSED"; @@ -29,8 +31,6 @@ namespace Sockeye.Biz //LICENSE USER COUNT FEATURES - - //SUBSCRIPTION private const string ACTIVE_INTERNAL_USERS_FEATURE_NAME = "ActiveInternalUsers"; private const string ACTIVE_CUSTOMER_USERS_FEATURE_NAME = "ActiveCustomerUsers"; diff --git a/server/biz/SubscriptionServerBiz.cs b/server/biz/SubscriptionServerBiz.cs index a96dffb..8d512f8 100644 --- a/server/biz/SubscriptionServerBiz.cs +++ b/server/biz/SubscriptionServerBiz.cs @@ -208,20 +208,7 @@ namespace Sockeye.Biz private async Task ValidateAsync(SubscriptionServer proposedObj, SubscriptionServer currentObj) { - bool isNew = currentObj == null; - - - - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == SockType.SubscriptionServer.ToString()); - if (FormCustomization != null) - { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required - - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj); - - } + await Task.CompletedTask; } diff --git a/server/biz/TrialLicenseRequestBiz.cs b/server/biz/TrialLicenseRequestBiz.cs index 433354a..d67e321 100644 --- a/server/biz/TrialLicenseRequestBiz.cs +++ b/server/biz/TrialLicenseRequestBiz.cs @@ -49,11 +49,50 @@ namespace Sockeye.Biz else { newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); + + //Process a new request / Generate an email confirm code + newObject.EmailConfirmCode = StringUtil.GenFetchCode(); + newObject.EmailValidated = false; + newObject.Status = TrialRequestStatus.AwaitingEmailValidation; + await ct.TrialLicenseRequest.AddAsync(newObject); await ct.SaveChangesAsync(); - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, SockEvent.Created), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, newObject.Id, BizType, SockEvent.Created), ct); await SearchIndexAsync(newObject, true); await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); + + + //## ------------------ DEFAULT NOTIFICATIONS TO CUSTOMER ---------------- + //ValidateEmail request message,RavenTrialApproved (which sends pending manual generation message) and RavenTrialRejected messages are sent in this block + // + //Send verification request + var verifyUrl = ServerGlobalOpsSettingsCache.Notify.SockeyeServerURL.Trim().TrimEnd('/') + $"/trial-license-request/verify/{newObject.EmailConfirmCode}"; + var body = ServerGlobalBizSettings.Cache.ValidateEmail.Replace("{verifyUrl}", verifyUrl);//$"Please verify your email address by clicking the link below or copy and pasting into a browser\r\n{verifyUrl}\r\nOnce your email is verified the request will be processed manually during business hours.\r\n(If you did not request this you can ignore this message)"; + + var notifyDirectSMTP = new Sockeye.Api.Controllers.NotifyController.NotifyDirectSMTP() + { + ToAddress = newObject.Email, + Subject = "AyaNova trial request email verification", + TextBody = body + }; + + + IMailer m = Sockeye.Util.ServiceProviderProvider.Mailer; + try + { + await m.SendEmailAsync(notifyDirectSMTP.ToAddress, notifyDirectSMTP.Subject, notifyDirectSMTP.TextBody, ServerGlobalOpsSettingsCache.Notify, null, null, null); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(1, notifyDirectSMTP.ObjectId, notifyDirectSMTP.SockType, SockEvent.DirectSMTP, $"\"{notifyDirectSMTP.Subject}\"->{notifyDirectSMTP.ToAddress}"), ct); + } + catch (Exception ex) + { + var err = "TrialLicenseRequest sending email confirmation request: SMTP direct message failed"; + await NotifyEventHelper.AddOpsProblemEvent(err, ex); + AddError(ApiErrorCode.API_SERVER_ERROR, null, ExceptionUtil.ExtractAllExceptionMessages(ex)); + return null; + + } + + await HandlePotentialNotificationEvent(SockEvent.Created, newObject); return newObject; } @@ -92,10 +131,58 @@ namespace Sockeye.Biz putObject.Tags = TagBiz.NormalizeTags(putObject.Tags); await ValidateAsync(putObject, dbObject); if (HasErrors) return null; + + + //Automated processint + //## ------------------ DEFAULT NOTIFICATIONS TO CUSTOMER ---------------- + //RavenTrialApproved (which sends pending manual generation message) and RavenTrialRejected messages are sent in this block + Sockeye.Api.Controllers.NotifyController.NotifyDirectSMTP notifyDirectSMTP = null; + if (dbObject.Status == TrialRequestStatus.AwaitingApproval && putObject.Status == TrialRequestStatus.Approved) + { + //APPROVED, generate and save key to be approved for release by us + putObject.Processed = DateTime.UtcNow; + License l = new License(); + l.Active = false;//not released to customer until we set to active and save + l.DbId = putObject.DbId; + l.PGroup = putObject.PGroup; + l.TrialMode = true; + l.RegTo = putObject.CompanyName; + //perpet and sub both same number of users, covers seeded data values + l.Users = RavenKeyFactory.TRIAL_KEY_USERS; + + if (putObject.PGroup == ProductGroup.RavenPerpetual) + { + l.LicenseExpire = l.MaintenanceExpire = DateTime.UtcNow.AddDays(RavenKeyFactory.TRIAL_KEY_PERPETUAL_PERIOD_DAYS); + } + if (putObject.PGroup == ProductGroup.RavenSubscription) + { + l.LicenseExpire = l.MaintenanceExpire = DateTime.UtcNow.AddDays(RavenKeyFactory.TRIAL_KEY_SUBSCRIPTION_PERIOD_DAYS); + l.CustomerUsers = RavenKeyFactory.TRIAL_KEY_SUBSCRIPTION_CUSTOMER_USERS; + l.MaxDataGB = RavenKeyFactory.TRIAL_KEY_SUBSCRIPTION_MAX_DATA_GB; + } + + } + + if (dbObject.Status == TrialRequestStatus.AwaitingApproval && putObject.Status == TrialRequestStatus.Rejected) + { + //REJECTED, send email + putObject.Processed = DateTime.UtcNow; + } + + + + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); + + //EMAIL? + if (notifyDirectSMTP != null) + { + //send an email + } + } catch (DbUpdateConcurrencyException) { @@ -204,20 +291,12 @@ namespace Sockeye.Biz private async Task ValidateAsync(TrialLicenseRequest proposedObj, TrialLicenseRequest currentObj) { - bool isNew = currentObj == null; + await Task.CompletedTask; + // bool isNew = currentObj == null; + //no need to validate required fields, they are set to required in the model with an attribute - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == SockType.TrialLicenseRequest.ToString()); - if (FormCustomization != null) - { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required - - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj); - - } } @@ -449,103 +528,101 @@ namespace Sockeye.Biz - //## ------------------ DEFAULT NOTIFICATIONS ---------------- - //ValidateEmail request message,RavenTrialApproved (which sends pending manual generation message) and RavenTrialRejected messages are sent in this block - // - // - var custInfo = await ct.Customer.AsNoTracking().Where(x => x.Id == o.CustomerId).Select(x => new { x.Active, x.Tags, x.EmailAddress }).FirstOrDefaultAsync(); - if (custInfo != null && custInfo.Active && !string.IsNullOrWhiteSpace(custInfo.EmailAddress))//can this customer receive *any* customer notifications? - { - //-------- Customer is notifiable ------ - // //# STATUS CHANGE (create new status) - // { - // //Conditions: must match specific status id value and also tags below - // //delivery is immediate so no need to remove old ones of this kind - // var subs = await ct.CustomerNotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.WorkorderStatusChange && z.IdValue == oProposed.WorkOrderStatusId).OrderBy(z => z.Id).ToListAsync(); - // foreach (var sub in subs) - // { - // //Object tags must match and Customer tags must match - // if (NotifyEventHelper.ObjectHasAllSubscriptionTags(WorkorderInfo.Tags, sub.Tags) && NotifyEventHelper.ObjectHasAllSubscriptionTags(custInfo.Tags, sub.CustomerTags)) - // { - // CustomerNotifyEvent n = new CustomerNotifyEvent() - // { - // EventType = NotifyEventType.WorkorderStatusChange, - // CustomerId = WorkorderInfo.CustomerId, - // AyaType = AyaType.WorkOrder, - // ObjectId = oProposed.WorkOrderId, - // CustomerNotifySubscriptionId = sub.Id, - // Name = WorkorderInfo.Serial.ToString() - // }; - // await ct.CustomerNotifyEvent.AddAsync(n); - // log.LogDebug($"Adding CustomerNotifyEvent: [{n.ToString()}]"); - // await ct.SaveChangesAsync(); - // break;//we have a match no need to process any further subs for this event - // } - // } - // }//workorder status change event + // todo: maybe this should be a direct smtp message, not bother with the notification system at all as that's how it was done in rockfish and it's kind of orthogonal + + // if ( == !string.IsNullOrWhiteSpace(o.Email))//can this customer receive *any* customer notifications? + // { + // //-------- Customer is notifiable ------ + + // // //# STATUS CHANGE (create new status) + // // { + // // //Conditions: must match specific status id value and also tags below + // // //delivery is immediate so no need to remove old ones of this kind + // // var subs = await ct.CustomerNotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.WorkorderStatusChange && z.IdValue == oProposed.WorkOrderStatusId).OrderBy(z => z.Id).ToListAsync(); + // // foreach (var sub in subs) + // // { + // // //Object tags must match and Customer tags must match + // // if (NotifyEventHelper.ObjectHasAllSubscriptionTags(WorkorderInfo.Tags, sub.Tags) && NotifyEventHelper.ObjectHasAllSubscriptionTags(custInfo.Tags, sub.CustomerTags)) + // // { + // // CustomerNotifyEvent n = new CustomerNotifyEvent() + // // { + // // EventType = NotifyEventType.WorkorderStatusChange, + // // CustomerId = WorkorderInfo.CustomerId, + // // AyaType = AyaType.WorkOrder, + // // ObjectId = oProposed.WorkOrderId, + // // CustomerNotifySubscriptionId = sub.Id, + // // Name = WorkorderInfo.Serial.ToString() + // // }; + // // await ct.CustomerNotifyEvent.AddAsync(n); + // // log.LogDebug($"Adding CustomerNotifyEvent: [{n.ToString()}]"); + // // await ct.SaveChangesAsync(); + // // break;//we have a match no need to process any further subs for this event + // // } + // // } + // // }//workorder status change event - // //# STATUS AGE - // { - // //WorkorderStatusAge = 24,//* Workorder STATUS unchanged for set time (stuck in state), conditional on: Duration (how long stuck), exact status selected IdValue, Tags. Advance notice can NOT be set - // //Always clear any old ones for this object as they are all irrelevant the moment the state has changed: - // await NotifyEventHelper.ClearPriorCustomerNotifyEventsForObject(ct, proposedObj.AyaType, proposedObj.Id, NotifyEventType.WorkorderStatusAge); - // var subs = await ct.CustomerNotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.WorkorderStatusAge && z.IdValue == oProposed.WorkOrderStatusId).OrderBy(z => z.Id).ToListAsync(); - // foreach (var sub in subs) - // { - // //Object tags must match and Customer tags must match - // if (NotifyEventHelper.ObjectHasAllSubscriptionTags(WorkorderInfo.Tags, sub.Tags) && NotifyEventHelper.ObjectHasAllSubscriptionTags(custInfo.Tags, sub.CustomerTags)) - // { - // CustomerNotifyEvent n = new CustomerNotifyEvent() - // { - // EventType = NotifyEventType.WorkorderStatusAge, - // CustomerId = WorkorderInfo.CustomerId, - // AyaType = AyaType.WorkOrder, - // ObjectId = oProposed.WorkOrderId, - // CustomerNotifySubscriptionId = sub.Id, - // Name = WorkorderInfo.Serial.ToString() - // }; - // await ct.CustomerNotifyEvent.AddAsync(n); - // log.LogDebug($"Adding CustomerNotifyEvent: [{n.ToString()}]"); - // await ct.SaveChangesAsync(); - // break;//we have a match no need to process any further subs for this event - // } - // } - // }//workorder status age event + // // //# STATUS AGE + // // { + // // //WorkorderStatusAge = 24,//* Workorder STATUS unchanged for set time (stuck in state), conditional on: Duration (how long stuck), exact status selected IdValue, Tags. Advance notice can NOT be set + // // //Always clear any old ones for this object as they are all irrelevant the moment the state has changed: + // // await NotifyEventHelper.ClearPriorCustomerNotifyEventsForObject(ct, proposedObj.AyaType, proposedObj.Id, NotifyEventType.WorkorderStatusAge); + // // var subs = await ct.CustomerNotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.WorkorderStatusAge && z.IdValue == oProposed.WorkOrderStatusId).OrderBy(z => z.Id).ToListAsync(); + // // foreach (var sub in subs) + // // { + // // //Object tags must match and Customer tags must match + // // if (NotifyEventHelper.ObjectHasAllSubscriptionTags(WorkorderInfo.Tags, sub.Tags) && NotifyEventHelper.ObjectHasAllSubscriptionTags(custInfo.Tags, sub.CustomerTags)) + // // { + // // CustomerNotifyEvent n = new CustomerNotifyEvent() + // // { + // // EventType = NotifyEventType.WorkorderStatusAge, + // // CustomerId = WorkorderInfo.CustomerId, + // // AyaType = AyaType.WorkOrder, + // // ObjectId = oProposed.WorkOrderId, + // // CustomerNotifySubscriptionId = sub.Id, + // // Name = WorkorderInfo.Serial.ToString() + // // }; + // // await ct.CustomerNotifyEvent.AddAsync(n); + // // log.LogDebug($"Adding CustomerNotifyEvent: [{n.ToString()}]"); + // // await ct.SaveChangesAsync(); + // // break;//we have a match no need to process any further subs for this event + // // } + // // } + // // }//workorder status age event - // //# WorkorderCompleted - // { - // if (wos.Completed) - // { - // var subs = await ct.CustomerNotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.WorkorderCompleted).OrderBy(z => z.Id).ToListAsync(); - // foreach (var sub in subs) - // { - // //Object tags must match and Customer tags must match - // if (NotifyEventHelper.ObjectHasAllSubscriptionTags(WorkorderInfo.Tags, sub.Tags) && NotifyEventHelper.ObjectHasAllSubscriptionTags(custInfo.Tags, sub.CustomerTags)) - // { + // // //# WorkorderCompleted + // // { + // // if (wos.Completed) + // // { + // // var subs = await ct.CustomerNotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.WorkorderCompleted).OrderBy(z => z.Id).ToListAsync(); + // // foreach (var sub in subs) + // // { + // // //Object tags must match and Customer tags must match + // // if (NotifyEventHelper.ObjectHasAllSubscriptionTags(WorkorderInfo.Tags, sub.Tags) && NotifyEventHelper.ObjectHasAllSubscriptionTags(custInfo.Tags, sub.CustomerTags)) + // // { - // CustomerNotifyEvent n = new CustomerNotifyEvent() - // { - // EventType = NotifyEventType.WorkorderCompleted, - // CustomerId = WorkorderInfo.CustomerId, - // AyaType = AyaType.WorkOrder, - // ObjectId = oProposed.WorkOrderId, - // CustomerNotifySubscriptionId = sub.Id, - // Name = WorkorderInfo.Serial.ToString() - // }; - // await ct.CustomerNotifyEvent.AddAsync(n); - // log.LogDebug($"Adding CustomerNotifyEvent: [{n.ToString()}]"); - // await ct.SaveChangesAsync(); - // break;//we have a match no need to process any further subs for this event - // } - // } - // } - // }//WorkorderCompleted + // // CustomerNotifyEvent n = new CustomerNotifyEvent() + // // { + // // EventType = NotifyEventType.WorkorderCompleted, + // // CustomerId = WorkorderInfo.CustomerId, + // // AyaType = AyaType.WorkOrder, + // // ObjectId = oProposed.WorkOrderId, + // // CustomerNotifySubscriptionId = sub.Id, + // // Name = WorkorderInfo.Serial.ToString() + // // }; + // // await ct.CustomerNotifyEvent.AddAsync(n); + // // log.LogDebug($"Adding CustomerNotifyEvent: [{n.ToString()}]"); + // // await ct.SaveChangesAsync(); + // // break;//we have a match no need to process any further subs for this event + // // } + // // } + // // } + // // }//WorkorderCompleted - //----------------------- - } //------------------ /default notifications --------------- + // //----------------------- + // } //------------------ /default notifications --------------- diff --git a/server/util/StringUtil.cs b/server/util/StringUtil.cs index b30a32b..0d66ae9 100644 --- a/server/util/StringUtil.cs +++ b/server/util/StringUtil.cs @@ -165,6 +165,30 @@ namespace Sockeye.Util return Encoding.ASCII.GetString(bytes); // returns: "Hello world" for "48656C6C6F20776F726C64" } + + //Generate a random code for email validation and v7 license key fetching + //doesn't have to be perfect, it's only temporary and + //requires knowledge of the customer / trial user + //email address to use it so it's kind of 2 factor + public static string GenFetchCode() + { + + //sufficient for this purpose + //https://stackoverflow.com/a/1344258/8939 + + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + var stringChars = new char[10]; + var random = new Random(); + + for (int i = 0; i < stringChars.Length; i++) + { + stringChars[i] = chars[random.Next(chars.Length)]; + } + + var finalString = new String(stringChars); + return finalString; + } + }//eoc }//eons \ No newline at end of file diff --git a/todo.txt b/todo.txt index 9ad528c..c699ae9 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,8 @@ TODO: +DOCS +Once it's up and running update docs to mention that perpetual trial period is two weeks subscription is one week + - Purchase event must trigger notification event subscribable (might already due to created etc) - figure out and implement default notifications for Customers where appropriate @@ -30,7 +33,7 @@ TODO: - TODO: subscription server approved Need message template and automatic proxy notification to customer - + - v7 license fetch route - v8 license fetch route