From 56e0a38b2ca246e8beeaef3c6c4b9c0ce7d0f09b Mon Sep 17 00:00:00 2001 From: vnugent Date: Tue, 9 Apr 2024 17:35:13 -0400 Subject: chore(app): Update deps, login spinner, curl msg, view prep --- back-end/src/Cache/UserSettings.cs | 35 -------------- back-end/src/Cache/UserSettingsStore.cs | 48 ------------------ back-end/src/Endpoints/BookmarkEndpoint.cs | 26 ++++++---- back-end/src/Model/BookmarkStore.cs | 38 +++++++++++++++ back-end/src/Model/SimpleBookmarkContext.cs | 54 ++++++--------------- back-end/src/Model/UserSettingsDbStore.cs | 75 ----------------------------- back-end/src/Model/UserSettingsEntry.cs | 39 --------------- back-end/src/SimpleBookmark.csproj | 8 +-- 8 files changed, 73 insertions(+), 250 deletions(-) delete mode 100644 back-end/src/Cache/UserSettings.cs delete mode 100644 back-end/src/Cache/UserSettingsStore.cs delete mode 100644 back-end/src/Model/UserSettingsDbStore.cs delete mode 100644 back-end/src/Model/UserSettingsEntry.cs (limited to 'back-end/src') diff --git a/back-end/src/Cache/UserSettings.cs b/back-end/src/Cache/UserSettings.cs deleted file mode 100644 index b656f83..0000000 --- a/back-end/src/Cache/UserSettings.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -using System.Text.Json.Serialization; - -using MemoryPack; - -namespace SimpleBookmark.Cache -{ - [MemoryPackable] - internal sealed partial class UserSettings - { - [JsonPropertyName("limit")] - public uint PreferredLimit { get; set; } = 10; - - [JsonPropertyName("new_tab")] - public bool OpenInNewTab { get; set; } = true; - - [JsonPropertyName("dark_mode")] - public bool DarkMode { get; set; } = false; - } -} diff --git a/back-end/src/Cache/UserSettingsStore.cs b/back-end/src/Cache/UserSettingsStore.cs deleted file mode 100644 index 8887973..0000000 --- a/back-end/src/Cache/UserSettingsStore.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -using VNLib.Plugins; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Data.Caching; -using VNLib.Plugins.Extensions.VNCache; -using VNLib.Plugins.Extensions.VNCache.DataModel; - -namespace SimpleBookmark.Cache -{ - - [ConfigurationName("settings")] - internal sealed class UserSettingsStore - { - private readonly IEntityCache? Cache; - - public UserSettingsStore(PluginBase plugin, IConfigScope config) - { - //try to get the global cache provider - IGlobalCacheProvider? cache = plugin.GetDefaultGlobalCache(); - if (cache != null) - { - MemPackCacheSerializer serializer = new(null); - - //Recover the cache prefix - string prefix = config.GetRequiredProperty("cache_prefix", p => p.GetString()!); - - //Create a prefixed cache, then create an entity cache for the user settings - Cache = cache.GetPrefixedCache(prefix) - .CreateEntityCache(serializer, serializer); - } - } - } -} diff --git a/back-end/src/Endpoints/BookmarkEndpoint.cs b/back-end/src/Endpoints/BookmarkEndpoint.cs index 001a41b..19f5118 100644 --- a/back-end/src/Endpoints/BookmarkEndpoint.cs +++ b/back-end/src/Endpoints/BookmarkEndpoint.cs @@ -243,20 +243,26 @@ namespace SimpleBookmark.Endpoints return VirtualClose(entity, webm, HttpStatusCode.UnprocessableEntity); } - //See if the uses has reached their quota - long count = await Bookmarks.GetUserRecordCountAsync(entity.Session.UserID, entity.EventCancellation); + /* + * Add the new entry to the database if the user is below their quota + * and the entry does not already exist by the desired url. + */ + int result = await Bookmarks.AddSingleIfNotExists( + entity.Session.UserID, + newBookmark, + entity.RequestedTimeUtc.DateTime, + BmConfig.PerPersonQuota, + entity.EventCancellation + ); - if(webm.Assert(count <= BmConfig.PerPersonQuota, "You have reached your bookmark quota")) + if (webm.Assert(result > -1, "You have reached your bookmark quota")) { - return VirtualClose(entity, webm, HttpStatusCode.OK); - } - - //Try to create the record - ERRNO result = await Bookmarks.CreateUserRecordAsync(newBookmark, entity.Session.UserID, entity.EventCancellation); + return VirtualOk(entity, webm); + } - if (webm.Assert(result > 0, "Failed to create new bookmark")) + if (webm.Assert(result > 0, "Bookmark with the same url alreay exists")) { - return VirtualClose(entity, webm, HttpStatusCode.OK); + return VirtualOk(entity, webm); } webm.Result = "Successfully created bookmark"; diff --git a/back-end/src/Model/BookmarkStore.cs b/back-end/src/Model/BookmarkStore.cs index ec020e8..d53ab01 100644 --- a/back-end/src/Model/BookmarkStore.cs +++ b/back-end/src/Model/BookmarkStore.cs @@ -52,6 +52,44 @@ namespace SimpleBookmark.Model existing.Description = newRecord.Description; existing.JsonTags = newRecord.JsonTags; } + + public async Task AddSingleIfNotExists(string userId, BookmarkEntry entry, DateTime now, uint maxRecords, CancellationToken cancellation) + { + ArgumentNullException.ThrowIfNull(userId); + ArgumentNullException.ThrowIfNull(entry); + + //Init new db connection + await using SimpleBookmarkContext context = new(dbOptions.Value); + + //Check if any bookmarks exist for the user with a given url + bool exists = await context.Bookmarks.AnyAsync(b => b.UserId == userId && b.Url == entry.Url, cancellation); + + //If no bookmarks exist, add a new one + if (!exists) + { + //Check if the user has reached the maximum number of bookmarks + if (await context.Bookmarks.CountAsync(b => b.UserId == userId, cancellation) >= maxRecords) + { + await context.SaveAndCloseAsync(true, cancellation); + return -1; + } + + context.Bookmarks.Add(new () + { + Id = GetNewRecordId(), //Overwrite with new record id + UserId = userId, //Enforce user id + Created = now, + LastModified = now, + Name = entry.Name, //Copy over the entry data + Url = entry.Url, + Description = entry.Description, + Tags = entry.Tags + }); + } + + await context.SaveAndCloseAsync(true, cancellation); + return exists ? 0 : 1; //1 if added, 0 if already exists + } public async Task SearchBookmarksAsync(string userId, string? query, string[] tags, int limit, int page, CancellationToken cancellation) { diff --git a/back-end/src/Model/SimpleBookmarkContext.cs b/back-end/src/Model/SimpleBookmarkContext.cs index 25343d9..f0e53b1 100644 --- a/back-end/src/Model/SimpleBookmarkContext.cs +++ b/back-end/src/Model/SimpleBookmarkContext.cs @@ -27,8 +27,6 @@ namespace SimpleBookmark.Model public DbSet Bookmarks { get; set; } - public DbSet SbSettings { get; set; } - public SimpleBookmarkContext(DbContextOptions options) : base(options) { } @@ -37,43 +35,21 @@ namespace SimpleBookmark.Model public void OnDatabaseCreating(IDbContextBuilder builder, object? userState) { - builder.DefineTable(nameof(Bookmarks)) - .WithColumn(p => p.Id) - .SetIsKey() - .Next() - - .WithColumn(p => p.Created) - .AllowNull(false) - .Next() - - .WithColumn(p => p.LastModified) - .AllowNull(false) - .Next() - - .WithColumn(p => p.UserId) - .AllowNull(false) - .Next() - - .WithColumn(p => p.Name) - .AllowNull(true) - .Next() - - .WithColumn(p => p.Version) - .TimeStamp() - .AllowNull(true) - .Next() - - .WithColumn(p => p.Url) - .AllowNull(true) - .Next() - - .WithColumn(p => p.Description) - .AllowNull(true) - .Next() - - .WithColumn(p => p.Tags) - .AllowNull(true) - .Next(); + /* + * Define the coloumn mappings for the BookmarkEntry table + */ + builder.DefineTable(nameof(Bookmarks), table => + { + table.WithColumn(p => p.Id).AllowNull(false); + table.WithColumn(p => p.Created); + table.WithColumn(p => p.LastModified); + table.WithColumn(p => p.UserId).AllowNull(false); + table.WithColumn(p => p.Name); + table.WithColumn(p => p.Version); + table.WithColumn(p => p.Url); + table.WithColumn(p => p.Description); + table.WithColumn(p => p.Tags); + }); } } diff --git a/back-end/src/Model/UserSettingsDbStore.cs b/back-end/src/Model/UserSettingsDbStore.cs deleted file mode 100644 index d392262..0000000 --- a/back-end/src/Model/UserSettingsDbStore.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -using System; -using System.Threading; -using System.Threading.Tasks; - -using Microsoft.EntityFrameworkCore; - -using VNLib.Utils; -using VNLib.Plugins.Extensions.Loading; - -namespace SimpleBookmark.Model -{ - internal sealed class UserSettingsDbStore(IAsyncLazy dbOptions) - { - - public async Task GetSettingsForUserAsync(string userId, CancellationToken cancellation) - { - ArgumentNullException.ThrowIfNull(userId); - - //Init new db connection - await using SimpleBookmarkContext context = new(dbOptions.Value); - - UserSettingsEntry? settings = await context.SbSettings.FirstOrDefaultAsync(p => p.UserId == userId, cancellation); - - //Close db and commit transaction - await context.SaveAndCloseAsync(true, cancellation); - - return settings; - } - - public async Task SetSettingsForUser(string userId, UserSettingsEntry settings, CancellationToken cancellation) - { - ArgumentNullException.ThrowIfNull(userId); - ArgumentNullException.ThrowIfNull(settings); - - //Init new db connection - await using SimpleBookmarkContext context = new(dbOptions.Value); - - //Search for existing settings entry - UserSettingsEntry? existing = await context.SbSettings.FirstOrDefaultAsync(p => p.UserId == userId, cancellation); - - if (existing is null) - { - //Add a new entry - settings.UserId = userId; - settings.LastModified = DateTime.UtcNow; - context.Add(settings); - } - else - { - //Update existing entry - existing.SettingsData = settings.SettingsData; - existing.LastModified = DateTime.UtcNow; - context.Update(existing); - } - - //Close db and commit transaction - return await context.SaveAndCloseAsync(true, cancellation); - } - } -} diff --git a/back-end/src/Model/UserSettingsEntry.cs b/back-end/src/Model/UserSettingsEntry.cs deleted file mode 100644 index c27af8a..0000000 --- a/back-end/src/Model/UserSettingsEntry.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -using System; -using System.Text.Json.Serialization; -using System.ComponentModel.DataAnnotations; - -using VNLib.Plugins.Extensions.Data.Abstractions; - -namespace SimpleBookmark.Model -{ - internal sealed class UserSettingsEntry : IUserEntity - { - public DateTime LastModified { get; set; } - - [Timestamp] - [JsonIgnore] - public byte[]? Version { get; set; } - - [Key] - [JsonIgnore] - public string? UserId { get; set; } - - [MaxLength(5000)] - public byte[]? SettingsData { get; set; } - } -} diff --git a/back-end/src/SimpleBookmark.csproj b/back-end/src/SimpleBookmark.csproj index 190dc44..d0235a2 100644 --- a/back-end/src/SimpleBookmark.csproj +++ b/back-end/src/SimpleBookmark.csproj @@ -34,10 +34,10 @@ - - - - + + + + -- cgit