Merge pull request #12 from Franciskid/feature/cleanup-finished-links

Add a button to clear out finished share links, and fold 1.0.3.1 back into 1.0.3.0
Cette révision appartient à :
Francois CB
2026-07-26 23:06:40 +02:00
révisé par GitHub
révision 3ed982b893
5 fichiers modifiés avec 81 ajouts et 5 suppressions
+15
Voir le fichier
@@ -269,6 +269,21 @@ public sealed class ShareLinksController : ControllerBase
return Ok(ToDto(record));
}
/// <summary>Removes revoked, expired and failed share links from the store.</summary>
[HttpPost("Admin/Cleanup")]
[Authorize(AuthenticationSchemes = "CustomAuthentication")]
public async Task<ActionResult> Cleanup(CancellationToken cancellationToken)
{
SetNoStoreHeaders();
if (!User.IsInRole("Administrator"))
{
return Forbid();
}
var removed = await _cleanupService.PurgeFinishedAsync(cancellationToken).ConfigureAwait(false);
return Ok(new { removed });
}
/// <summary>Returns the guest session state for the current authenticated user.</summary>
[HttpGet("GuestState")]
[Authorize(AuthenticationSchemes = "CustomAuthentication")]
+3 -3
Voir le fichier
@@ -6,9 +6,9 @@
<LangVersion>latest</LangVersion>
<RootNamespace>Jellyfin.Plugin.ShareLinks</RootNamespace>
<AssemblyName>Jellyfin.Plugin.ShareLinks</AssemblyName>
<Version>1.0.3.1</Version>
<AssemblyVersion>1.0.3.1</AssemblyVersion>
<FileVersion>1.0.3.1</FileVersion>
<Version>1.0.3.0</Version>
<AssemblyVersion>1.0.3.0</AssemblyVersion>
<FileVersion>1.0.3.0</FileVersion>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<ImplicitUsings>disable</ImplicitUsings>
+34
Voir le fichier
@@ -75,6 +75,40 @@ public sealed class ShareLinkCleanupService : IShareLinkCleanupService
await CleanupRecordInternalAsync(record, records, force, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Runs a normal cleanup pass and then drops every record that is finished
/// with, so the admin list does not grow forever. A link that can still be
/// used is never removed, and neither is one whose guest is still watching.
/// </summary>
public async Task<int> PurgeFinishedAsync(CancellationToken cancellationToken)
{
// Teardown first: this expires anything past its date and removes the guest
// account and tags, so nothing is dropped from the store while it still has
// state hanging off it.
await CleanupAsync(cancellationToken).ConfigureAwait(false);
var records = await _store.ListAsync(cancellationToken).ConfigureAwait(false);
var removed = 0;
foreach (var record in records)
{
cancellationToken.ThrowIfCancellationRequested();
if (record.Status is not (ShareLinkStatus.Expired or ShareLinkStatus.Revoked or ShareLinkStatus.Failed))
{
continue;
}
await _store.DeleteAsync(record.Id, cancellationToken).ConfigureAwait(false);
removed++;
}
if (removed > 0)
{
_logger.LogInformation("ShareLinks: purged {Count} finished share-link record(s).", removed);
}
return removed;
}
private async Task<ShareLinkRecord> CleanupRecordInternalAsync(
ShareLinkRecord record,
IReadOnlyList<ShareLinkRecord> allRecords,
+27
Voir le fichier
@@ -173,6 +173,9 @@
<button is="emby-button" type="button" id="RefreshLinks" class="raised">
<span>Refresh</span>
</button>
<button is="emby-button" type="button" id="CleanupLinks" class="raised">
<span>Clean up finished links</span>
</button>
<span id="LinksStatus" class="sl-muted sl-list-status">Loading…</span>
</div>
<div style="overflow-x:auto; margin-top:0.5rem;">
@@ -426,6 +429,30 @@
document.querySelector('#RefreshLinks').addEventListener('click', function () {
loadLinks();
});
document.querySelector('#CleanupLinks').addEventListener('click', function () {
var button = page.querySelector('#CleanupLinks');
var status = page.querySelector('#LinksStatus');
button.disabled = true;
status.textContent = 'Cleaning up…';
ApiClient.ajax({
type: 'POST',
url: ApiClient.getUrl('ShareLinks/Admin/Cleanup'),
dataType: 'json'
}).then(function (result) {
var removed = result && (result.removed !== undefined ? result.removed : result.Removed);
removed = parseInt(removed, 10) || 0;
return loadLinks().then(function () {
page.querySelector('#LinksStatus').textContent = removed === 1
? '1 finished link removed.'
: removed + ' finished links removed.';
});
}).catch(function () {
status.textContent = 'Cleanup failed.';
}).finally(function () {
button.disabled = false;
});
});
})();
</script>
</div>
+2 -2
Voir le fichier
@@ -1,12 +1,12 @@
{
"guid": "68540b76-ee74-436d-85ff-2abc884bbea6",
"name": "ShareLinks",
"version": "1.0.3.1",
"version": "1.0.3.0",
"targetAbi": "10.11.0.0",
"framework": "net9.0",
"owner": "Franciskid",
"overview": "Secure expiring guest-share links for Jellyfin items.",
"description": "Adds secure, expiring share links for Jellyfin items with JSON-backed storage, token hashing, and cleanup scaffolding.",
"category": "General",
"timestamp": "2026-07-26T20:00:00.0000000Z"
"timestamp": "2026-07-26T20:15:00.0000000Z"
}