initial commit

This commit is contained in:
2026-08-04 14:41:21 +03:00
parent 3cb8fcde60
commit 1a1d6186d8
101 changed files with 23239 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
using Microsoft.AspNetCore.Mvc;
using System.Text;
using System.Text.Json;
namespace Library;
public class APIController : AbstractController
{
protected APIController( ILogger logger , IConfiguration configuration )
: base( logger , configuration )
{
}
protected void AssertID( int value )
{
if ( value <= 0 )
throw new APIErrorException( "Invalid id parameter" );
}
protected void AssertParameter( int value , int min_value )
{
if ( value < min_value )
throw new APIErrorException( "Invalid int parameter" );
}
protected void AssertParameter( string? value , int min_length )
{
if ( !ValidateString( value , min_length ) )
throw new APIErrorException( "Invalid string parameter" );
}
protected void AssertURL( string? value , bool mandatory )
{
if ( !ValidateURL( value , mandatory ) )
throw new APIErrorException( "Invalid url parameter" );
}
protected void AssertEmail( string? value , bool mandatory )
{
if ( !ValidateEmail( value , mandatory ) )
throw new APIErrorException( "Invalid email parameter" );
}
protected override void OnInvalidLogin( )
{
throw new APIErrorException( "Access denied." );
}
protected override void OnInvalidAdminLogin( )
{
throw new APIErrorException( "Access denied (admin)." );
}
protected ContentResult Error( string message )
{
using MemoryStream stream = new( );
using Utf8JsonWriter writer = new( stream );
writer.WriteStartObject( );
writer.WriteBoolean( "Success" , false );
writer.WriteString( "ErrorMessage" , message );
writer.WriteEndObject( );
writer.Dispose( );
return new ContentResult( )
{
ContentType = System.Net.Mime.MediaTypeNames.Application.Json ,
Content = Encoding.UTF8.GetString( stream.ToArray( ) )
};
}
protected ContentResult Success( )
{
return Success( "true" );
}
protected ContentResult Success( string data )
{
using MemoryStream stream = new( );
using Utf8JsonWriter writer = new( stream );
writer.WriteStartObject( );
writer.WriteBoolean( "Success" , true );
writer.WriteString( "Result" , data );
writer.WriteEndObject( );
writer.Dispose( );
return new ContentResult( )
{
ContentType = System.Net.Mime.MediaTypeNames.Application.Json ,
Content = Encoding.UTF8.GetString( stream.ToArray( ) )
};
}
}

View File

