mirror of
https://github.com/streetwriters/notesnook-sync-server.git
synced 2026-02-12 11:12:44 +00:00
monograph: {id}/stats -> {id}/analytics
This commit is contained in:
@@ -35,6 +35,7 @@ using Notesnook.API.Authorization;
|
||||
using Notesnook.API.Models;
|
||||
using Notesnook.API.Services;
|
||||
using Streetwriters.Common;
|
||||
using Streetwriters.Common.Helpers;
|
||||
using Streetwriters.Common.Interfaces;
|
||||
using Streetwriters.Common.Messages;
|
||||
using Streetwriters.Data.Interfaces;
|
||||
@@ -272,18 +273,20 @@ namespace Notesnook.API.Controllers
|
||||
return Content(SVG_PIXEL, "image/svg+xml");
|
||||
}
|
||||
|
||||
[HttpGet("{id}/stats")]
|
||||
public async Task<IActionResult> GetMonographStatsAsync([FromRoute] string id)
|
||||
[HttpGet("{id}/analytics")]
|
||||
public async Task<IActionResult> GetMonographAnalyticsAsync([FromRoute] string id)
|
||||
{
|
||||
if (!FeatureAuthorizationHelper.IsFeatureAllowed(Features.MONOGRAPH_ANALYTICS, Clients.Notesnook.Id, User))
|
||||
return BadRequest(new { error = "Monograph analytics are only available on the Pro & Believer plans." });
|
||||
|
||||
var userId = this.User.GetUserId();
|
||||
var monograph = await FindMonographAsync(id);
|
||||
|
||||
if (monograph == null || monograph.Deleted || monograph.UserId != userId)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(new { viewCount = monograph.ViewCount });
|
||||
return Ok(new { totalViews = monograph.ViewCount });
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
|
||||
60
Streetwriters.Common/Helpers/FeatureAuthorizationHelper.cs
Normal file
60
Streetwriters.Common/Helpers/FeatureAuthorizationHelper.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.IO;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Interfaces;
|
||||
using WebMarkupMin.Core;
|
||||
using WebMarkupMin.Core.Loggers;
|
||||
|
||||
namespace Streetwriters.Common.Helpers
|
||||
{
|
||||
public enum Features
|
||||
{
|
||||
SMS_2FA,
|
||||
MONOGRAPH_ANALYTICS
|
||||
}
|
||||
|
||||
public static class FeatureAuthorizationHelper
|
||||
{
|
||||
private static SubscriptionPlan? GetUserSubscriptionPlan(string clientId, ClaimsPrincipal user)
|
||||
{
|
||||
var claimKey = $"{clientId}:status";
|
||||
var status = user.FindFirstValue(claimKey);
|
||||
switch (status)
|
||||
{
|
||||
case "free":
|
||||
return SubscriptionPlan.FREE;
|
||||
case "believer":
|
||||
return SubscriptionPlan.BELIEVER;
|
||||
case "education":
|
||||
return SubscriptionPlan.EDUCATION;
|
||||
case "essential":
|
||||
return SubscriptionPlan.ESSENTIAL;
|
||||
case "pro":
|
||||
return SubscriptionPlan.PRO;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsFeatureAllowed(Features feature, string clientId, ClaimsPrincipal user)
|
||||
{
|
||||
if (Constants.IS_SELF_HOSTED)
|
||||
return true;
|
||||
|
||||
var status = GetUserSubscriptionPlan(clientId, user);
|
||||
|
||||
switch (feature)
|
||||
{
|
||||
case Features.SMS_2FA:
|
||||
case Features.MONOGRAPH_ANALYTICS:
|
||||
return status == SubscriptionPlan.LEGACY_PRO ||
|
||||
status == SubscriptionPlan.PRO ||
|
||||
status == SubscriptionPlan.EDUCATION ||
|
||||
status == SubscriptionPlan.BELIEVER;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Streetwriters.Common;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Helpers;
|
||||
using Streetwriters.Common.Models;
|
||||
using Streetwriters.Identity.Interfaces;
|
||||
using Streetwriters.Identity.Models;
|
||||
@@ -53,6 +54,9 @@ namespace Streetwriters.Identity.Controllers
|
||||
|
||||
var user = await UserManager.GetUserAsync(User) ?? throw new Exception("User not found.");
|
||||
|
||||
if (form.Type == MFAMethods.SMS && !FeatureAuthorizationHelper.IsFeatureAllowed(Features.SMS_2FA, client.Id, User))
|
||||
throw new Exception("2FA via SMS is only available on Pro & Believer plans.");
|
||||
|
||||
try
|
||||
{
|
||||
switch (form.Type)
|
||||
@@ -62,7 +66,7 @@ namespace Streetwriters.Identity.Controllers
|
||||
return Ok(authenticatorDetails);
|
||||
case "sms":
|
||||
case "email":
|
||||
await MFAService.SendOTPAsync(user, client, form, true);
|
||||
await MFAService.SendOTPAsync(user, client, form);
|
||||
return Ok();
|
||||
default:
|
||||
return BadRequest("Invalid authenticator type.");
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Streetwriters.Identity.Interfaces
|
||||
bool IsValidMFAMethod(string method);
|
||||
bool IsValidMFAMethod(string method, User user);
|
||||
Task<AuthenticatorDetails> GetAuthenticatorDetailsAsync(User user, IClient client);
|
||||
Task SendOTPAsync(User user, IClient client, MultiFactorSetupForm form, bool isSetup = false);
|
||||
Task SendOTPAsync(User user, IClient client, MultiFactorSetupForm form);
|
||||
Task<bool> VerifyOTPAsync(User user, string code, string method);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Streetwriters.Common;
|
||||
using Streetwriters.Common.Enums;
|
||||
using Streetwriters.Common.Interfaces;
|
||||
using Streetwriters.Common.Models;
|
||||
@@ -168,18 +167,12 @@ namespace Streetwriters.Identity.Services
|
||||
};
|
||||
}
|
||||
|
||||
public async Task SendOTPAsync(User user, IClient client, MultiFactorSetupForm form, bool isSetup = false)
|
||||
public async Task SendOTPAsync(User user, IClient client, MultiFactorSetupForm form)
|
||||
{
|
||||
var method = form.Type;
|
||||
if ((method != MFAMethods.Email && method != MFAMethods.SMS) || !IsValidMFAMethod(method))
|
||||
throw new Exception("Invalid method.");
|
||||
|
||||
if (isSetup &&
|
||||
method == MFAMethods.SMS &&
|
||||
!UserService.IsSMSMFAAllowed(client.Id, user))
|
||||
throw new Exception("Due to the high costs of SMS, 2FA via SMS is only available on Pro & Believer plans.");
|
||||
|
||||
// if (!user.EmailConfirmed) throw new Exception("Please confirm your email before activating 2FA by email.");
|
||||
await GetAuthenticatorDetailsAsync(user, client);
|
||||
|
||||
switch (method)
|
||||
|
||||
@@ -29,37 +29,6 @@ namespace Streetwriters.Identity.Services
|
||||
{
|
||||
public class UserService
|
||||
{
|
||||
private static SubscriptionPlan? GetUserSubscriptionPlan(string clientId, User user)
|
||||
{
|
||||
var claimKey = GetClaimKey(clientId);
|
||||
var status = user.Claims.FirstOrDefault((c) => c.ClaimType == claimKey)?.ClaimValue;
|
||||
switch (status)
|
||||
{
|
||||
case "free":
|
||||
return SubscriptionPlan.FREE;
|
||||
case "believer":
|
||||
return SubscriptionPlan.BELIEVER;
|
||||
case "education":
|
||||
return SubscriptionPlan.EDUCATION;
|
||||
case "essential":
|
||||
return SubscriptionPlan.ESSENTIAL;
|
||||
case "pro":
|
||||
return SubscriptionPlan.PRO;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsSMSMFAAllowed(string clientId, User user)
|
||||
{
|
||||
var status = GetUserSubscriptionPlan(clientId, user);
|
||||
if (status == null) return false;
|
||||
return status == SubscriptionPlan.LEGACY_PRO ||
|
||||
status == SubscriptionPlan.PRO ||
|
||||
status == SubscriptionPlan.EDUCATION ||
|
||||
status == SubscriptionPlan.BELIEVER;
|
||||
}
|
||||
|
||||
public static Claim SubscriptionPlanToClaim(string clientId, Subscription subscription)
|
||||
{
|
||||
var claimKey = GetClaimKey(clientId);
|
||||
|
||||
Reference in New Issue
Block a user