Skip to content

Commit 7b045da

Browse files
authored
Merge branch 'main' into w-again
2 parents 19141e4 + b19ac23 commit 7b045da

22 files changed

Lines changed: 473 additions & 60 deletions

Refresh.Database/GameDatabaseContext.Levels.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public GameLevel AddLevel(ISerializedPublishLevel createInfo, TokenGame game, Ga
5252
EnforceMinMaxPlayers = createInfo.EnforceMinMaxPlayers,
5353
SameScreenGame = createInfo.SameScreenGame,
5454
BackgroundGuid = createInfo.BackgroundGuid,
55-
Publisher = publisher,
55+
PublisherUserId = publisher.UserId,
5656
GameVersion = game,
5757
PublishDate = timestamp,
5858
UpdateDate = timestamp,
@@ -63,22 +63,19 @@ public GameLevel AddLevel(ISerializedPublishLevel createInfo, TokenGame game, Ga
6363

6464
this.SaveChanges();
6565

66-
this.CreateRevisionForLevel(level, level.Publisher);
67-
this.GameLevelStatistics.Add(level.Statistics = new GameLevelStatistics
68-
{
69-
LevelId = level.LevelId,
70-
});
71-
72-
this.SaveChanges();
73-
74-
if (level.Publisher != null)
66+
this.WriteEnsuringStatistics(publisher, () =>
7567
{
76-
this.WriteEnsuringStatistics(level.Publisher, () =>
68+
this.GameLevelStatistics.Add(level.Statistics = new GameLevelStatistics
7769
{
78-
level.Publisher.Statistics!.LevelCount++;
70+
LevelId = level.LevelId,
7971
});
80-
}
8172

73+
this.CreateRevisionForLevel(level, level.Publisher);
74+
publisher.Statistics!.LevelCount++;
75+
});
76+
77+
level.Publisher = publisher;
78+
this.Entry(level.Publisher).State = EntityState.Unchanged;
8279
return level;
8380
}
8481

Refresh.Database/GameDatabaseContext.Workers.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ public bool MarkWorkerContacted(int id)
5050
return JsonConvert.DeserializeObject(state.State, type);
5151
}
5252

