Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions .github/scripts/inject-token.ps1

This file was deleted.

10 changes: 0 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,6 @@ jobs:
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
echo "CHANNEL=$channel" >> $env:GITHUB_OUTPUT

- name: Inject Secrets
shell: pwsh
run: |
./.github/scripts/inject-token.ps1 -Token "${{ secrets.UPLOADTHING_TOKEN }}"

- name: Build Projects
shell: pwsh
run: |
Expand Down Expand Up @@ -285,11 +280,6 @@ jobs:
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

- name: Inject Secrets
shell: pwsh
run: |
./.github/scripts/inject-token.ps1 -Token "${{ secrets.UPLOADTHING_TOKEN }}"

- name: Build Projects
run: |
BUILD_PROPS="-p:Version=${{ steps.buildinfo.outputs.VERSION }} -p:GitShortHash=${{ steps.buildinfo.outputs.SHORT_HASH }} -p:PullRequestNumber=${{ steps.buildinfo.outputs.PR_NUMBER }} -p:BuildChannel=${{ steps.buildinfo.outputs.CHANNEL }}"
Expand Down
10 changes: 0 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ jobs:
$version = "0.0.${{ github.run_number }}"
echo "VERSION=$version" >> $env:GITHUB_OUTPUT

- name: Inject Secrets
shell: pwsh
run: |
./.github/scripts/inject-token.ps1 -Token "${{ secrets.UPLOADTHING_TOKEN }}"

- name: Publish Windows App
shell: pwsh
run: |
Expand Down Expand Up @@ -116,11 +111,6 @@ jobs:
- name: Install Linux Dependencies
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev libx11-dev

- name: Inject Secrets
shell: pwsh
run: |
./.github/scripts/inject-token.ps1 -Token "${{ secrets.UPLOADTHING_TOKEN }}"

- name: Publish Linux App
run: |
dotnet publish "${{ env.LINUX_PROJECT }}" \
Expand Down
75 changes: 2 additions & 73 deletions GenHub/GenHub.Core/Constants/ApiConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,84 +59,13 @@ public static class ApiConstants
/// </summary>
public const string GitHubApiRunArtifactsFormat = "https://api.github.com/repos/{0}/{1}/actions/runs/{2}/artifacts";

// UploadThing

/// <summary>
/// UploadThing API version.
/// </summary>
public const string UploadThingApiVersion = "7.7.4";

/// <summary>
/// UploadThing prepare upload URL.
/// </summary>
public const string UploadThingPrepareUrl = "https://api.uploadthing.com/v7/prepareUpload";

/// <summary>
/// UploadThing delete file URL.
/// </summary>
public const string UploadThingDeleteUrl = "https://api.uploadthing.com/v6/deleteFiles";

/// <summary>
/// UploadThing public file URL format.
/// </summary>
public const string UploadThingPublicUrlFormat = "https://utfs.io/f/{0}";
// UploadThing links

/// <summary>
/// UploadThing URL fragment for identification.
/// </summary>
public const string UploadThingUrlFragment = "utfs.io/f/";

/// <summary>
/// UploadThing token environment variable.
/// </summary>
public const string UploadThingTokenEnvVar = "UPLOADTHING_TOKEN";

/// <summary>
/// Alternative UploadThing token environment variable.
/// </summary>
public const string UploadThingTokenEnvVarAlt = "GENHUB_UPLOADTHING_TOKEN";

/// <summary>
/// Gets the default UploadThing token injected at build time (Obfuscated).
/// </summary>
public static string BuildTimeUploadThingToken
{
get
{
// This is a simple XOR obfuscation to prevent the raw token from appearing in strings/debuggers.
// The actual values are injected during the GitHub Actions build process.
byte[] data = []; // [PLACEHOLDER_DATA]
byte[] key = []; // [PLACEHOLDER_KEY]

if (data.Length == 0 || key.Length == 0) return string.Empty;

var result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[i % key.Length]);
}

return System.Text.Encoding.UTF8.GetString(result);
}
}

/// <summary>
/// UploadThing API key header.
/// </summary>
public const string UploadThingApiKeyHeader = "x-uploadthing-api-key";

/// <summary>
/// UploadThing version header.
/// </summary>
public const string UploadThingVersionHeader = "x-uploadthing-version";

// Media Types

/// <summary>
/// Media type for ZIP files.
/// </summary>
public const string MediaTypeZip = "application/zip";

// GenTool

