mirror of
https://github.com/streetwriters/notesnook-sync-server.git
synced 2026-10-04 19:46:50 +02:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdde244c69 | ||
|
|
8b610e0952 | ||
|
|
0a3ee07b95 | ||
|
|
768384011d | ||
|
|
a8e73069c2 |
No files matched your search
@@ -45,28 +45,29 @@ namespace Notesnook.API.Controllers
|
|||||||
[HttpPut]
|
[HttpPut]
|
||||||
public async Task<IActionResult> Upload([FromQuery] string name)
|
public async Task<IActionResult> Upload([FromQuery] string name)
|
||||||
{
|
{
|
||||||
try
|
return BadRequest(new { error = "Attachment storage is temporarily unavailable. Please try again later." });
|
||||||
{
|
// try
|
||||||
var userId = this.User.GetUserId();
|
// {
|
||||||
|
// var userId = this.User.GetUserId();
|
||||||
|
|
||||||
var fileSize = HttpContext.Request.ContentLength ?? 0;
|
// var fileSize = HttpContext.Request.ContentLength ?? 0;
|
||||||
bool hasBody = fileSize > 0;
|
// bool hasBody = fileSize > 0;
|
||||||
|
|
||||||
if (!hasBody)
|
// if (!hasBody)
|
||||||
{
|
// {
|
||||||
return Ok(Request.GetEncodedUrl() + "&access_token=" + Request.Headers.Authorization.ToString().Replace("Bearer ", ""));
|
// return Ok(Request.GetEncodedUrl() + "&access_token=" + Request.Headers.Authorization.ToString().Replace("Bearer ", ""));
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (Constants.IS_SELF_HOSTED) await UploadFileAsync(userId, name, fileSize);
|
// if (Constants.IS_SELF_HOSTED) await UploadFileAsync(userId, name, fileSize);
|
||||||
else await UploadFileWithChecksAsync(userId, name, fileSize);
|
// else await UploadFileWithChecksAsync(userId, name, fileSize);
|
||||||
|
|
||||||
return Ok();
|
// return Ok();
|
||||||
}
|
// }
|
||||||
catch (Exception ex)
|
// catch (Exception ex)
|
||||||
{
|
// {
|
||||||
logger.LogError(ex, "Error uploading attachment for user.");
|
// logger.LogError(ex, "Error uploading attachment for user.");
|
||||||
return BadRequest(new { error = "Failed to upload attachment." });
|
// return BadRequest(new { error = "Failed to upload attachment." });
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UploadFileWithChecksAsync(string userId, string name, long fileSize)
|
private async Task UploadFileWithChecksAsync(string userId, string name, long fileSize)
|
||||||
@@ -115,17 +116,18 @@ namespace Notesnook.API.Controllers
|
|||||||
[HttpGet("multipart")]
|
[HttpGet("multipart")]
|
||||||
public async Task<IActionResult> MultipartUpload([FromQuery] string name, [FromQuery] int parts, [FromQuery] string? uploadId)
|
public async Task<IActionResult> MultipartUpload([FromQuery] string name, [FromQuery] int parts, [FromQuery] string? uploadId)
|
||||||
{
|
{
|
||||||
var userId = this.User.GetUserId();
|
return BadRequest(new { error = "Attachment storage is temporarily unavailable. Please try again later." });
|
||||||
try
|
// var userId = this.User.GetUserId();
|
||||||
{
|
// try
|
||||||
var meta = await s3Service.StartMultipartUploadAsync(userId, name, parts, uploadId);
|
// {
|
||||||
return Ok(meta);
|
// var meta = await s3Service.StartMultipartUploadAsync(userId, name, parts, uploadId);
|
||||||
}
|
// return Ok(meta);
|
||||||
catch (Exception ex)
|
// }
|
||||||
{
|
// catch (Exception ex)
|
||||||
logger.LogError(ex, "Error starting multipart upload for user.");
|
// {
|
||||||
return BadRequest(new { error = "Failed to start multipart upload." });
|
// logger.LogError(ex, "Error starting multipart upload for user.");
|
||||||
}
|
// return BadRequest(new { error = "Failed to start multipart upload." });
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("multipart")]
|
[HttpDelete("multipart")]
|
||||||
|
|||||||
@@ -93,39 +93,56 @@ namespace Notesnook.API.Controllers
|
|||||||
[HttpPatch("password/{type}")]
|
[HttpPatch("password/{type}")]
|
||||||
public async Task<IActionResult> ChangePassword([FromRoute] string type, [FromBody] ChangePasswordForm form)
|
public async Task<IActionResult> ChangePassword([FromRoute] string type, [FromBody] ChangePasswordForm form)
|
||||||
{
|
{
|
||||||
return BadRequest(new { error = "Password change is currently disabled." });
|
var userId = User.GetUserId();
|
||||||
// var userId = User.GetUserId();
|
var clientId = User.FindFirstValue("client_id");
|
||||||
// var clientId = User.FindFirstValue("client_id");
|
var jti = User.FindFirstValue("jti");
|
||||||
// var jti = User.FindFirstValue("jti");
|
var isPasswordReset = type == "reset";
|
||||||
// var isPasswordReset = type == "reset";
|
try
|
||||||
// try
|
{
|
||||||
// {
|
var result = isPasswordReset ? await serviceAccessor.UserAccountService.ResetPasswordAsync(userId, form.NewPassword) : await serviceAccessor.UserAccountService.ChangePasswordAsync(userId, form.OldPassword, form.NewPassword);
|
||||||
// var result = isPasswordReset ? await serviceAccessor.UserAccountService.ResetPasswordAsync(userId, form.NewPassword) : await serviceAccessor.UserAccountService.ChangePasswordAsync(userId, form.OldPassword, form.NewPassword);
|
if (!result)
|
||||||
// if (!result)
|
return BadRequest("Failed to change password.");
|
||||||
// return BadRequest("Failed to change password.");
|
|
||||||
|
|
||||||
// await UserService.SetUserKeysAsync(userId, form.UserKeys);
|
await UserService.SetUserKeysAsync(userId, form.UserKeys);
|
||||||
|
|
||||||
// await serviceAccessor.UserAccountService.ClearSessionsAsync(userId, clientId, all: false, jti, null);
|
await serviceAccessor.UserAccountService.ClearSessionsAsync(userId, clientId, all: false, jti, null);
|
||||||
|
|
||||||
// await WampServers.MessengerServer.PublishMessageAsync(MessengerServerTopics.SendSSETopic, new SendSSEMessage
|
await WampServers.MessengerServer.PublishMessageAsync(MessengerServerTopics.SendSSETopic, new SendSSEMessage
|
||||||
// {
|
{
|
||||||
// UserId = userId,
|
UserId = userId,
|
||||||
// OriginTokenId = jti,
|
OriginTokenId = jti,
|
||||||
// Message = new Message
|
Message = new Message
|
||||||
// {
|
{
|
||||||
// Type = "logout",
|
Type = "logout",
|
||||||
// Data = JsonSerializer.Serialize(new { reason = "Password changed." })
|
Data = JsonSerializer.Serialize(new { reason = "Password changed." })
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
// return Ok();
|
return Ok();
|
||||||
// }
|
}
|
||||||
// catch (Exception ex)
|
catch (Exception ex)
|
||||||
// {
|
{
|
||||||
// logger.LogError(ex, "Failed to change password");
|
logger.LogError(ex, "Failed to change password");
|
||||||
// return BadRequest(new { error = ex.Message });
|
return BadRequest(new { error = ex.Message });
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("verifier")]
|
||||||
|
public async Task<IActionResult> GetEncryptionVerifier()
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await UserService.GetEncryptionVerifier(userId);
|
||||||
|
if (response == null) return NotFound();
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Failed to get encryption verifier for user id: {UserId}", userId);
|
||||||
|
return BadRequest(new { error = ex.Message });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("reset")]
|
[HttpPost("reset")]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace Notesnook.API.Interfaces
|
|||||||
Task DeleteUserAsync(string userId, string? jti, string password);
|
Task DeleteUserAsync(string userId, string? jti, string password);
|
||||||
Task<bool> ResetUserAsync(string userId, bool removeAttachments);
|
Task<bool> ResetUserAsync(string userId, bool removeAttachments);
|
||||||
Task<UserResponse> GetUserAsync(string userId);
|
Task<UserResponse> GetUserAsync(string userId);
|
||||||
|
Task<EncryptedData?> GetEncryptionVerifier(string userId);
|
||||||
Task SetUserKeysAsync(string userId, UserKeys keys);
|
Task SetUserKeysAsync(string userId, UserKeys keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,7 @@ using Streetwriters.Common;
|
|||||||
using Streetwriters.Data.DbContexts;
|
using Streetwriters.Data.DbContexts;
|
||||||
using Streetwriters.Data.Interfaces;
|
using Streetwriters.Data.Interfaces;
|
||||||
using Streetwriters.Data.Repositories;
|
using Streetwriters.Data.Repositories;
|
||||||
|
using AspNetCore.Identity.Mongo.Mongo;
|
||||||
|
|
||||||
namespace Notesnook.API.Repositories
|
namespace Notesnook.API.Repositories
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ using Notesnook.API.Helpers;
|
|||||||
using Notesnook.API.Interfaces;
|
using Notesnook.API.Interfaces;
|
||||||
using Notesnook.API.Models;
|
using Notesnook.API.Models;
|
||||||
using Notesnook.API.Models.Responses;
|
using Notesnook.API.Models.Responses;
|
||||||
|
using Notesnook.API.Repositories;
|
||||||
using Streetwriters.Common;
|
using Streetwriters.Common;
|
||||||
using Streetwriters.Common.Accessors;
|
using Streetwriters.Common.Accessors;
|
||||||
using Streetwriters.Common.Enums;
|
using Streetwriters.Common.Enums;
|
||||||
@@ -192,6 +193,26 @@ namespace Notesnook.API.Services
|
|||||||
await Repositories.UsersSettings.UpdateAsync(userSettings.Id, userSettings);
|
await Repositories.UsersSettings.UpdateAsync(userSettings.Id, userSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<EncryptedData?> GetEncryptionVerifier(string userId)
|
||||||
|
{
|
||||||
|
SyncItemsRepository[] repositories = [Repositories.Notes, Repositories.Notebooks, Repositories.Shortcuts, Repositories.Contents, Repositories.Settings, Repositories.LegacySettings, Repositories.Attachments, Repositories.Reminders, Repositories.Relations, Repositories.Colors, Repositories.Tags, Repositories.Vaults, Repositories.InboxItemsHistory];
|
||||||
|
foreach (var repo in repositories)
|
||||||
|
{
|
||||||
|
var item = await repo.FindOneAsync((s) => s.UserId == userId && (s.KeyVersion == null || s.KeyVersion == 0));
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
return new EncryptedData
|
||||||
|
{
|
||||||
|
Cipher = item.Cipher,
|
||||||
|
IV = item.IV,
|
||||||
|
Salt = "",
|
||||||
|
Length = item.Length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteUserAsync(string userId)
|
public async Task DeleteUserAsync(string userId)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Deleting user {UserId}", userId);
|
logger.LogInformation("Deleting user {UserId}", userId);
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ using Streetwriters.Common.Models;
|
|||||||
|
|
||||||
namespace Streetwriters.Common.Services
|
namespace Streetwriters.Common.Services
|
||||||
{
|
{
|
||||||
public class EmailSender : IEmailSender, IAsyncDisposable
|
public class EmailSender : IEmailSender
|
||||||
{
|
{
|
||||||
private readonly SmtpClient mailClient = new();
|
|
||||||
private readonly ILogger<EmailSender> logger;
|
private readonly ILogger<EmailSender> logger;
|
||||||
|
|
||||||
public EmailSender(ILogger<EmailSender> logger)
|
public EmailSender(ILogger<EmailSender> logger)
|
||||||
@@ -32,27 +31,25 @@ namespace Streetwriters.Common.Services
|
|||||||
Dictionary<string, byte[]>? attachments = null
|
Dictionary<string, byte[]>? attachments = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!mailClient.IsConnected)
|
using var mailClient = new SmtpClient();
|
||||||
|
|
||||||
|
if (int.TryParse(Common.Constants.SMTP_PORT, out int port))
|
||||||
{
|
{
|
||||||
if (int.TryParse(Common.Constants.SMTP_PORT, out int port))
|
await mailClient.ConnectAsync(
|
||||||
{
|
Common.Constants.SMTP_HOST,
|
||||||
await mailClient.ConnectAsync(
|
port,
|
||||||
Common.Constants.SMTP_HOST,
|
MailKit.Security.SecureSocketOptions.Auto
|
||||||
port,
|
);
|
||||||
MailKit.Security.SecureSocketOptions.Auto
|
}
|
||||||
);
|
else
|
||||||
}
|
{
|
||||||
else
|
throw new InvalidDataException("SMTP_PORT is not a valid integer value.");
|
||||||
{
|
|
||||||
throw new InvalidDataException("SMTP_PORT is not a valid integer value.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mailClient.IsAuthenticated)
|
await mailClient.AuthenticateAsync(
|
||||||
await mailClient.AuthenticateAsync(
|
Common.Constants.SMTP_USERNAME,
|
||||||
Common.Constants.SMTP_USERNAME,
|
Common.Constants.SMTP_PASSWORD
|
||||||
Common.Constants.SMTP_PASSWORD
|
);
|
||||||
);
|
|
||||||
|
|
||||||
var message = new MimeMessage();
|
var message = new MimeMessage();
|
||||||
message.From.Add(new MailboxAddress(from.DisplayName, from.Address));
|
message.From.Add(new MailboxAddress(from.DisplayName, from.Address));
|
||||||
@@ -70,6 +67,11 @@ namespace Streetwriters.Common.Services
|
|||||||
);
|
);
|
||||||
|
|
||||||
await mailClient.SendAsync(message);
|
await mailClient.SendAsync(message);
|
||||||
|
|
||||||
|
if (mailClient.IsConnected)
|
||||||
|
{
|
||||||
|
await mailClient.DisconnectAsync(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MimeEntity> GetEmailBodyAsync(
|
private async Task<MimeEntity> GetEmailBodyAsync(
|
||||||
@@ -129,10 +131,5 @@ namespace Streetwriters.Common.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async ValueTask IAsyncDisposable.DisposeAsync()
|
|
||||||
{
|
|
||||||
await mailClient.DisconnectAsync(true);
|
|
||||||
mailClient.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+11
-3
@@ -279,8 +279,8 @@ function serveYouTubeEmbed(url: string) {
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<iframe src="${transformYouTubeUrl(
|
<iframe src="${escapeHtmlAttr(
|
||||||
url,
|
transformYouTubeUrl(url),
|
||||||
)}" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture;web-share" allowfullscreen referrerpolicy="strict-origin-when-cross-origin" title="Video player"></iframe>
|
)}" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture;web-share" allowfullscreen referrerpolicy="strict-origin-when-cross-origin" title="Video player"></iframe>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
@@ -299,6 +299,14 @@ function isYouTubeEmbed(urlString: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeHtmlAttr(str: string): string {
|
||||||
|
return str
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">");
|
||||||
|
}
|
||||||
|
|
||||||
// Transform YouTube URLs to use youtube-nocookie.com for enhanced privacy
|
// Transform YouTube URLs to use youtube-nocookie.com for enhanced privacy
|
||||||
function transformYouTubeUrl(urlString: string): string {
|
function transformYouTubeUrl(urlString: string): string {
|
||||||
try {
|
try {
|
||||||
@@ -315,7 +323,7 @@ function transformYouTubeUrl(urlString: string): string {
|
|||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return urlString;
|
return url.toString();
|
||||||
} catch {
|
} catch {
|
||||||
return urlString;
|
return urlString;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user