@@ -0,0 +1,202 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using System.Text.RegularExpressions;
namespace Library;
public abstract class AbstractController : Controller
{
private const string SESSION_COOKIE_NAME = "X-VTU";
private readonly ILogger m_Logger;
private readonly IConfiguration m_Configuration;
private readonly string m_DataDirectory = string.Empty;
protected AbstractController( ILogger logger , IConfiguration configuration )
{
m_Logger = logger;
m_Configuration = configuration;
m_DataDirectory = configuration.GetValue<string>( "DataDirectory" ) ?? string.Empty;
}
protected string GetDataDirectory( )
{
return m_DataDirectory;
}
protected void Trace( string function )
{
#pragma warning disable CA2254 // Template should be a static expression
m_Logger.Log( LogLevel.Trace , function + " called" );
#pragma warning restore CA2254 // Template should be a static expression
}
private SqlConnection? m_DBConnection = null;
protected SqlConnection GetDBConnection( )
{
if ( m_DBConnection == null )
{
try
{
m_DBConnection = new SqlConnection( m_Configuration.GetValue<string>( "ConnectionString" ) );
m_DBConnection.Open( );
HttpContext.Response.RegisterForDispose( m_DBConnection );
}
catch ( SqlException )
{
if ( m_DBConnection != null )
{
m_DBConnection.Dispose( );
m_DBConnection = null;
}
throw;
}
}
return m_DBConnection;
}
protected void SessionStart( string session_id )
{
CookieOptions cookieOptions = new( )
{
Secure = false, // only when HTTPS
HttpOnly = true,
IsEssential = true,
};
Response.Cookies.Append( SESSION_COOKIE_NAME , session_id , cookieOptions );
}
protected void SessionCancel( )
{
Response.Cookies.Delete( SESSION_COOKIE_NAME );
}
protected string? GetSessionKey( )
{
if ( !Request.Cookies.TryGetValue( SESSION_COOKIE_NAME , out string? session_id ) || string.IsNullOrEmpty( session_id ) )
return null;
return session_id;
}
private UserData? m_CurrentUser = null;
private bool m_bUserValidated = false;
protected UserData? GetUserData( )
{
if ( m_bUserValidated )
return m_CurrentUser;
m_bUserValidated = true;
if ( m_CurrentUser != null )
return m_CurrentUser;
string? session_id = GetSessionKey( );
if ( session_id == null )
return null;
m_CurrentUser = UserData.FromSession( GetDBConnection( ) , session_id! );
if ( m_CurrentUser == null )
return null;
HttpContext.SetUserID( m_CurrentUser!.ID );
HttpContext.Items.Add( "CONTEXT_IS_ADMIN" , m_CurrentUser?.IsAdmin ?? false );
return m_CurrentUser;
}
protected SQLBuilder PrepareSQL( string query , string? order_by = null )
{
return new SQLBuilder( GetDBConnection( ) , query , order_by );
}
protected void AssertLogin( string? id )
{
string? session_id = GetSessionKey( );
if ( session_id != null )
{
string? sql =
@"select rec.idgnm, gnm.idlis1, lis.ime, c.*
from gnm_rec rec
left join gnm on gnm.id = rec.idgnm
left join lis on lis.id = gnm.idlis1
left join tbl_cross c on
c.id1 = (select top 1 iddom from livelog where id_session = @session_id)
and c.id2 = gnm.idlis1
where rec.uuid = @id
and c.id is not null";
SqlDataReader reader_x = PrepareSQL( sql )
.AddParameter( "@id" , id )
.AddParameter( "@session_id" , session_id )
.ExecuteReader( );
bool res_x = reader_x.Read( );
reader_x.Close( );
if ( res_x == false )
OnInvalidLogin( );
}
else
{
if ( id == null )
{
if ( GetUserData( ) == null )
OnInvalidLogin( );
}
else
{
if ( GetUserData( ) != null )
return;
SqlDataReader reader = PrepareSQL( @"SELECT gnm.access FROM gnm_rec left join gnm on gnm.id = gnm_rec.idgnm WHERE gnm.access is not null and gnm_rec.uuid = @uuid and gnm_rec.data_format = 'pdf'" )
.AddParameter( "@uuid" , id )
.ExecuteReader( );
bool res = reader.Read( );
reader.Close( );
if ( res == true )
OnInvalidLogin( );
}
}
}
protected void AssertAdmin( )
{
if ( GetUserData( ) == null || GetUserData( )!.IsAdmin == false )
OnInvalidLogin( );
}
protected abstract void OnInvalidLogin( );
protected abstract void OnInvalidAdminLogin( );
protected bool ValidateString( string? parameter , int min_length )
{
int length = parameter?.Length ?? 0;
return length >= min_length;
}
private const string EMAIL_REGEX = @"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$";
protected bool ValidateEmail( string? email , bool required )
{
return ValidateRegex( EMAIL_REGEX , email , required );
}
private const string URL_REGEX = @"(?i)(http(s)?:\/\/)?(\w{2,25}\.)+\w{3}([a-z0-9\-?=$-_.+!*()]+)(?i)";
protected bool ValidateURL( string? email , bool required )
{
return ValidateRegex( URL_REGEX , email , required );
}
protected bool ValidateRegex( string regex , string? value , bool is_required )
{
if ( value == null )
return is_required == false;
try
{
return Regex.IsMatch( value! , regex , RegexOptions.IgnoreCase , TimeSpan.FromMilliseconds( 250 ) );
}
catch ( RegexMatchTimeoutException )
{
return false;
}
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Mvc;
using Library.Models;
namespace Library;
public class PageController : AbstractController
{
protected PageController( ILogger logger , IConfiguration configuration )
: base( logger , configuration )
{
}
protected IActionResult View( BasePageModel page_model )
{
page_model.UserData = GetUserData( );
return base.View( page_model );
}
protected IActionResult View( string view_name , BasePageModel page_model)
{
page_model.UserData = GetUserData( );
return base.View( view_name , page_model );
}
protected override void OnInvalidLogin( )
{
throw new ErrorException( "Access denied." );
}
protected override void OnInvalidAdminLogin( )
{
throw new ErrorException( "Access denied." );
}
protected IActionResult RawText( string text )
{
return new ContentResult( )
{
ContentType = System.Net.Mime.MediaTypeNames.Text.Plain ,
Content = text
};
}
}