/// <summary>
Expand Down Expand Up @@ -167,4 +96,4 @@ public static string BuildTimeUploadThingToken
/// UserAgent string that mimics a standard web browser.
/// </summary>
public const string BrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
}
}
25 changes: 0 additions & 25 deletions GenHub/GenHub.Core/Constants/ErrorMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ namespace GenHub.Core.Constants;
/// </summary>
public static class ErrorMessages
{
/// <summary>
/// Error message for missing UploadThing token.
/// </summary>
public const string UploadThingTokenMissing = "UploadThing V7 Token is missing. Ensure UPLOADTHING_TOKEN is in your .env file.";

/// <summary>
/// Error message for ZIP validation failure.
/// </summary>
Expand All @@ -20,26 +15,6 @@ public static class ErrorMessages
/// </summary>
public const string FileExceedsSizeLimit = "File exceeds size limit: {Path}";

/// <summary>
/// Error message for failed prepare upload.
/// </summary>
public const string V7PrepareUploadFailed = "V7 PrepareUpload failed: {StatusCode} - {Error}";

/// <summary>
/// Error message for missing required fields in UploadThing response.
/// </summary>
public const string UploadThingMissingFields = "UploadThing V7 returned 200 OK but missing required fields. Response: {Response}";

/// <summary>
/// Error message for failed binary upload.
/// </summary>
public const string V7BinaryUploadFailed = "V7 PUT Binary Upload failed: {StatusCode} - {Error}";

/// <summary>
/// Error message for exception in UploadThing flow.
/// </summary>
public const string ExceptionInUploadThingFlow = "Exception in UploadThing V7 flow";

/// <summary>
/// Error message for could not extract download URL.
/// </summary>
Expand Down
10 changes: 0 additions & 10 deletions GenHub/GenHub.Core/Constants/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,6 @@ public static class LogMessages
/// </summary>
public const string FailedToDeleteReplay = "Failed to delete replay: {Path}";

/// <summary>
/// Log message for uploading to UploadThing.
/// </summary>
public const string UploadingToUploadThing = "Uploading to UploadThing V7: {Path}";

/// <summary>
/// Log message for successful UploadThing upload.
/// </summary>
public const string UploadThingSuccessful = "UploadThing V7 successful. Public URL: {Url}";

/// <summary>
/// Log message for failed ZIP creation.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions GenHub/GenHub.Core/Interfaces/Common/IUploadHistoryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public interface IUploadHistoryService
Task<IEnumerable<UploadHistoryItem>> GetUploadHistoryAsync();

/// <summary>
/// Removes a history item.
/// Removes an item from local history without deleting the hosted file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Missing parameter validation

The RemoveHistoryItemAsync method accepts a string url parameter but doesn't validate that the URL is not null or empty. This could cause issues if called with invalid input.

Suggested change
/// Removes an item from local history without deleting the hosted file.
Task RemoveHistoryItemAsync(string url);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping this unchanged. The parameter is non-nullable, and the implementation’s equality check safely treats null or empty input as a no-op. The suggested interface change is identical and adds no runtime validation.

/// </summary>
/// <param name="url">The URL.</param>
/// <returns>A task representing the asynchronous operation.</returns>
Task RemoveHistoryItemAsync(string url);

/// <summary>
/// Clears the history.
/// Clears local history without deleting hosted files.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
Task ClearHistoryAsync();
}
}
7 changes: 1 addition & 6 deletions GenHub/GenHub.Core/Models/Tools/UploadRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,4 @@ public sealed class UploadRecord
/// Gets or sets the name of the uploaded file.
/// </summary>
public string? FileName { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this item is queued for deletion.
/// </summary>
public bool IsPendingDeletion { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using GenHub.Core.Interfaces.Common;
using GenHub.Features.Tools.Services;
using Microsoft.Extensions.Logging;
using Moq;

namespace GenHub.Tests.Core.Features.Tools.Services;

/// <summary>
/// Tests for local upload history behavior while cloud deletion is disabled.
/// </summary>
public sealed class UploadHistoryServiceTests : IDisposable
{
private readonly string _tempDirectory;

/// <summary>
/// Initializes a new instance of the <see cref="UploadHistoryServiceTests"/> class.
/// </summary>
public UploadHistoryServiceTests()
{
_tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(_tempDirectory);
}

/// <summary>
/// Removes temporary test data.
/// </summary>
public void Dispose()
{
if (Directory.Exists(_tempDirectory))
{
Directory.Delete(_tempDirectory, recursive: true);
}

GC.SuppressFinalize(this);
}

/// <summary>
/// Verifies that removing an item deletes its local record immediately.
/// </summary>
/// <returns>A task representing the asynchronous test operation.</returns>
[Fact]
public async Task RemoveHistoryItemAsync_WhenItemExists_RemovesLocalRecord()
{
var service = CreateService();
service.RecordUpload(1024, "https://utfs.io/f/example", "example.zip");

await service.RemoveHistoryItemAsync("https://utfs.io/f/example");

var reloadedService = CreateService();
Assert.Empty(await reloadedService.GetUploadHistoryAsync());
}

/// <summary>
/// Verifies that clearing history deletes every local record immediately.
/// </summary>
/// <returns>A task representing the asynchronous test operation.</returns>
[Fact]
public async Task ClearHistoryAsync_WhenItemsExist_RemovesAllLocalRecords()
{
var service = CreateService();
service.RecordUpload(1024, "https://utfs.io/f/first", "first.zip");
service.RecordUpload(2048, "https://utfs.io/f/second", "second.zip");

await service.ClearHistoryAsync();

var reloadedService = CreateService();
Assert.Empty(await reloadedService.GetUploadHistoryAsync());
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private UploadHistoryService CreateService()
{
var appConfig = new Mock<IAppConfiguration>();
appConfig.Setup(config => config.GetConfiguredDataPath()).Returns(_tempDirectory);

return new UploadHistoryService(
Mock.Of<ILogger<UploadHistoryService>>(),
appConfig.Object);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using GenHub.Features.Tools.Services;
using Microsoft.Extensions.Logging;
using Moq;

namespace GenHub.Tests.Core.Features.Tools.Services;

/// <summary>
/// Tests for the disabled UploadThing integration.
/// </summary>
public class UploadThingServiceTests
{
private readonly UploadThingService _service =
new(Mock.Of<ILogger<UploadThingService>>());

/// <summary>
/// Verifies that uploads fail closed while short-lived credentials are unavailable.
/// </summary>
/// <returns>A task representing the asynchronous test operation.</returns>
[Fact]
public async Task UploadFileAsync_WhenCredentialsAreUnavailable_ReturnsNull()
{
var result = await _service.UploadFileAsync("unused.zip");

Assert.Null(result);
}

/// <summary>
/// Verifies that authenticated deletion also fails closed.
/// </summary>
/// <returns>A task representing the asynchronous test operation.</returns>
[Fact]
public async Task DeleteFileAsync_WhenCredentialsAreUnavailable_ReturnsFalse()
{
var result = await _service.DeleteFileAsync("unused-key");

Assert.False(result);
}
}
Loading
Loading