2024-10-16 12:05:02 +00:00
using CodexOpenApi ;
using IdentityModel.Client ;
2024-10-30 07:34:41 +00:00
using Logging ;
2024-10-16 12:05:02 +00:00
using Utils ;
namespace BiblioTech
{
public class CodexCidChecker
{
private static readonly string nl = Environment . NewLine ;
private readonly Configuration config ;
2024-10-30 07:34:41 +00:00
private readonly ILog log ;
2024-10-16 12:05:02 +00:00
private CodexApi ? currentCodexNode ;
2024-10-30 07:34:41 +00:00
public CodexCidChecker ( Configuration config , ILog log )
2024-10-16 12:05:02 +00:00
{
this . config = config ;
2024-10-30 07:34:41 +00:00
this . log = log ;
2024-10-16 12:05:02 +00:00
}
public async Task < CheckResponse > PerformCheck ( string cid )
{
if ( string . IsNullOrEmpty ( config . CodexEndpoint ) )
{
return new CheckResponse ( false , "Codex CID checker is not (yet) available." , "" ) ;
}
try
{
var codex = GetCodex ( ) ;
var nodeCheck = await CheckCodex ( codex ) ;
if ( ! nodeCheck ) return new CheckResponse ( false , "Codex node is not available. Cannot perform check." , $"Codex node at '{config.CodexEndpoint}' did not respond correctly to debug/info." ) ;
return await PerformCheck ( codex , cid ) ;
}
catch ( Exception ex )
{
return new CheckResponse ( false , "Internal server error" , ex . ToString ( ) ) ;
}
}
private async Task < CheckResponse > PerformCheck ( CodexApi codex , string cid )
{
try
{
var manifest = await codex . DownloadNetworkManifestAsync ( cid ) ;
return SuccessMessage ( manifest ) ;
}
catch ( ApiException apiEx )
{
if ( apiEx . StatusCode = = 400 ) return CidFormatInvalid ( apiEx . Response ) ;
if ( apiEx . StatusCode = = 404 ) return FailedToFetch ( apiEx . Response ) ;
return UnexpectedReturnCode ( apiEx . Response ) ;
}
catch ( Exception ex )
{
return UnexpectedException ( ex ) ;
}
}
#region Response formatting
private CheckResponse SuccessMessage ( DataItem content )
{
return FormatResponse (
success : true ,
title : $"Success: '{content.Cid}'" ,
error : "" ,
2024-11-21 08:39:28 +00:00
$"size: {content.Manifest.DatasetSize} bytes" ,
2024-10-16 12:05:02 +00:00
$"blockSize: {content.Manifest.BlockSize} bytes" ,
$"protected: {content.Manifest.Protected}"
) ;
}
private CheckResponse UnexpectedException ( Exception ex )
{
return FormatResponse (
success : false ,
title : "Unexpected error" ,
error : ex . ToString ( ) ,
content : "Details will be sent to the bot-admin channel."
) ;
}
private CheckResponse UnexpectedReturnCode ( string response )
{
var msg = "Unexpected return code. Response: " + response ;
return FormatResponse (
success : false ,
title : "Unexpected return code" ,
error : msg ,
content : msg
) ;
}
private CheckResponse FailedToFetch ( string response )
{
var msg = "Failed to download content. Response: " + response ;
return FormatResponse (
success : false ,
title : "Could not download content" ,
error : msg ,
msg ,
$"Connection trouble? See 'https://docs.codex.storage/learn/troubleshoot'"
) ;
}
private CheckResponse CidFormatInvalid ( string response )
{
return FormatResponse (
success : false ,
title : "Invalid format" ,
error : "" ,
content : "Provided CID is not formatted correctly."
) ;
}
private CheckResponse FormatResponse ( bool success , string title , string error , params string [ ] content )
{
var msg = string . Join ( nl ,
new string [ ]
{
title ,
"```"
}
. Concat ( content )
. Concat ( new string [ ]
{
"```"
} )
) + nl + nl ;
return new CheckResponse ( success , msg , error ) ;
}
#endregion
#region Codex Node API
private CodexApi GetCodex ( )
{
if ( currentCodexNode = = null ) currentCodexNode = CreateCodex ( ) ;
return currentCodexNode ;
}
private async Task < bool > CheckCodex ( CodexApi codex )
{
try
{
var info = await currentCodexNode ! . GetDebugInfoAsync ( ) ;
if ( info = = null | | string . IsNullOrEmpty ( info . Id ) ) return false ;
return true ;
}
catch ( Exception e )
{
2024-10-30 07:34:41 +00:00
log . Error ( e . ToString ( ) ) ;
2024-10-16 12:05:02 +00:00
return false ;
}
}
private CodexApi CreateCodex ( )
{
var endpoint = config . CodexEndpoint ;
var splitIndex = endpoint . LastIndexOf ( ':' ) ;
var host = endpoint . Substring ( 0 , splitIndex ) ;
var port = Convert . ToInt32 ( endpoint . Substring ( splitIndex + 1 ) ) ;
var address = new Address (
host : host ,
port : port
) ;
var client = new HttpClient ( ) ;
if ( ! string . IsNullOrEmpty ( config . CodexEndpointAuth ) & & config . CodexEndpointAuth . Contains ( ":" ) )
{
var tokens = config . CodexEndpointAuth . Split ( ':' ) ;
if ( tokens . Length ! = 2 ) throw new Exception ( "Expected '<username>:<password>' in CodexEndpointAuth parameter." ) ;
client . SetBasicAuthentication ( tokens [ 0 ] , tokens [ 1 ] ) ;
}
var codex = new CodexApi ( client ) ;
codex . BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1" ;
return codex ;
}
#endregion
}
public class CheckResponse
{
public CheckResponse ( bool success , string message , string error )
{
Success = success ;
Message = message ;
Error = error ;
}
public bool Success { get ; }
public string Message { get ; }
public string Error { get ; }
}
}