53+
public IQueryable<string> GetAllJobIds(WorkerClass? workerClass)
54+
{
55+
IQueryable<PersistentJobState> states = this.JobStates;
56+
if (workerClass != null) states = states.Where(s => s.Class == workerClass);
57+
return states.Select(s => s.JobId);
58+
}
59+
5360
public void UpdateOrCreateJobState(string jobId, object state, WorkerClass workerClass)
5461
{
5562
PersistentJobState? jobState = this.JobStates.FirstOrDefault(s => s.JobId == jobId && s.Class == workerClass);
@@ -58,6 +65,7 @@ public void UpdateOrCreateJobState(string jobId, object state, WorkerClass worke
5865
jobState = new PersistentJobState
5966
{
6067
JobId = jobId,
68+
Class = workerClass,
6169
};
6270

6371
this.JobStates.Add(jobState);
@@ -66,4 +74,10 @@ public void UpdateOrCreateJobState(string jobId, object state, WorkerClass worke
6674
jobState.State = JsonConvert.SerializeObject(state, Formatting.None);
6775
this.SaveChanges();
6876
}
77+
78+
public void RemoveJobState(string jobId, bool save = true)
79+
{
80+
this.JobStates.RemoveRange(s => s.JobId == jobId);
81+
if (save) this.SaveChanges();
82+
}
6983
}

Refresh.Interfaces.APIv3/Endpoints/ApiTypes/Errors/ApiAuthenticationError.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ public class ApiAuthenticationError : ApiError
99
public const string NoPermissionsForCreationWhen = "You lack the permissions to create this type of resource.";
1010
public static readonly ApiAuthenticationError NoPermissionsForCreation = new(NoPermissionsForCreationWhen);
1111

12+
public const string ReadOnlyErrorWhen = "The server is currently in read-only mode.";
13+
public static readonly ApiAuthenticationError ReadOnlyError = new(ReadOnlyErrorWhen);
14+
1215
public const string NotAuthenticatedWhen = "You are not authenticated.";
1316
public static readonly ApiAuthenticationError NotAuthenticated = new(NotAuthenticatedWhen);
1417

Refresh.Interfaces.APIv3/Endpoints/CommentApiEndpoints.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Bunkum.Core.RateLimit;
55
using Bunkum.Protocols.Http;
66
using Refresh.Common.Constants;
7+
using Refresh.Core.Configuration;
78
using Refresh.Core.RateLimits.Comments;
89
using Refresh.Core.RateLimits.Relations;
910
using Refresh.Core.Types.Data;
@@ -51,8 +52,11 @@ public ApiListResponse<ApiProfileCommentResponse> GetCommentsOnProfile(RequestCo
5152
public ApiResponse<ApiProfileCommentResponse> PostCommentOnProfile(RequestContext context,
5253
DataContext dataContext, GameUser user, ApiCommentPostRequest body,
5354
[DocSummary(SharedParamDescriptions.UserIdParam)] string id,
54-
[DocSummary(SharedParamDescriptions.UserIdTypeParam)] string idType)
55+
[DocSummary(SharedParamDescriptions.UserIdTypeParam)] string idType, GameServerConfig config)
5556
{
57+
if (user.IsWriteBlocked(config))
58+
return ApiAuthenticationError.ReadOnlyError;
59+
5660
GameUser? profile = dataContext.Database.GetUserByIdAndType(idType, id);
5761
if (profile == null) return ApiNotFoundError.UserMissingError;
5862

@@ -99,8 +103,11 @@ public ApiOkResponse DeleteProfileComment(RequestContext context, DataContext da
99103
[RateLimitSettings(CommonRelationEndpointLimits.TimeoutDuration, CommonRelationEndpointLimits.RequestAmount,
100104
CommonRelationEndpointLimits.BlockDuration, CommonRelationEndpointLimits.RequestBucket)]
101105
public ApiOkResponse RateProfileComment(RequestContext context, DataContext dataContext, GameUser user, int id,
102-
[DocSummary("The user's new rating for the comment. -1 = dislike, 0 = neutral, 1 = like.")] string rawRating)
106+
[DocSummary("The user's new rating for the comment. -1 = dislike, 0 = neutral, 1 = like.")] string rawRating, GameServerConfig config)
103107
{
108+
if (user.IsWriteBlocked(config))
109+
return ApiAuthenticationError.ReadOnlyError;
110+
104111
GameProfileComment? comment = dataContext.Database.GetProfileCommentById(id);
105112
if (comment == null) return ApiNotFoundError.CommentMissingError;
106113

@@ -138,8 +145,11 @@ public ApiListResponse<ApiLevelCommentResponse> GetCommentsOnLevel(RequestContex
138145
[RateLimitSettings(CommentUploadEndpointLimits.TimeoutDuration, CommentUploadEndpointLimits.RequestAmount,
139146
CommentUploadEndpointLimits.BlockDuration, CommentUploadEndpointLimits.RequestBucket)]
140147
public ApiResponse<ApiLevelCommentResponse> PostCommentOnLevel(RequestContext context,
141-
DataContext dataContext, int id, GameUser user, ApiCommentPostRequest body)
148+
DataContext dataContext, int id, GameUser user, ApiCommentPostRequest body, GameServerConfig config)
142149
{
150+
if (user.IsWriteBlocked(config))
151+
return ApiAuthenticationError.ReadOnlyError;
152+
143153
GameLevel? level = dataContext.Database.GetLevelById(id);
144154
if (level == null) return ApiNotFoundError.LevelMissingError;
145155

@@ -186,8 +196,11 @@ public ApiOkResponse DeleteLevelComment(RequestContext context, DataContext data
186196
[RateLimitSettings(CommonRelationEndpointLimits.TimeoutDuration, CommonRelationEndpointLimits.RequestAmount,
187197
CommonRelationEndpointLimits.BlockDuration, CommonRelationEndpointLimits.RequestBucket)]
188198
public ApiOkResponse RateLevelComment(RequestContext context, DataContext dataContext, GameUser user, int id,
189-
[DocSummary("The user's new rating for the comment. -1 = dislike, 0 = neutral, 1 = like.")] string rawRating)
199+
[DocSummary("The user's new rating for the comment. -1 = dislike, 0 = neutral, 1 = like.")] string rawRating, GameServerConfig config)
190200
{
201+
if (user.IsWriteBlocked(config))
202+
return ApiAuthenticationError.ReadOnlyError;
203+
191204
GameLevelComment? comment = dataContext.Database.GetLevelCommentById(id);
192205
if (comment == null) return ApiNotFoundError.CommentMissingError;
193206

Refresh.Interfaces.APIv3/Endpoints/LevelApiEndpoints.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Refresh.Common.Constants;
88
using Refresh.Common.Verification;
99
using Refresh.Core.Authentication.Permission;
10+
using Refresh.Core.Configuration;
1011
using Refresh.Core.RateLimits.Levels;
1112
using Refresh.Core.RateLimits.Playlists;
1213
using Refresh.Core.RateLimits.Presence;
@@ -63,9 +64,12 @@ public ApiResponse<ApiGameLevelResponse> GetLevelByRootResource(RequestContext c
6364
[DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)]
6465
[DocError(typeof(ApiAuthenticationError), ApiAuthenticationError.NoPermissionsForObjectWhen)]
6566
[RateLimitSettings(420, 8, 300, "level-update-api")]
66-
public ApiResponse<ApiGameLevelResponse> EditLevelById(RequestContext context,
67-
[DocSummary("The ID of the level")] int id, ApiEditLevelRequest body, DataContext dataContext)
67+
public ApiResponse<ApiGameLevelResponse> EditLevelById(RequestContext context, GameUser user,
68+
[DocSummary("The ID of the level")] int id, ApiEditLevelRequest body, DataContext dataContext, GameServerConfig config)
6869
{
70+
if (user.IsWriteBlocked(config))
71+
return ApiAuthenticationError.ReadOnlyError;
72+
6973
GameLevel? level = dataContext.Database.GetLevelById(id);
7074
if (level == null) return ApiNotFoundError.LevelMissingError;
7175

@@ -162,9 +166,12 @@ public ApiResponse<ApiGameLevelOwnRelationsResponse> GetLevelRelationsOfUser(Req
162166
[DocError(typeof(ApiNotFoundError), ApiNotFoundError.LevelMissingErrorWhen)]
163167
[RateLimitSettings(CommonRelationEndpointLimits.TimeoutDuration, CommonRelationEndpointLimits.RequestAmount,
164168
CommonRelationEndpointLimits.BlockDuration, CommonRelationEndpointLimits.RequestBucket)]
165-
public ApiOkResponse FavouriteLevel(RequestContext context, GameDatabaseContext database, GameUser user,
166-
[DocSummary("The ID of the level")] int id, DataContext dataContext)
169+
public ApiOkResponse HeartLevel(RequestContext context, GameDatabaseContext database, GameUser user,
170+
[DocSummary("The ID of the level")] int id, DataContext dataContext, GameServerConfig config)
167171
{
172+
if (user.IsWriteBlocked(config))
173+
return ApiAuthenticationError.ReadOnlyError;
174+
168175
GameLevel? level = database.GetLevelById(id);
169176
if (level == null) return ApiNotFoundError.LevelMissingError;
170177

@@ -179,8 +186,11 @@ public ApiOkResponse FavouriteLevel(RequestContext context, GameDatabaseContext
179186
[RateLimitSettings(CommonRelationEndpointLimits.TimeoutDuration, CommonRelationEndpointLimits.RequestAmount,
180187
CommonRelationEndpointLimits.BlockDuration, CommonRelationEndpointLimits.RequestBucket)]
181188
public ApiOkResponse UnheartLevel(RequestContext context, GameDatabaseContext database, GameUser user,
182-
[DocSummary("The ID of the level")] int id, DataContext dataContext)
189+
[DocSummary("The ID of the level")] int id, DataContext dataContext, GameServerConfig config)
183190
{
191+
if (user.IsWriteBlocked(config))
192+
return ApiAuthenticationError.ReadOnlyError;
193+
184194
GameLevel? level = database.GetLevelById(id);
185195
if (level == null) return ApiNotFoundError.LevelMissingError;
186196

@@ -232,7 +242,7 @@ public ApiOkResponse DequeueLevel(RequestContext context, GameDatabaseContext da
232242
[RateLimitSettings(CommonRelationEndpointLimits.TimeoutDuration, CommonRelationEndpointLimits.RequestAmount,
233243
CommonRelationEndpointLimits.BlockDuration, CommonRelationEndpointLimits.RequestBucket)]
234244
public ApiOkResponse ClearQueuedLevels(RequestContext context, GameDatabaseContext database,
235-
IDataStore dataStore, GameUser user, DataContext dataContext)
245+
GameUser user, DataContext dataContext)
236246
{
237247
database.ClearQueue(user);
238248
dataContext.Cache.ClearQueueByUser(user);

Refresh.Interfaces.APIv3/Endpoints/PlaylistApiEndpoints.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Refresh.Interfaces.APIv3.Endpoints.DataTypes.Response.Playlists;
1515
using Refresh.Interfaces.APIv3.Extensions;
1616
using Refresh.Core.RateLimits.Playlists;
17+
using Refresh.Core.Configuration;
1718

1819
namespace Refresh.Interfaces.APIv3.Endpoints;
1920

@@ -54,8 +55,11 @@ public class PlaylistApiEndpoints : EndpointGroup
5455
[DocQueryParam("parentId", "If set, the new playlist will be added to the playlist specified by ID here instead of the root playlist. "
5556
+ "If the specified playlist doesn't exist or is not owned by the user calling this endpoint, nothing will happen.")]
5657
public ApiResponse<ApiGamePlaylistResponse> CreatePlaylist(RequestContext context, DataContext dataContext,
57-
GameUser user, ApiPlaylistCreationRequest body)
58+
GameUser user, ApiPlaylistCreationRequest body, GameServerConfig config)
5859
{
60+
if (user.IsWriteBlocked(config))
61+
return ApiAuthenticationError.ReadOnlyError;
62+
5963
ApiError? error = this.ValidatePlaylist(body, dataContext);
6064
if (error != null) return error;
6165

@@ -96,8 +100,11 @@ public ApiResponse<ApiGamePlaylistResponse> CreatePlaylist(RequestContext contex
96100
[RateLimitSettings(PlaylistCreationEndpointLimits.UploadTimeoutDuration, PlaylistCreationEndpointLimits.MaxCreateAmount,
97101
PlaylistCreationEndpointLimits.UploadBlockDuration, PlaylistCreationEndpointLimits.CreateBucket)]
98102
public ApiResponse<ApiGamePlaylistResponse> UpdatePlaylist(RequestContext context, DataContext dataContext,
99-
GameUser user, ApiPlaylistCreationRequest body, int id)
103+
GameUser user, ApiPlaylistCreationRequest body, int id, GameServerConfig config)
100104
{
105+
if (user.IsWriteBlocked(config))
106+
return ApiAuthenticationError.ReadOnlyError;
107+
101108
GamePlaylist? playlist = dataContext.Database.GetPlaylistById(id);
102109
if (playlist == null) return ApiNotFoundError.PlaylistMissingError;
103110

@@ -149,8 +156,11 @@ public ApiResponse<ApiGamePlaylistResponse> GetPlaylistById(RequestContext conte
149156
[RateLimitSettings(PlaylistModificationEndpointLimits.TimeoutDuration, PlaylistModificationEndpointLimits.RequestAmount,
150157
PlaylistModificationEndpointLimits.BlockDuration, PlaylistModificationEndpointLimits.RequestBucket)]
151158
public ApiOkResponse AddLevelToPlaylist(RequestContext context, DataContext dataContext,
152-
GameUser user, int playlistId, int levelId)
159+
GameUser user, int playlistId, int levelId, GameServerConfig config)
153160
{
161+
if (user.IsWriteBlocked(config))
162+
return ApiAuthenticationError.ReadOnlyError;
163+
154164
GamePlaylist? playlist = dataContext.Database.GetPlaylistById(playlistId);
155165
if (playlist == null) return ApiNotFoundError.ParentPlaylistMissingError;
156166

@@ -172,8 +182,11 @@ public ApiOkResponse AddLevelToPlaylist(RequestContext context, DataContext data
172182
[RateLimitSettings(PlaylistModificationEndpointLimits.TimeoutDuration, PlaylistModificationEndpointLimits.RequestAmount,
173183
PlaylistModificationEndpointLimits.BlockDuration, PlaylistModificationEndpointLimits.RequestBucket)]
174184
public ApiOkResponse RemoveLevelFromPlaylist(RequestContext context, DataContext dataContext,
175-
GameUser user, int playlistId, int levelId)
185+
GameUser user, int playlistId, int levelId, GameServerConfig config)
176186
{
187+
if (user.IsWriteBlocked(config))
188+
return ApiAuthenticationError.ReadOnlyError;
189+
177190
GamePlaylist? playlist = dataContext.Database.GetPlaylistById(playlistId);
178191
if (playlist == null) return ApiNotFoundError.ParentPlaylistMissingError;
179192

@@ -195,8 +208,11 @@ public ApiOkResponse RemoveLevelFromPlaylist(RequestContext context, DataContext
195208
[RateLimitSettings(PlaylistModificationEndpointLimits.TimeoutDuration, PlaylistModificationEndpointLimits.RequestAmount,
196209
PlaylistModificationEndpointLimits.BlockDuration, PlaylistModificationEndpointLimits.RequestBucket)]
197210
public ApiOkResponse AddPlaylistToPlaylist(RequestContext context, DataContext dataContext,
198-
GameUser user, int playlistId, int subPlaylistId)
211+
GameUser user, int playlistId, int subPlaylistId, GameServerConfig config)
199212
{
213+
if (user.IsWriteBlocked(config))
214+
return ApiAuthenticationError.ReadOnlyError;
215+
200216
GamePlaylist? parent = dataContext.Database.GetPlaylistById(playlistId);
201217
if (parent == null) return ApiNotFoundError.ParentPlaylistMissingError;
202218

@@ -218,8 +234,11 @@ public ApiOkResponse AddPlaylistToPlaylist(RequestContext context, DataContext d
218234
[RateLimitSettings(PlaylistModificationEndpointLimits.TimeoutDuration, PlaylistModificationEndpointLimits.RequestAmount,
219235
PlaylistModificationEndpointLimits.BlockDuration, PlaylistModificationEndpointLimits.RequestBucket)]
220236
public ApiOkResponse RemovePlaylistFromPlaylist(RequestContext context, DataContext dataContext,
221-
GameUser user, int playlistId, int subPlaylistId)
237+
GameUser user, int playlistId, int subPlaylistId, GameServerConfig config)
222238
{
239+
if (user.IsWriteBlocked(config))
240+
return ApiAuthenticationError.ReadOnlyError;
241+
223242
GamePlaylist? parent = dataContext.Database.GetPlaylistById(playlistId);
224243
if (parent == null) return ApiNotFoundError.ParentPlaylistMissingError;
225244

Refresh.Interfaces.APIv3/Endpoints/ResourceApiEndpoints.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ public ApiResponse<ApiGameAssetResponse> GetPspAssetInfo(RequestContext context,
161161
[DocError(typeof(ApiValidationError), ApiValidationError.BodyTooLongErrorWhen)]
162162
[DocError(typeof(ApiValidationError), ApiValidationError.CannotReadAssetErrorWhen)]
163163
[DocError(typeof(ApiValidationError), ApiValidationError.BodyMustBeImageErrorWhen)]
164-
[DocError(typeof(ApiAuthenticationError), ApiAuthenticationError.NoPermissionsForCreationWhen)]
165164
[RateLimitSettings(420, 10, 300, "image-upload-api")]
166165
public ApiResponse<ApiGameAssetResponse> UploadImageAsset(RequestContext context, GameDatabaseContext database,
167166
IDataStore dataStore, AssetImporter importer, GameServerConfig config,
@@ -176,7 +175,7 @@ IntegrationConfig integration
176175
// If we're blocking asset uploads, throw unless the user is an admin.
177176
// We also have the ability to block asset uploads for trusted users (when they would normally bypass this)
178177
if (user.IsWriteBlocked(config))
179-
return ApiAuthenticationError.NoPermissionsForCreation;
178+
return ApiAuthenticationError.ReadOnlyError;
180179

181180
if (!CommonPatterns.Sha1Regex().IsMatch(hash)) return ApiValidationError.HashInvalidError;
182181

0 commit comments

Comments
 (0)