mirror of
https://github.com/streetwriters/notesnook-sync-server.git
synced 2026-08-13 12:00:20 +02:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d27ab68735 | ||
|
|
294d885dbf | ||
|
|
bdd5017394 | ||
|
|
7ad70c63ee | ||
|
|
0367ab6f80 | ||
|
|
f3bfe0957b | ||
|
|
7f614f6954 | ||
|
|
6663778e3e | ||
|
|
14f0a3b37e | ||
|
|
82a1152f9f | ||
|
|
580524b855 |
@@ -42,6 +42,7 @@ namespace Notesnook.API.Accessors
|
|||||||
public SyncItemsRepository Colors { get; }
|
public SyncItemsRepository Colors { get; }
|
||||||
public SyncItemsRepository Vaults { get; }
|
public SyncItemsRepository Vaults { get; }
|
||||||
public SyncItemsRepository Tags { get; }
|
public SyncItemsRepository Tags { get; }
|
||||||
|
public SyncItemsRepository InboxItemsHistory { get; }
|
||||||
public Repository<UserSettings> UsersSettings { get; }
|
public Repository<UserSettings> UsersSettings { get; }
|
||||||
public Repository<Monograph> Monographs { get; }
|
public Repository<Monograph> Monographs { get; }
|
||||||
public Repository<InboxApiKey> InboxApiKey { get; }
|
public Repository<InboxApiKey> InboxApiKey { get; }
|
||||||
@@ -75,6 +76,8 @@ namespace Notesnook.API.Accessors
|
|||||||
IMongoCollection<SyncItem> vaults,
|
IMongoCollection<SyncItem> vaults,
|
||||||
[FromKeyedServices(Collections.TagsKey)]
|
[FromKeyedServices(Collections.TagsKey)]
|
||||||
IMongoCollection<SyncItem> tags,
|
IMongoCollection<SyncItem> tags,
|
||||||
|
[FromKeyedServices(Collections.InboxItemsHistoryKey)]
|
||||||
|
IMongoCollection<SyncItem> inboxItemsHistory,
|
||||||
|
|
||||||
Repository<UserSettings> usersSettings,
|
Repository<UserSettings> usersSettings,
|
||||||
Repository<Monograph> monographs,
|
Repository<Monograph> monographs,
|
||||||
@@ -102,6 +105,7 @@ namespace Notesnook.API.Accessors
|
|||||||
Colors = new SyncItemsRepository(dbContext, colors, logger);
|
Colors = new SyncItemsRepository(dbContext, colors, logger);
|
||||||
Vaults = new SyncItemsRepository(dbContext, vaults, logger);
|
Vaults = new SyncItemsRepository(dbContext, vaults, logger);
|
||||||
Tags = new SyncItemsRepository(dbContext, tags, logger);
|
Tags = new SyncItemsRepository(dbContext, tags, logger);
|
||||||
|
InboxItemsHistory = new SyncItemsRepository(dbContext, inboxItemsHistory, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,5 +18,6 @@ namespace Notesnook.API
|
|||||||
public const string InboxApiKeysKey = "inbox_api_keys";
|
public const string InboxApiKeysKey = "inbox_api_keys";
|
||||||
public const string SyncDevicesKey = "sync_devices";
|
public const string SyncDevicesKey = "sync_devices";
|
||||||
public const string DeviceIdsChunksKey = "device_ids_chunks";
|
public const string DeviceIdsChunksKey = "device_ids_chunks";
|
||||||
|
public const string InboxItemsHistoryKey = "inbox_items_history";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -72,9 +73,9 @@ namespace Notesnook.API.Controllers
|
|||||||
{
|
{
|
||||||
return BadRequest(new { error = "Api key name is required." });
|
return BadRequest(new { error = "Api key name is required." });
|
||||||
}
|
}
|
||||||
if (request.ExpiryDate <= -1)
|
if (request.ExpiryDate == null)
|
||||||
{
|
{
|
||||||
return BadRequest(new { error = "Valid expiry date is required." });
|
return BadRequest(new { error = "Expiry date is required." });
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = await inboxApiKeysRepository.CountAsync(t => t.UserId == userId);
|
var count = await inboxApiKeysRepository.CountAsync(t => t.UserId == userId);
|
||||||
@@ -133,7 +134,7 @@ namespace Notesnook.API.Controllers
|
|||||||
var userSetting = await userSettingsRepository.FindOneAsync(u => u.UserId == userId);
|
var userSetting = await userSettingsRepository.FindOneAsync(u => u.UserId == userId);
|
||||||
if (string.IsNullOrWhiteSpace(userSetting?.InboxKeys?.Public))
|
if (string.IsNullOrWhiteSpace(userSetting?.InboxKeys?.Public))
|
||||||
{
|
{
|
||||||
return BadRequest(new { error = "Inbox public key is not configured." });
|
return NotFound(new { error = "Inbox public key is not configured." });
|
||||||
}
|
}
|
||||||
return Ok(new { key = userSetting.InboxKeys.Public });
|
return Ok(new { key = userSetting.InboxKeys.Public });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,8 +183,8 @@ namespace Notesnook.API.Controllers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var userId = this.User.GetUserId();
|
var userId = this.User.GetUserId();
|
||||||
var size = await s3Service.GetObjectSizeAsync(userId, name);
|
var size = await s3Service.GetObjectSizeAsync(userId, name); Response.Headers.ContentLength = size;
|
||||||
HttpContext.Response.Headers.ContentLength = size;
|
Response.Headers["X-Object-Size"] = size.ToString();
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -57,21 +57,9 @@ namespace Notesnook.API.Hubs
|
|||||||
private ISyncItemsRepositoryAccessor Repositories { get; }
|
private ISyncItemsRepositoryAccessor Repositories { get; }
|
||||||
private SyncDeviceService SyncDeviceService { get; }
|
private SyncDeviceService SyncDeviceService { get; }
|
||||||
private readonly IUnitOfWork unit;
|
private readonly IUnitOfWork unit;
|
||||||
private static readonly string[] CollectionKeys = [
|
|
||||||
"settingitem",
|
|
||||||
"attachment",
|
|
||||||
"note",
|
|
||||||
"notebook",
|
|
||||||
"content",
|
|
||||||
"shortcut",
|
|
||||||
"reminder",
|
|
||||||
"color",
|
|
||||||
"tag",
|
|
||||||
"vault",
|
|
||||||
"relation", // relations must sync at the end to prevent invalid state
|
|
||||||
];
|
|
||||||
private readonly FrozenDictionary<string, Action<IEnumerable<SyncItem>, string, long>> UpsertActionsMap;
|
private readonly FrozenDictionary<string, Action<IEnumerable<SyncItem>, string, long>> UpsertActionsMap;
|
||||||
private readonly Func<string, IEnumerable<string>, bool, int, Task<IAsyncCursor<SyncItem>>>[] Collections;
|
private readonly CollectionDef[] BaseCollectionDefs;
|
||||||
|
private readonly CollectionDef[] V4CollectionDefs;
|
||||||
ILogger<SyncV2Hub> Logger { get; }
|
ILogger<SyncV2Hub> Logger { get; }
|
||||||
|
|
||||||
public SyncV2Hub(ISyncItemsRepositoryAccessor syncItemsRepositoryAccessor, IUnitOfWork unitOfWork, SyncDeviceService syncDeviceService, ILogger<SyncV2Hub> logger)
|
public SyncV2Hub(ISyncItemsRepositoryAccessor syncItemsRepositoryAccessor, IUnitOfWork unitOfWork, SyncDeviceService syncDeviceService, ILogger<SyncV2Hub> logger)
|
||||||
@@ -81,18 +69,32 @@ namespace Notesnook.API.Hubs
|
|||||||
unit = unitOfWork;
|
unit = unitOfWork;
|
||||||
SyncDeviceService = syncDeviceService;
|
SyncDeviceService = syncDeviceService;
|
||||||
|
|
||||||
Collections = [
|
BaseCollectionDefs = [
|
||||||
Repositories.Settings.FindItemsById,
|
new("settingitem", Repositories.Settings.FindItemsById),
|
||||||
Repositories.Attachments.FindItemsById,
|
new("attachment", Repositories.Attachments.FindItemsById),
|
||||||
Repositories.Notes.FindItemsById,
|
new("note", Repositories.Notes.FindItemsById),
|
||||||
Repositories.Notebooks.FindItemsById,
|
new("notebook", Repositories.Notebooks.FindItemsById),
|
||||||
Repositories.Contents.FindItemsById,
|
new("content", Repositories.Contents.FindItemsById),
|
||||||
Repositories.Shortcuts.FindItemsById,
|
new("shortcut", Repositories.Shortcuts.FindItemsById),
|
||||||
Repositories.Reminders.FindItemsById,
|
new("reminder", Repositories.Reminders.FindItemsById),
|
||||||
Repositories.Colors.FindItemsById,
|
new("color", Repositories.Colors.FindItemsById),
|
||||||
Repositories.Tags.FindItemsById,
|
new("tag", Repositories.Tags.FindItemsById),
|
||||||
Repositories.Vaults.FindItemsById,
|
new("vault", Repositories.Vaults.FindItemsById),
|
||||||
Repositories.Relations.FindItemsById,
|
new("relation", Repositories.Relations.FindItemsById), // relations must sync at the end to prevent invalid state
|
||||||
|
];
|
||||||
|
V4CollectionDefs = [
|
||||||
|
new("settingitem", Repositories.Settings.FindItemsById),
|
||||||
|
new("attachment", Repositories.Attachments.FindItemsById),
|
||||||
|
new("note", Repositories.Notes.FindItemsById),
|
||||||
|
new("notebook", Repositories.Notebooks.FindItemsById),
|
||||||
|
new("content", Repositories.Contents.FindItemsById),
|
||||||
|
new("shortcut", Repositories.Shortcuts.FindItemsById),
|
||||||
|
new("reminder", Repositories.Reminders.FindItemsById),
|
||||||
|
new("color", Repositories.Colors.FindItemsById),
|
||||||
|
new("tag", Repositories.Tags.FindItemsById),
|
||||||
|
new("vault", Repositories.Vaults.FindItemsById),
|
||||||
|
new("inboxitemhistory", Repositories.InboxItemsHistory.FindItemsById),
|
||||||
|
new("relation", Repositories.Relations.FindItemsById), // relations must sync at the end to prevent invalid state
|
||||||
];
|
];
|
||||||
UpsertActionsMap = new Dictionary<string, Action<IEnumerable<SyncItem>, string, long>> {
|
UpsertActionsMap = new Dictionary<string, Action<IEnumerable<SyncItem>, string, long>> {
|
||||||
{ "settingitem", Repositories.Settings.UpsertMany },
|
{ "settingitem", Repositories.Settings.UpsertMany },
|
||||||
@@ -106,6 +108,7 @@ namespace Notesnook.API.Hubs
|
|||||||
{ "color", Repositories.Colors.UpsertMany },
|
{ "color", Repositories.Colors.UpsertMany },
|
||||||
{ "vault", Repositories.Vaults.UpsertMany },
|
{ "vault", Repositories.Vaults.UpsertMany },
|
||||||
{ "tag", Repositories.Tags.UpsertMany },
|
{ "tag", Repositories.Tags.UpsertMany },
|
||||||
|
{ "inboxitemhistory", Repositories.InboxItemsHistory.UpsertMany },
|
||||||
}.ToFrozenDictionary();
|
}.ToFrozenDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,17 +184,17 @@ namespace Notesnook.API.Hubs
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async IAsyncEnumerable<SyncTransferItemV2> PrepareChunks(string userId, HashSet<ItemKey> ids, int size, bool resetSync, long maxBytes)
|
private async IAsyncEnumerable<SyncTransferItemV2> PrepareChunks(string userId, HashSet<ItemKey> ids, int size, bool resetSync, long maxBytes, CollectionDef[] collectionDefs)
|
||||||
{
|
{
|
||||||
var itemsProcessed = 0;
|
var itemsProcessed = 0;
|
||||||
for (int i = 0; i < Collections.Length; i++)
|
foreach (var def in collectionDefs)
|
||||||
{
|
{
|
||||||
var type = CollectionKeys[i];
|
var type = def.Key;
|
||||||
|
|
||||||
var filteredIds = ids.Where((id) => id.Type == type).Select((id) => id.ItemId).ToArray();
|
var filteredIds = ids.Where((id) => id.Type == type).Select((id) => id.ItemId).ToArray();
|
||||||
if (!resetSync && filteredIds.Length == 0) continue;
|
if (!resetSync && filteredIds.Length == 0) continue;
|
||||||
|
|
||||||
using var cursor = await Collections[i](userId, filteredIds, resetSync, size);
|
using var cursor = await def.FindItems(userId, filteredIds, resetSync, size);
|
||||||
|
|
||||||
var chunk = new List<SyncItem>();
|
var chunk = new List<SyncItem>();
|
||||||
long totalBytes = 0;
|
long totalBytes = 0;
|
||||||
@@ -233,20 +236,25 @@ namespace Notesnook.API.Hubs
|
|||||||
|
|
||||||
public async Task<SyncV2Metadata> RequestFetch(string deviceId)
|
public async Task<SyncV2Metadata> RequestFetch(string deviceId)
|
||||||
{
|
{
|
||||||
return await HandleRequestFetch(deviceId, false, false);
|
return await HandleRequestFetch(deviceId, false, false, BaseCollectionDefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SyncV2Metadata> RequestFetchV2(string deviceId)
|
public async Task<SyncV2Metadata> RequestFetchV2(string deviceId)
|
||||||
{
|
{
|
||||||
return await HandleRequestFetch(deviceId, true, false);
|
return await HandleRequestFetch(deviceId, true, false, BaseCollectionDefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SyncV2Metadata> RequestFetchV3(string deviceId)
|
public async Task<SyncV2Metadata> RequestFetchV3(string deviceId)
|
||||||
{
|
{
|
||||||
return await HandleRequestFetch(deviceId, true, true);
|
return await HandleRequestFetch(deviceId, true, true, BaseCollectionDefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SyncV2Metadata> HandleRequestFetch(string deviceId, bool includeMonographs, bool includeInboxItems)
|
public async Task<SyncV2Metadata> RequestFetchV4(string deviceId)
|
||||||
|
{
|
||||||
|
return await HandleRequestFetch(deviceId, true, true, V4CollectionDefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<SyncV2Metadata> HandleRequestFetch(string deviceId, bool includeMonographs, bool includeInboxItems, CollectionDef[] collectionDefs)
|
||||||
{
|
{
|
||||||
var userId = Context.User?.FindFirstValue("sub") ?? throw new HubException("Please login to sync.");
|
var userId = Context.User?.FindFirstValue("sub") ?? throw new HubException("Please login to sync.");
|
||||||
|
|
||||||
@@ -270,7 +278,8 @@ namespace Notesnook.API.Hubs
|
|||||||
ids,
|
ids,
|
||||||
size: 100,
|
size: 100,
|
||||||
resetSync: device.IsSyncReset,
|
resetSync: device.IsSyncReset,
|
||||||
maxBytes: 3 * 1024 * 1024
|
maxBytes: 3 * 1024 * 1024,
|
||||||
|
collectionDefs
|
||||||
);
|
);
|
||||||
|
|
||||||
await foreach (var chunk in chunks)
|
await foreach (var chunk in chunks)
|
||||||
@@ -325,7 +334,7 @@ namespace Notesnook.API.Hubs
|
|||||||
var unsyncedInboxItemIds = ids.Where(k => k.Type == "inbox_item").Select(k => k.ItemId);
|
var unsyncedInboxItemIds = ids.Where(k => k.Type == "inbox_item").Select(k => k.ItemId);
|
||||||
var userInboxItems = device.IsSyncReset
|
var userInboxItems = device.IsSyncReset
|
||||||
? await Repositories.InboxItems.FindAsync(m => m.UserId == userId)
|
? await Repositories.InboxItems.FindAsync(m => m.UserId == userId)
|
||||||
: await Repositories.InboxItems.FindAsync(m => m.UserId == userId && unsyncedInboxItemIds.Contains(m.ItemId ?? m.Id.ToString()));
|
: await Repositories.InboxItems.FindAsync(m => m.UserId == userId && unsyncedInboxItemIds.Contains(m.ItemId));
|
||||||
if (userInboxItems.Any() && !await Clients.Caller.SendInboxItems(userInboxItems).WaitAsync(TimeSpan.FromMinutes(10)))
|
if (userInboxItems.Any() && !await Clients.Caller.SendInboxItems(userInboxItems).WaitAsync(TimeSpan.FromMinutes(10)))
|
||||||
{
|
{
|
||||||
throw new HubException("Client rejected inbox items.");
|
throw new HubException("Client rejected inbox items.");
|
||||||
@@ -344,6 +353,11 @@ namespace Notesnook.API.Hubs
|
|||||||
SyncEventCounterSource.Log.RecordFetchDuration(stopwatch.ElapsedMilliseconds);
|
SyncEventCounterSource.Log.RecordFetchDuration(stopwatch.ElapsedMilliseconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record CollectionDef(
|
||||||
|
string Key,
|
||||||
|
Func<string, IEnumerable<string>, bool, int, Task<IAsyncCursor<SyncItem>>> FindItems
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessagePack.MessagePackObject]
|
[MessagePack.MessagePackObject]
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace Notesnook.API.Interfaces
|
|||||||
SyncItemsRepository Colors { get; }
|
SyncItemsRepository Colors { get; }
|
||||||
SyncItemsRepository Vaults { get; }
|
SyncItemsRepository Vaults { get; }
|
||||||
SyncItemsRepository Tags { get; }
|
SyncItemsRepository Tags { get; }
|
||||||
|
SyncItemsRepository InboxItemsHistory { get; }
|
||||||
Repository<UserSettings> UsersSettings { get; }
|
Repository<UserSettings> UsersSettings { get; }
|
||||||
Repository<Monograph> Monographs { get; }
|
Repository<Monograph> Monographs { get; }
|
||||||
Repository<InboxApiKey> InboxApiKey { get; }
|
Repository<InboxApiKey> InboxApiKey { get; }
|
||||||
|
|||||||
@@ -174,18 +174,19 @@ namespace Notesnook.API.Services
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
userSettings.InboxKeys = keys.InboxKeys;
|
userSettings.InboxKeys = keys.InboxKeys;
|
||||||
var defaultInboxKey = new InboxApiKey
|
|
||||||
{
|
|
||||||
UserId = userId,
|
|
||||||
Name = "Default",
|
|
||||||
DateCreated = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
|
||||||
ExpiryDate = DateTimeOffset.UtcNow.AddYears(1).ToUnixTimeMilliseconds(),
|
|
||||||
LastUsedAt = 0
|
|
||||||
};
|
|
||||||
await Repositories.InboxApiKey.InsertAsync(defaultInboxKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Repositories.InboxItems.DeleteManyAsync(t => t.UserId == userId);
|
await Repositories.InboxItems.DeleteManyAsync(t => t.UserId == userId);
|
||||||
|
await WampServers.MessengerServer.PublishMessageAsync(MessengerServerTopics.SendSSETopic, new SendSSEMessage
|
||||||
|
{
|
||||||
|
OriginTokenId = null,
|
||||||
|
UserId = userId,
|
||||||
|
Message = new Message
|
||||||
|
{
|
||||||
|
Type = "inboxUpdated",
|
||||||
|
Data = JsonSerializer.Serialize(new { reason = "Inbox PGP keys added, updated, or removed." })
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await Repositories.UsersSettings.UpdateAsync(userSettings.Id, userSettings);
|
await Repositories.UsersSettings.UpdateAsync(userSettings.Id, userSettings);
|
||||||
@@ -208,6 +209,7 @@ namespace Notesnook.API.Services
|
|||||||
Repositories.Colors.DeleteByUserId(userId);
|
Repositories.Colors.DeleteByUserId(userId);
|
||||||
Repositories.Tags.DeleteByUserId(userId);
|
Repositories.Tags.DeleteByUserId(userId);
|
||||||
Repositories.Vaults.DeleteByUserId(userId);
|
Repositories.Vaults.DeleteByUserId(userId);
|
||||||
|
Repositories.InboxItemsHistory.DeleteByUserId(userId);
|
||||||
Repositories.UsersSettings.Delete((u) => u.UserId == userId);
|
Repositories.UsersSettings.Delete((u) => u.UserId == userId);
|
||||||
Repositories.Monographs.DeleteMany((m) => m.UserId == userId);
|
Repositories.Monographs.DeleteMany((m) => m.UserId == userId);
|
||||||
Repositories.InboxApiKey.DeleteMany((t) => t.UserId == userId);
|
Repositories.InboxApiKey.DeleteMany((t) => t.UserId == userId);
|
||||||
@@ -269,6 +271,7 @@ namespace Notesnook.API.Services
|
|||||||
Repositories.Colors.DeleteByUserId(userId);
|
Repositories.Colors.DeleteByUserId(userId);
|
||||||
Repositories.Tags.DeleteByUserId(userId);
|
Repositories.Tags.DeleteByUserId(userId);
|
||||||
Repositories.Vaults.DeleteByUserId(userId);
|
Repositories.Vaults.DeleteByUserId(userId);
|
||||||
|
Repositories.InboxItemsHistory.DeleteByUserId(userId);
|
||||||
Repositories.Monographs.DeleteMany((m) => m.UserId == userId);
|
Repositories.Monographs.DeleteMany((m) => m.UserId == userId);
|
||||||
Repositories.InboxApiKey.DeleteMany((t) => t.UserId == userId);
|
Repositories.InboxApiKey.DeleteMany((t) => t.UserId == userId);
|
||||||
if (!await unit.Commit()) return false;
|
if (!await unit.Commit()) return false;
|
||||||
|
|||||||
@@ -197,7 +197,8 @@ namespace Notesnook.API
|
|||||||
.AddMongoCollection(Collections.ColorsKey)
|
.AddMongoCollection(Collections.ColorsKey)
|
||||||
.AddMongoCollection(Collections.VaultsKey)
|
.AddMongoCollection(Collections.VaultsKey)
|
||||||
.AddMongoCollection(Collections.InboxItemsKey)
|
.AddMongoCollection(Collections.InboxItemsKey)
|
||||||
.AddMongoCollection(Collections.InboxApiKeysKey);
|
.AddMongoCollection(Collections.InboxApiKeysKey)
|
||||||
|
.AddMongoCollection(Collections.InboxItemsHistoryKey);
|
||||||
|
|
||||||
services.AddScoped<ISyncItemsRepositoryAccessor, SyncItemsRepositoryAccessor>();
|
services.AddScoped<ISyncItemsRepositoryAccessor, SyncItemsRepositoryAccessor>();
|
||||||
services.AddScoped<SyncDeviceService>();
|
services.AddScoped<SyncDeviceService>();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const RawInboxItemSchema = z.object({
|
|||||||
notebookIds: z.array(z.string()).optional(),
|
notebookIds: z.array(z.string()).optional(),
|
||||||
tagIds: z.array(z.string()).optional(),
|
tagIds: z.array(z.string()).optional(),
|
||||||
type: z.enum(["note"]),
|
type: z.enum(["note"]),
|
||||||
source: z.string(),
|
source: z.string().min(1, "Source is required"),
|
||||||
version: z.literal(1),
|
version: z.literal(1),
|
||||||
content: z
|
content: z
|
||||||
.object({
|
.object({
|
||||||
@@ -56,7 +56,9 @@ async function encrypt(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getInboxPublicEncryptionKey(apiKey: string) {
|
async function getInboxPublicEncryptionKey(
|
||||||
|
apiKey: string,
|
||||||
|
): Promise<{ status: "unauthorized" } | { status: "ok"; key: string | null }> {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${NOTESNOOK_API_SERVER_URL}/inbox/public-encryption-key`,
|
`${NOTESNOOK_API_SERVER_URL}/inbox/public-encryption-key`,
|
||||||
{
|
{
|
||||||
@@ -65,6 +67,9 @@ async function getInboxPublicEncryptionKey(apiKey: string) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (response.status === 401) {
|
||||||
|
return { status: "unauthorized" };
|
||||||
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`failed to fetch inbox public encryption key: ${await response.text()}`,
|
`failed to fetch inbox public encryption key: ${await response.text()}`,
|
||||||
@@ -72,7 +77,7 @@ async function getInboxPublicEncryptionKey(apiKey: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = (await response.json()) as unknown as any;
|
const data = (await response.json()) as unknown as any;
|
||||||
return (data?.key as string) || null;
|
return { status: "ok", key: (data?.key as string) || null };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postEncryptedInboxItem(
|
async function postEncryptedInboxItem(
|
||||||
@@ -110,10 +115,14 @@ app.post("/", async (req, res) => {
|
|||||||
return res.status(401).json({ error: "unauthorized" });
|
return res.status(401).json({ error: "unauthorized" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const inboxPublicKey = await getInboxPublicEncryptionKey(apiKey);
|
const encryptionKeyResult = await getInboxPublicEncryptionKey(apiKey);
|
||||||
if (!inboxPublicKey) {
|
if (encryptionKeyResult.status === "unauthorized") {
|
||||||
return res.status(403).json({ error: "inbox public key not found" });
|
return res.status(401).json({ error: "unauthorized" });
|
||||||
}
|
}
|
||||||
|
if (!encryptionKeyResult.key) {
|
||||||
|
return res.status(404).json({ error: "inbox public key not found" });
|
||||||
|
}
|
||||||
|
const inboxPublicKey = encryptionKeyResult.key;
|
||||||
console.log("[info] fetched inbox public key");
|
console.log("[info] fetched inbox public key");
|
||||||
|
|
||||||
const validationResult = RawInboxItemSchema.safeParse(req.body);
|
const validationResult = RawInboxItemSchema.safeParse(req.body);
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ namespace Streetwriters.Common.Extensions
|
|||||||
b.WithOrigins(Constants.NOTESNOOK_CORS_ORIGINS);
|
b.WithOrigins(Constants.NOTESNOOK_CORS_ORIGINS);
|
||||||
|
|
||||||
b.AllowAnyMethod()
|
b.AllowAnyMethod()
|
||||||
.AllowAnyHeader();
|
.AllowAnyHeader()
|
||||||
|
.WithExposedHeaders(["X-Object-Size", "Content-Length"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return services;
|
return services;
|
||||||
|
|||||||
@@ -504,7 +504,6 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
text-indent: 0px;
|
text-indent: 0px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
white-space: pre-wrap;
|
|
||||||
widows: 2;
|
widows: 2;
|
||||||
word-spacing: 0px;
|
word-spacing: 0px;
|
||||||
-webkit-text-stroke-width: 0px;
|
-webkit-text-stroke-width: 0px;
|
||||||
@@ -536,7 +535,6 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
text-indent: 0px;
|
text-indent: 0px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
white-space: pre-wrap;
|
|
||||||
widows: 2;
|
widows: 2;
|
||||||
word-spacing: 0px;
|
word-spacing: 0px;
|
||||||
-webkit-text-stroke-width: 0px;
|
-webkit-text-stroke-width: 0px;
|
||||||
@@ -567,7 +565,6 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
text-indent: 0px;
|
text-indent: 0px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
white-space: pre-wrap;
|
|
||||||
widows: 2;
|
widows: 2;
|
||||||
word-spacing: 0px;
|
word-spacing: 0px;
|
||||||
-webkit-text-stroke-width: 0px;
|
-webkit-text-stroke-width: 0px;
|
||||||
|
|||||||
@@ -309,7 +309,6 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
text-indent: 0px;
|
text-indent: 0px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
white-space: pre-wrap;
|
|
||||||
widows: 2;
|
widows: 2;
|
||||||
word-spacing: 0px;
|
word-spacing: 0px;
|
||||||
-webkit-text-stroke-width: 0px;
|
-webkit-text-stroke-width: 0px;
|
||||||
|
|||||||
@@ -401,7 +401,6 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
text-indent: 0px;
|
text-indent: 0px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
white-space: pre-wrap;
|
|
||||||
word-spacing: 0px;
|
word-spacing: 0px;
|
||||||
-webkit-text-stroke-width: 0px;
|
-webkit-text-stroke-width: 0px;
|
||||||
background-color: rgb(
|
background-color: rgb(
|
||||||
|
|||||||
@@ -467,7 +467,6 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
text-indent: 0px;
|
text-indent: 0px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
white-space: pre-wrap;
|
|
||||||
widows: 2;
|
widows: 2;
|
||||||
word-spacing: 0px;
|
word-spacing: 0px;
|
||||||
-webkit-text-stroke-width: 0px;
|
-webkit-text-stroke-width: 0px;
|
||||||
@@ -483,9 +482,7 @@
|
|||||||
display: inline;
|
display: inline;
|
||||||
"
|
"
|
||||||
><em
|
><em
|
||||||
>If you did not request to reset
|
>If you did not request to reset your account password, you can safely ignore this email.</em
|
||||||
your account password, you can
|
|
||||||
safely ignore this email.</em
|
|
||||||
></span
|
></span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -554,7 +551,6 @@
|
|||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
.
|
|
||||||
<td
|
<td
|
||||||
style="
|
style="
|
||||||
padding: 18px 0px 18px 0px;
|
padding: 18px 0px 18px 0px;
|
||||||
|
|||||||
Reference in New Issue
Block a user