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)); 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> /// <summary>Returns the guest session state for the current authenticated user.</summary>
[HttpGet("GuestState")] [HttpGet("GuestState")]
[Authorize(AuthenticationSchemes = "CustomAuthentication")] [Authorize(AuthenticationSchemes = "CustomAuthentication")]
+3 -3
Voir le fichier
@@ -6,9 +6,9 @@
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<RootNamespace>Jellyfin.Plugin.ShareLinks</RootNamespace> <RootNamespace>Jellyfin.Plugin.ShareLinks</RootNamespace>
<AssemblyName>Jellyfin.Plugin.ShareLinks</AssemblyName> <AssemblyName>Jellyfin.Plugin.ShareLinks</AssemblyName>
<Version>1.0.3.1</Version> <Version>1.0.3.0</Version>
<AssemblyVersion>1.0.3.1</AssemblyVersion> <AssemblyVersion>1.0.3.0</AssemblyVersion>
<FileVersion>1.0.3.1</FileVersion> <FileVersion>1.0.3.0</FileVersion>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo> <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
+34
Voir le fichier
@@ -75,6 +75,40 @@ public sealed class ShareLinkCleanupService : IShareLinkCleanupService
await CleanupRecordInternalAsync(record, records, force, cancellationToken).ConfigureAwait(false); 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( private async Task<ShareLinkRecord> CleanupRecordInternalAsync(
ShareLinkRecord record, ShareLinkRecord record,
IReadOnlyList<ShareLinkRecord> allRecords, IReadOnlyList<ShareLinkRecord> allRecords,
+27
Voir le fichier
@@ -173,6 +173,9 @@
<button is="emby-button" type="button" id="RefreshLinks" class="raised"> <button is="emby-button" type="button" id="RefreshLinks" class="raised">
<span>Refresh</span> <span>Refresh</span>
</button> </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> <span id="LinksStatus" class="sl-muted sl-list-status">Loading…</span>
</div> </div>
<div style="overflow-x:auto; margin-top:0.5rem;"> <div style="overflow-x:auto; margin-top:0.5rem;">
@@ -426,6 +429,30 @@
document.querySelector('#RefreshLinks').addEventListener('click', function () { document.querySelector('#RefreshLinks').addEventListener('click', function () {
loadLinks(); 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> </script>
</div> </div>
+2 -2
Voir le fichier
@@ -1,12 +1,12 @@
{ {
"guid": "68540b76-ee74-436d-85ff-2abc884bbea6", "guid": "68540b76-ee74-436d-85ff-2abc884bbea6",
"name": "ShareLinks", "name": "ShareLinks",
"version": "1.0.3.1", "version": "1.0.3.0",
"targetAbi": "10.11.0.0", "targetAbi": "10.11.0.0",
"framework": "net9.0", "framework": "net9.0",
"owner": "Franciskid", "owner": "Franciskid",
"overview": "Secure expiring guest-share links for Jellyfin items.", "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.", "description": "Adds secure, expiring share links for Jellyfin items with JSON-backed storage, token hashing, and cleanup scaffolding.",
"category": "General", "category": "General",
"timestamp": "2026-07-26T20:00:00.0000000Z" "timestamp": "2026-07-26T20:15:00.0000000Z"
} }