feat(backend): global notices
This commit is contained in:
		
							parent
							
								
									22be49976a
								
							
						
					
					
						commit
						b07f4b75c0
					
				
					 19 changed files with 1247 additions and 8 deletions
				
			
		|  | @ -253,14 +253,14 @@ public class AuthService( | |||
|     { | ||||
|         AssertValidAuthType(authType, app); | ||||
| 
 | ||||
|         // This is already checked when | ||||
|         // This is already checked when generating an add account state, but we check it here too just in case. | ||||
|         int currentCount = await db | ||||
|             .AuthMethods.Where(m => m.UserId == userId && m.AuthType == authType) | ||||
|             .CountAsync(ct); | ||||
|         if (currentCount >= AuthUtils.MaxAuthMethodsPerType) | ||||
|         { | ||||
|             throw new ApiError.BadRequest( | ||||
|                 "Too many linked accounts of this type, maximum of 3 per account." | ||||
|                 $"Too many linked accounts of this type, maximum of {AuthUtils.MaxAuthMethodsPerType} per account." | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										39
									
								
								Foxnouns.Backend/Services/Caching/NoticeCacheService.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Foxnouns.Backend/Services/Caching/NoticeCacheService.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| // Copyright (C) 2023-present sam/u1f320 (vulpine.solutions) | ||||
| // | ||||
| // 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 <https://www.gnu.org/licenses/>. | ||||
| using Foxnouns.Backend.Database; | ||||
| using Foxnouns.Backend.Database.Models; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using NodaTime; | ||||
| 
 | ||||
| namespace Foxnouns.Backend.Services.Caching; | ||||
| 
 | ||||
| public class NoticeCacheService(IServiceProvider serviceProvider, IClock clock, ILogger logger) | ||||
|     : SingletonCacheService<Notice>(serviceProvider, clock, logger) | ||||
| { | ||||
|     public override Duration MaxAge { get; init; } = Duration.FromMinutes(5); | ||||
| 
 | ||||
|     public override Func< | ||||
|         DatabaseContext, | ||||
|         CancellationToken, | ||||
|         Task<Notice?> | ||||
|     > FetchFunc { get; init; } = | ||||
|         async (db, ct) => | ||||
|             await db | ||||
|                 .Notices.Where(n => | ||||
|                     n.StartTime < clock.GetCurrentInstant() && n.EndTime > clock.GetCurrentInstant() | ||||
|                 ) | ||||
|                 .OrderByDescending(n => n.Id) | ||||
|                 .FirstOrDefaultAsync(ct); | ||||
| } | ||||
							
								
								
									
										63
									
								
								Foxnouns.Backend/Services/Caching/SingletonCacheService.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Foxnouns.Backend/Services/Caching/SingletonCacheService.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| // Copyright (C) 2023-present sam/u1f320 (vulpine.solutions) | ||||
| // | ||||
| // 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 <https://www.gnu.org/licenses/>. | ||||
| using Foxnouns.Backend.Database; | ||||
| using NodaTime; | ||||
| 
 | ||||
| namespace Foxnouns.Backend.Services.Caching; | ||||
| 
 | ||||
| public abstract class SingletonCacheService<T>( | ||||
|     IServiceProvider serviceProvider, | ||||
|     IClock clock, | ||||
|     ILogger logger | ||||
| ) | ||||
|     where T : class | ||||
| { | ||||
|     private T? _item; | ||||
|     private Instant _lastUpdated = Instant.MinValue; | ||||
|     private readonly SemaphoreSlim _semaphore = new(1, 1); | ||||
|     private readonly ILogger _logger = logger.ForContext<SingletonCacheService<T>>(); | ||||
| 
 | ||||
|     public virtual Duration MaxAge { get; init; } = Duration.FromMinutes(5); | ||||
| 
 | ||||
|     public virtual Func<DatabaseContext, CancellationToken, Task<T?>> FetchFunc { get; init; } = | ||||
|         (_, __) => Task.FromResult<T?>(null); | ||||
| 
 | ||||
|     public async Task<T?> GetAsync(CancellationToken ct = default) | ||||
|     { | ||||
|         await _semaphore.WaitAsync(ct); | ||||
|         try | ||||
|         { | ||||
|             if (_lastUpdated > clock.GetCurrentInstant() - MaxAge) | ||||
|             { | ||||
|                 return _item; | ||||
|             } | ||||
| 
 | ||||
|             _logger.Debug("Cached item of type {Type} is expired, fetching it.", typeof(T)); | ||||
| 
 | ||||
|             await using AsyncServiceScope scope = serviceProvider.CreateAsyncScope(); | ||||
|             await using DatabaseContext db = | ||||
|                 scope.ServiceProvider.GetRequiredService<DatabaseContext>(); | ||||
| 
 | ||||
|             T? item = await FetchFunc(db, ct); | ||||
|             _item = item; | ||||
|             _lastUpdated = clock.GetCurrentInstant(); | ||||
|             return item; | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             _semaphore.Release(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue