2025-01-16 15:13:16 +01:00
using CodexClient ;
2024-10-30 08:34:41 +01:00
using Logging ;
2024-10-16 14:05:02 +02:00
using Utils ;
namespace BiblioTech
{
public class CodexCidChecker
{
private static readonly string nl = Environment . NewLine ;
private readonly Configuration config ;
2024-10-30 08:34:41 +01:00
private readonly ILog log ;
2024-12-03 08:50:50 +01:00
private readonly Mutex checkMutex = new Mutex ( ) ;
2025-01-16 15:13:16 +01:00
private readonly CodexNodeFactory factory ;
private ICodexNode ? currentCodexNode ;
2024-10-16 14:05:02 +02:00
2024-10-30 08:34:41 +01:00
public CodexCidChecker ( Configuration config , ILog log )
2024-10-16 14:05:02 +02:00
{
this . config = config ;
2024-10-30 08:34:41 +01:00
this . log = log ;
2025-01-16 15:13:16 +01:00
factory = new CodexNodeFactory ( log , dataDir : config . DataPath ) ;
if ( ! string . IsNullOrEmpty ( config . CodexEndpointAuth ) & & config . CodexEndpointAuth . Contains ( ":" ) )
{
throw new Exception ( "Todo: codexnodefactory httpfactory support basicauth!" ) ;
//var tokens = config.CodexEndpointAuth.Split(':');
//if (tokens.Length != 2) throw new Exception("Expected '<username>:<password>' in CodexEndpointAuth parameter.");
//client.SetBasicAuthentication(tokens[0], tokens[1]);
}
2024-10-16 14:05:02 +02:00
}
2025-01-16 15:13:16 +01:00
public CheckResponse PerformCheck ( string cid )
2024-10-16 14:05:02 +02:00
{
if ( string . IsNullOrEmpty ( config . CodexEndpoint ) )
{
return new CheckResponse ( false , "Codex CID checker is not (yet) available." , "" ) ;
}
try
{
2024-12-03 08:50:50 +01:00
checkMutex . WaitOne ( ) ;
2024-10-16 14:05:02 +02:00
var codex = GetCodex ( ) ;
2025-01-16 15:13:16 +01:00
var nodeCheck = CheckCodex ( codex ) ;
2024-10-16 14:05:02 +02:00
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." ) ;
2025-01-16 15:13:16 +01:00
return PerformCheck ( codex , cid ) ;
2024-10-16 14:05:02 +02:00
}
catch ( Exception ex )
{
return new CheckResponse ( false , "Internal server error" , ex . ToString ( ) ) ;
2024-12-03 08:50:50 +01:00
}
finally
{
checkMutex . ReleaseMutex ( ) ;
2024-10-16 14:05:02 +02:00
}
}
2025-01-16 15:13:16 +01:00
private CheckResponse PerformCheck ( ICodexNode codex , string cid )
2024-10-16 14:05:02 +02:00
{
try
{
2025-01-16 15:13:16 +01:00
var manifest = codex . DownloadManifestOnly ( new ContentId ( cid ) ) ;
2024-10-16 14:05:02 +02:00
return SuccessMessage ( manifest ) ;
}
catch ( Exception ex )
{
return UnexpectedException ( ex ) ;
}
}
#region Response formatting
2025-01-16 15:13:16 +01:00
private CheckResponse SuccessMessage ( LocalDataset content )
2024-10-16 14:05:02 +02:00
{
return FormatResponse (
success : true ,
title : $"Success: '{content.Cid}'" ,
error : "" ,
2025-01-16 15:13:16 +01:00
$"size: {content.Manifest.OriginalBytes} bytes" ,
2024-10-16 14:05:02 +02: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
2025-01-16 15:13:16 +01:00
private ICodexNode GetCodex ( )
2024-10-16 14:05:02 +02:00
{
if ( currentCodexNode = = null ) currentCodexNode = CreateCodex ( ) ;
return currentCodexNode ;
}
2025-01-16 15:13:16 +01:00
private bool CheckCodex ( ICodexNode node )
2024-10-16 14:05:02 +02:00
{
try
{
2025-01-16 15:13:16 +01:00
var info = node . GetDebugInfo ( ) ;
2024-10-16 14:05:02 +02:00
if ( info = = null | | string . IsNullOrEmpty ( info . Id ) ) return false ;
return true ;
}
catch ( Exception e )
{
2024-10-30 08:34:41 +01:00
log . Error ( e . ToString ( ) ) ;
2024-10-16 14:05:02 +02:00
return false ;
}
}
2025-01-16 15:13:16 +01:00
private ICodexNode CreateCodex ( )
2024-10-16 14:05:02 +02:00
{
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 (
2025-01-15 16:05:57 +01:00
logName : $"cdx@{host}:{port}" ,
2024-10-16 14:05:02 +02:00
host : host ,
port : port
) ;
2025-01-16 15:13:16 +01:00
var instance = CodexInstance . CreateFromApiEndpoint ( "ac" , address ) ;
return factory . CreateCodexNode ( instance ) ;
2024-10-16 14:05:02 +02:00
}
#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 ; }
}
}