Skip to content

Commit

Permalink
Merge pull request #14 from bdach/mirrors
Browse files Browse the repository at this point in the history
Implement required interactions with beatmap mirrors
  • Loading branch information
peppy authored Dec 30, 2024
2 parents db04619 + 45137e9 commit 42064f6
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public BeatmapSubmissionControllerTest(IntegrationTestWebApplicationFactory<Prog
services.AddTransient<IBeatmapStorage>(_ => beatmapStorage);
services.AddTransient<BeatmapPackagePatcher>();
services.AddTransient<ILegacyIO>(_ => mockLegacyIO.Object);
services.AddTransient<IMirrorService, NoOpMirrorService>();
});
}).CreateClient();
}
Expand Down
10 changes: 9 additions & 1 deletion osu.Server.BeatmapSubmission/BeatmapSubmissionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,18 @@ public class BeatmapSubmissionController : Controller
private readonly IBeatmapStorage beatmapStorage;
private readonly BeatmapPackagePatcher patcher;
private readonly ILegacyIO legacyIO;
private readonly IMirrorService mirrorService;

public BeatmapSubmissionController(IBeatmapStorage beatmapStorage, BeatmapPackagePatcher patcher, ILegacyIO legacyIO)
public BeatmapSubmissionController(
IBeatmapStorage beatmapStorage,
BeatmapPackagePatcher patcher,
ILegacyIO legacyIO,
IMirrorService mirrorService)
{
this.beatmapStorage = beatmapStorage;
this.patcher = patcher;
this.legacyIO = legacyIO;
this.mirrorService = mirrorService;
}

/// <summary>
Expand Down Expand Up @@ -403,6 +409,8 @@ private async Task<bool> updateBeatmapSetFromArchiveAsync(uint beatmapSetId, Str
if (await db.IsBeatmapSetNominatedAsync(beatmapSetId))
await legacyIO.DisqualifyBeatmapSetAsync(beatmapSetId, "This beatmap set was updated by the mapper after a nomination. Please ensure to re-check the beatmaps for new issues. If you are the mapper, please comment in this thread on what you changed.");

await mirrorService.PurgeBeatmapSetAsync(db, beatmapSetId);

if (!await db.IsBeatmapSetInProcessingQueueAsync(beatmapSetId))
{
await db.AddBeatmapSetToProcessingQueueAsync(beatmapSetId);
Expand Down
19 changes: 19 additions & 0 deletions osu.Server.BeatmapSubmission/DatabaseOperationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,5 +397,24 @@ public static async Task<IEnumerable<uint>> GetBeatmapOwnersAsync(this MySqlConn
},
transaction);
}

public static async Task<IEnumerable<osu_mirror>> GetMirrorsRequiringUpdateAsync(this MySqlConnection db, MySqlTransaction? transaction = null)
{
return await db.QueryAsync<osu_mirror>(
"SELECT * FROM `osu_mirrors` WHERE `version` > 1 AND `perform_updates` > 0",
transaction: transaction);
}

public static async Task MarkPendingPurgeAsync(this MySqlConnection db, osu_mirror mirror, uint beatmapSetId, MySqlTransaction? transaction = null)
{
await db.ExecuteAsync(
"UPDATE `osu_mirrors` SET `pending_purge` = CONCAT(`pending_purge`, ',', @beatmapset_id) WHERE `mirror_id` = @mirror_id",
new
{
beatmapset_id = beatmapSetId,
mirror_id = mirror.mirror_id
},
transaction);
}
}
}
16 changes: 16 additions & 0 deletions osu.Server.BeatmapSubmission/Models/Database/osu_mirror.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

// ReSharper disable InconsistentNaming

namespace osu.Server.BeatmapSubmission.Models.Database
{
public class osu_mirror
{
public ushort mirror_id { get; set; }
public string base_url { get; set; } = string.Empty;
public decimal version { get; set; }
public bool perform_updates { get; set; }
public string secret_key { get; set; } = string.Empty;
}
}
2 changes: 2 additions & 0 deletions osu.Server.BeatmapSubmission/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public static void Main(string[] args)
builder.Services.AddTransient<BeatmapPackagePatcher>();
builder.Services.AddHttpClient();
builder.Services.AddTransient<ILegacyIO, LegacyIO>();
builder.Services.AddTransient<IMirrorService, NoOpMirrorService>();
builder.Services.AddSwaggerGen(c =>
{
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, Assembly.GetExecutingAssembly().GetName().Name + ".xml"));
Expand All @@ -57,6 +58,7 @@ public static void Main(string[] args)
builder.Services.AddTransient<BeatmapPackagePatcher>();
builder.Services.AddHttpClient();
builder.Services.AddTransient<ILegacyIO, LegacyIO>();
builder.Services.AddTransient<IMirrorService, MirrorService>();
break;
}
}
Expand Down
12 changes: 12 additions & 0 deletions osu.Server.BeatmapSubmission/Services/IMirrorService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using MySqlConnector;

namespace osu.Server.BeatmapSubmission.Services
{
public interface IMirrorService
{
Task PurgeBeatmapSetAsync(MySqlConnection db, uint beatmapSetId);
}
}
57 changes: 57 additions & 0 deletions osu.Server.BeatmapSubmission/Services/MirrorService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using MySqlConnector;
using osu.Framework.Extensions;
using osu.Server.BeatmapSubmission.Models.Database;

namespace osu.Server.BeatmapSubmission.Services
{
public class MirrorService : IMirrorService
{
private readonly HttpClient client;

public MirrorService(HttpClient client)
{
this.client = client;
}

public async Task PurgeBeatmapSetAsync(MySqlConnection db, uint beatmapSetId)
{
osu_mirror[] mirrors = (await db.GetMirrorsRequiringUpdateAsync()).ToArray();

foreach (var mirror in mirrors)
{
if (await performMirrorAction(mirror, "purge", new Dictionary<string, string> { ["s"] = beatmapSetId.ToString() }) != "1")
await db.MarkPendingPurgeAsync(mirror, beatmapSetId);
}
}

private async Task<string?> performMirrorAction(osu_mirror mirror, string action, Dictionary<string, string> data)
{
data["ts"] = DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
data["action"] = action;
data["cs"] = ($"{data.GetValueOrDefault("s")}{data.GetValueOrDefault("fd")}{data.GetValueOrDefault("fs")}{data.GetValueOrDefault("ts")}"
+ $"{data.GetValueOrDefault("nv")}{data.GetValueOrDefault("action")}{mirror.secret_key}").ComputeMD5Hash();

var request = new HttpRequestMessage(HttpMethod.Post, mirror.base_url);
request.Content = new FormUrlEncodedContent(data);

try
{
var response = await client.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
catch (Exception)
{
// TODO: log error
return null;
}
}
}

public class NoOpMirrorService : IMirrorService
{
public Task PurgeBeatmapSetAsync(MySqlConnection db, uint beatmapSetId) => Task.CompletedTask;
}
}

0 comments on commit 42064f6

Please sign in to comment.