v1.3.0 Restructure Saxonite workspace and upgrade to .NET 10 (#1)
Move Saxonite’s projects into a dedicated `Workspace` folder and align the solution with .NET 10 and the current Common workspace. Update encryption integration, test project structure and coverage, and repository guidance and upgrade notes. - Relocate application and library projects while retaining Git rename history. - Split tests into Admin, Online, and Shared projects; add representative behavioral and serialization tests. - Update Common integration, application version, and repository documentation. Application version: 1.3.0 Reviewed-on: #1 Co-authored-by: andy <andy@sarand.com>
This commit was merged in pull request #1.
This commit is contained in:
+407
@@ -0,0 +1,407 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Dbas;
|
||||
using Sarand.Net.Standard.Base.Helpers;
|
||||
using Sarand.Net.Standard.Base.Procs;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Sarand.Net.Standard.Dbas.Data;
|
||||
using Sarand.Net.Standard.Dbas.Proc;
|
||||
using Saxonite.Net.Core.Web.Admin.Models;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Standard.Dbas.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Admin.Controllers.System
|
||||
{
|
||||
//=======================================================================
|
||||
// System - Ajax - Database controller
|
||||
//=======================================================================
|
||||
partial class SystemController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Database data.
|
||||
private class DatabaseTableData
|
||||
{
|
||||
public String SchemaName { get; set; }
|
||||
public String TableName { get; set; }
|
||||
public String QualifiedName { get; set; }
|
||||
public int RowCount { get; set; }
|
||||
public int ColumnCount { get; set; }
|
||||
public int IndexCount { get; set; }
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Database Table List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_DatabaseTableList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_DatabaseTableList(SaxModel_Admin model) : base(model) { }
|
||||
// Table list.
|
||||
public List<DatabaseTableData> Tables { get; } = new List<DatabaseTableData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/GetDatabaseTables")]
|
||||
public IActionResult Ajax_GetDatabaseTables(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetDatabaseTables";
|
||||
String messageTitle = Localiser["system.caption.database"];
|
||||
AjaxResponse_DatabaseTableList responseData = new AjaxResponse_DatabaseTableList(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
||||
{
|
||||
// Database connection.
|
||||
Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseFilter.Instance), Sax.Database.Core);
|
||||
ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance);
|
||||
|
||||
// Database table row counts.
|
||||
Dictionary<String, int> tableRowCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
String query =
|
||||
"SELECT QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) + '.' + QUOTENAME(sOBJ.name) As [TableName], SUM(sPTN.Rows) As [RowCount] " +
|
||||
"FROM sys.objects AS sOBJ " +
|
||||
"INNER JOIN sys.partitions AS sPTN ON sOBJ.object_id = sPTN.object_id " +
|
||||
"WHERE sOBJ.type = 'U' AND sOBJ.is_ms_shipped = 0x0 AND index_id< 2 /*Index Id: 0:Heap,1:Clustered*/ " +
|
||||
"GROUP BY sOBJ.schema_id, sOBJ.name";
|
||||
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query))
|
||||
{
|
||||
while (dbResultSet != null && dbResultSet.NextRecord())
|
||||
{
|
||||
tableRowCounts.Add(dbResultSet.GetRecordValues().GetValue("TableName").ToString(), (int)dbResultSet.GetRecordValues().GetValue("RowCount").AsLong());
|
||||
}
|
||||
}
|
||||
|
||||
// Database tables.
|
||||
List<DbDfnTable> tableDfns = db.GetTableDfns();
|
||||
foreach (DbDfnTable tableDfn in tableDfns.OrderBy(item => item.Name))
|
||||
{
|
||||
// Filter by table name.
|
||||
if (!StringUtil.IsEmpty(model.DatabaseFilter.Search) && !tableDfn.Name.ContainsCI(model.DatabaseFilter.Search))
|
||||
continue;
|
||||
|
||||
DatabaseTableData tableData = new DatabaseTableData();
|
||||
tableData.SchemaName = tableDfn.Schema;
|
||||
tableData.TableName = tableDfn.Name;
|
||||
tableData.QualifiedName = tableDfn.QualifiedName;
|
||||
tableData.ColumnCount = tableDfn.ColumnCount;
|
||||
tableData.IndexCount = tableDfn.IndexCount;
|
||||
|
||||
// Row count.
|
||||
tableData.RowCount = tableRowCounts.GetValueOrDefault(tableDfn.QuotedQualifiedName, 0);
|
||||
|
||||
responseData.Tables.Add(tableData);
|
||||
}
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Validate Database Schema ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class DbSchemaItemResponseData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public String SchemaName { get; set; }
|
||||
public String TableName { get; set; }
|
||||
public String ColumnName { get; set; }
|
||||
public int ColumnPosition { get; set; }
|
||||
public String IndexName { get; set; }
|
||||
public String Comment { get; set; }
|
||||
}
|
||||
private class AjaxResponse_ValidateDatabaseSchema : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_ValidateDatabaseSchema(SaxModel_Admin model) : base(model) { }
|
||||
// Database instance.
|
||||
public String Instance { get; set; }
|
||||
// Schema validation list.
|
||||
public List<DbSchemaItemResponseData> Items { get; } = new List<DbSchemaItemResponseData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/ValidateDatabaseSchema")]
|
||||
public IActionResult Ajax_ValidateDatabaseSchema(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_ValidateDatabaseSchema";
|
||||
String messageTitle = Localiser["system.caption.database"];
|
||||
AjaxResponse_ValidateDatabaseSchema responseData = new AjaxResponse_ValidateDatabaseSchema(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
||||
{
|
||||
|
||||
// Database instance.
|
||||
Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseFilter.Instance), Sax.Database.Unknown);
|
||||
if (databaseInstance <= 0)
|
||||
return Json(responseData.SetPopupMessage(messageTitle, "Database instance must be specified"));
|
||||
responseData.Instance = databaseInstance.ToString();
|
||||
|
||||
// Database connection.
|
||||
ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance);
|
||||
if (db == null)
|
||||
return Json(responseData.SetPopupMessage(messageTitle, "Unrecognised database instance specified: " + model.DatabaseFilter.Instance));
|
||||
|
||||
// Retrieve XML database schema definition.
|
||||
var assembly = typeof(SaxSystem_Dbas).GetTypeInfo().Assembly;
|
||||
String xmlSchema = SacsResourceHelper.GetManifestResourceAsText(assembly, "", "SaxDbDfn_" + databaseInstance.ToString() + ".xml");
|
||||
if (StringUtil.IsEmpty(xmlSchema))
|
||||
return Json(responseData.SetPopupMessage(messageTitle, "Failed to extract XML schema definition."));
|
||||
|
||||
// Extract schema from XML definition.
|
||||
SaDbProcess_ConvertDbSchema.Config cfgSchemaConvert = new SaDbProcess_ConvertDbSchema.Config();
|
||||
SaDbProcess_ConvertDbSchema procSchemaConvert = new SaDbProcess_ConvertDbSchema(cfgSchemaConvert);
|
||||
List<DbDfnTable> tables = procSchemaConvert.ConvertXmlToTables(xmlSchema);
|
||||
if (procSchemaConvert.HasErrorMessage())
|
||||
return Json(responseData.SetPopupMessage(messageTitle, "Failed to convert XML schema: " + procSchemaConvert.GetErrorMessage()));
|
||||
|
||||
SaDbProcess_CompareDatabaseSchemas.Config cfgSchemaCompare = new SaDbProcess_CompareDatabaseSchemas.Config(tables, db);
|
||||
cfgSchemaCompare.HideIdenticalObjects = true;
|
||||
cfgSchemaCompare.IgnorePosition = true;
|
||||
SaDbProcess_CompareDatabaseSchemas procSchemaCompare = new SaDbProcess_CompareDatabaseSchemas(cfgSchemaCompare);
|
||||
procSchemaCompare.OnSchemaComparisonItemAdded += OnSchemaComparisonItemAdded;
|
||||
procSchemaCompare.RunProcess();
|
||||
|
||||
List<SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItem> schemaComparisonItems = procSchemaCompare.GetSchemaComparisonItems();
|
||||
foreach (SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItem schemaComparisonItem in schemaComparisonItems)
|
||||
{
|
||||
DbSchemaItemResponseData item = new DbSchemaItemResponseData()
|
||||
{
|
||||
Id = schemaComparisonItem.Id,
|
||||
SchemaName = schemaComparisonItem.SchemaName,
|
||||
TableName = schemaComparisonItem.TableName,
|
||||
ColumnName = schemaComparisonItem.ColumnName,
|
||||
ColumnPosition = schemaComparisonItem.ColumnPosition,
|
||||
IndexName = schemaComparisonItem.IndexName,
|
||||
Comment = schemaComparisonItem.Comment
|
||||
};
|
||||
responseData.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
// Processing events.
|
||||
public void OnSchemaComparisonItemAdded(object sender, SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItemAddedEventArgs e)
|
||||
{
|
||||
SaDbProcess_CompareDatabaseSchemas proc = sender as SaDbProcess_CompareDatabaseSchemas;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Database Table ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponseData_Table
|
||||
{
|
||||
public AjaxResponseData_Table(DbDfnTable tableDfn)
|
||||
{
|
||||
Schema = tableDfn.Schema;
|
||||
Name = tableDfn.Name;
|
||||
if (!StringUtil.IsEmpty(tableDfn.Remarks))
|
||||
Remarks = tableDfn.Remarks;
|
||||
foreach (DbDfnColumn columnDfn in tableDfn.Columns)
|
||||
Columns.Add(new AjaxResponseData_Column(columnDfn));
|
||||
foreach (DbDfnIndex indexDfn in tableDfn.Indices)
|
||||
Indices.Add(new AjaxResponseData_Index(indexDfn));
|
||||
}
|
||||
public String Schema { get; }
|
||||
public String Name { get; }
|
||||
public String QualifiedName { get { return (!StringUtil.IsEmpty(Schema) ? Schema + "." : "") + Name; } }
|
||||
public String QuotedQualifiedName { get { return (!StringUtil.IsEmpty(Schema) ? "[" + Schema + "]." : "") + "[" + Name + "]"; } }
|
||||
public String Remarks { get; set; } = String.Empty;
|
||||
public List<AjaxResponse_Data.ItemLabelValue> Properties { get; } = new List<AjaxResponse_Data.ItemLabelValue>();
|
||||
public List<AjaxResponseData_Column> Columns { get; } = new List<AjaxResponseData_Column>();
|
||||
public List<AjaxResponseData_Index> Indices { get; } = new List<AjaxResponseData_Index>();
|
||||
}
|
||||
private class AjaxResponseData_Column
|
||||
{
|
||||
public int Pos { get; }
|
||||
public String Name { get; }
|
||||
public String Format { get; }
|
||||
public String DataTypeName { get; }
|
||||
public short? DataTypeNative { get; }
|
||||
public String DataTypeNameNative { get; }
|
||||
public bool? IsIdentity { get; }
|
||||
public long? Size { get; }
|
||||
public long? BufferLen { get; }
|
||||
public short? DecimalDigits { get; }
|
||||
public bool? Nullable { get; }
|
||||
public bool? IsNullable { get; }
|
||||
public String Remarks { get; }
|
||||
public AjaxResponseData_Column(DbDfnColumn columnDfn)
|
||||
{
|
||||
Pos = columnDfn.Pos;
|
||||
Name = columnDfn.Name;
|
||||
Format = columnDfn.GetFormat();
|
||||
if (!StringUtil.IsEmpty(columnDfn.DataTypeName))
|
||||
DataTypeName = columnDfn.DataTypeName;
|
||||
if (columnDfn.DataTypeNative > 0)
|
||||
DataTypeNative = columnDfn.DataTypeNative;
|
||||
if (!StringUtil.IsEmpty(columnDfn.DataTypeNameNative))
|
||||
DataTypeNameNative = columnDfn.DataTypeNameNative;
|
||||
if (columnDfn.IsIdentity)
|
||||
IsIdentity = columnDfn.IsIdentity;
|
||||
if (columnDfn.Size > 0)
|
||||
Size = columnDfn.Size;
|
||||
if (columnDfn.BufferLen > 0)
|
||||
BufferLen = columnDfn.BufferLen;
|
||||
if (columnDfn.DecimalDigits > 0)
|
||||
DecimalDigits = columnDfn.DecimalDigits;
|
||||
if (columnDfn.Nullable == DbColumnNullable.Nullable)
|
||||
Nullable = true;
|
||||
if (columnDfn.IsNullable)
|
||||
IsNullable = columnDfn.IsNullable;
|
||||
if (!StringUtil.IsEmpty(columnDfn.Remarks))
|
||||
Remarks = columnDfn.Remarks;
|
||||
}
|
||||
}
|
||||
private class AjaxResponseData_Index
|
||||
{
|
||||
public String Name { get; }
|
||||
public int? IndexType { get; }
|
||||
public bool? Unique { get; }
|
||||
public bool? Primary { get; }
|
||||
public bool? Clustered { get; }
|
||||
public bool? Nullable { get; }
|
||||
public int? Pages { get; }
|
||||
public int? UniqueValues { get; }
|
||||
public List<AjaxResponseData_IndexColumn> Columns { get; } = new List<AjaxResponseData_IndexColumn>();
|
||||
public AjaxResponseData_Index(DbDfnIndex indexDfn)
|
||||
{
|
||||
Name = indexDfn.Name;
|
||||
if (indexDfn.IndexType > 0)
|
||||
IndexType = indexDfn.IndexType;
|
||||
if (indexDfn.Unique)
|
||||
Unique = indexDfn.Unique;
|
||||
if (indexDfn.Primary)
|
||||
Primary = indexDfn.Primary;
|
||||
if (indexDfn.Clustered)
|
||||
Clustered = indexDfn.Clustered;
|
||||
if (indexDfn.Nullable)
|
||||
Nullable = indexDfn.Nullable;
|
||||
if (indexDfn.Pages > 0)
|
||||
Pages = indexDfn.Pages;
|
||||
if (indexDfn.UniqueValues > 0)
|
||||
UniqueValues = indexDfn.UniqueValues;
|
||||
foreach (DbDfnIndexColumn indexColumnDfn in indexDfn.Columns)
|
||||
Columns.Add(new AjaxResponseData_IndexColumn(indexColumnDfn));
|
||||
}
|
||||
}
|
||||
private class AjaxResponseData_IndexColumn
|
||||
{
|
||||
public short Seq { get; }
|
||||
public String Name { get; }
|
||||
public bool? Desc { get; }
|
||||
public AjaxResponseData_IndexColumn(DbDfnIndexColumn indexColumnDfn)
|
||||
{
|
||||
Seq = indexColumnDfn.Seq;
|
||||
Name = indexColumnDfn.Name;
|
||||
if (!indexColumnDfn.Ascend)
|
||||
Desc = true;
|
||||
}
|
||||
}
|
||||
private class AjaxResponse_DatabaseTable : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_DatabaseTable(SaxModel_Admin model) : base(model) { }
|
||||
// Database table.
|
||||
public String PopupTitle { get; set; }
|
||||
public AjaxResponseData_Table Table { get; set; }
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/GetDatabaseTable")]
|
||||
public IActionResult Ajax_GetDatabaseTable(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetDatabaseTable";
|
||||
String messageTitle = Localiser["system.caption.database"];
|
||||
AjaxResponse_DatabaseTable responseData = new AjaxResponse_DatabaseTable(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Record disposable items as they are created so all can be disposed when needed.
|
||||
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
||||
{
|
||||
|
||||
// Parameters.
|
||||
if (StringUtil.IsEmpty(model.DatabaseTable.Name))
|
||||
return Json(responseData.SetPopupMessage(messageTitle, "Table name must be supplied."));
|
||||
|
||||
// Database instance.
|
||||
Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseTable.Instance), Sax.Database.Unknown);
|
||||
if (databaseInstance <= 0)
|
||||
return Json(responseData.SetPopupMessage(messageTitle, "Database instance must be specified"));
|
||||
|
||||
// Database connection.
|
||||
ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance);
|
||||
|
||||
// Table definition.
|
||||
DbDfnTable tableDfn = db.GetTableDfn(model.DatabaseTable.Name, true);
|
||||
if (tableDfn == null)
|
||||
return Json(responseData.SetPopupMessage(messageTitle, "Invalid Table name supplied."));
|
||||
|
||||
// Populate table response.
|
||||
responseData.Table = new AjaxResponseData_Table(tableDfn);
|
||||
responseData.PopupTitle = Localiser["label.table"] + ": " + tableDfn.Name;
|
||||
|
||||
// Database sizing.
|
||||
String query = "Exec sp_SpaceUsed [" + tableDfn.QualifiedName + "];";
|
||||
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query))
|
||||
{
|
||||
if (dbResultSet != null && dbResultSet.NextRecord())
|
||||
{
|
||||
// Row count.
|
||||
long rows = StringUtil.ToLong(dbResultSet.GetRecordValues().GetValue("rows").ToString());
|
||||
if (rows > 0)
|
||||
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Rows", Value = rows.ToString() });
|
||||
// Reserved space.
|
||||
String reserved = dbResultSet.GetRecordValues().GetValue("reserved").AsString();
|
||||
if (!StringUtil.IsEmpty(reserved))
|
||||
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Reserved", Value = reserved });
|
||||
// Data space.
|
||||
String data = dbResultSet.GetRecordValues().GetValue("data").AsString();
|
||||
if (!StringUtil.IsEmpty(data))
|
||||
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Data", Value = data });
|
||||
// Index space.
|
||||
String index = dbResultSet.GetRecordValues().GetValue("index_size").AsString();
|
||||
if (!StringUtil.IsEmpty(index))
|
||||
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Indices", Value = index });
|
||||
// Unused space.
|
||||
String unused = dbResultSet.GetRecordValues().GetValue("unused").AsString();
|
||||
if (!StringUtil.IsEmpty(unused))
|
||||
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Unused", Value = unused });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Dbas;
|
||||
using Sarand.Net.Standard.Base.IO;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Saxonite.Net.Core.Web.Admin.Models;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Admin.Controllers.System
|
||||
{
|
||||
//=======================================================================
|
||||
// System - Ajax - Logs controller
|
||||
//=======================================================================
|
||||
partial class SystemController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Database data.
|
||||
private class LogFolderData
|
||||
{
|
||||
public String Name { get; set; }
|
||||
}
|
||||
private class LogFileData
|
||||
{
|
||||
public String Name { get; set; }
|
||||
public String UpdateFmt { get; set; }
|
||||
public long Size { get; set; }
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Log Folder List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_LogFolderList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_LogFolderList(SaxModel_Admin model) : base(model) { }
|
||||
// Log file list.
|
||||
public List<LogFolderData> Folders { get; } = new List<LogFolderData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Retrieve the list of folders under the root folder.
|
||||
/// <para>- These should correspond to applications (admin, api, online etc).</para>
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/Logs/GetFolderList")]
|
||||
[Route("System/Ajax/Logs/GetFolders")]
|
||||
public IActionResult Ajax_GetLogFolderList(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetLogFolderList";
|
||||
String messageTitle = Localiser["system.caption.logs"];
|
||||
AjaxResponse_LogFolderList responseData = new AjaxResponse_LogFolderList(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Identify folders in root log folder.
|
||||
String rootLogFolder = model.LoggingRootFolder;
|
||||
foreach (String folder in Directory.EnumerateDirectories(rootLogFolder))
|
||||
{
|
||||
String folderName = Path.GetFileName(folder);
|
||||
if (folderName.IsMatch("^(Online|Admin|Api)$", RegexOptions.IgnoreCase))
|
||||
responseData.Folders.Add(new LogFolderData() { Name = folderName });
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Log Year Folder List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_LogYearList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_LogYearList(SaxModel_Admin model) : base(model) { }
|
||||
// Log file list.
|
||||
public List<LogFolderData> Years { get; } = new List<LogFolderData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Retrieve the list of year folders under the selected folder.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/Logs/GetYearList")]
|
||||
[Route("System/Ajax/Logs/GetYears")]
|
||||
public IActionResult Ajax_GetLogYearList(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetLogYearList";
|
||||
String messageTitle = Localiser["system.caption.logs"];
|
||||
AjaxResponse_LogYearList responseData = new AjaxResponse_LogYearList(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Store filter properties to session.
|
||||
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
|
||||
SessionData_Admin.System.Logs.Filter.Year = "";
|
||||
SessionData_Admin.System.Logs.Filter.Month = "";
|
||||
|
||||
// Identify folders in selected log folder.
|
||||
String logFolder = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder);
|
||||
foreach (String folder in Directory.EnumerateDirectories(logFolder))
|
||||
{
|
||||
String folderName = Path.GetFileName(folder);
|
||||
if (folderName.IsMatch("^\\d{4}$"))
|
||||
responseData.Years.Add(new LogFolderData() { Name = folderName });
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Log Month Folder List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_LogMonthList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_LogMonthList(SaxModel_Admin model) : base(model) { }
|
||||
// Log file list.
|
||||
public List<LogFolderData> Months { get; } = new List<LogFolderData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Retrieve the list of year folders under the selected folder.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/Logs/GetMonthList")]
|
||||
[Route("System/Ajax/Logs/GetMonths")]
|
||||
public IActionResult Ajax_GetLogMonthList(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetLogMonthList";
|
||||
String messageTitle = Localiser["system.caption.logs"];
|
||||
AjaxResponse_LogMonthList responseData = new AjaxResponse_LogMonthList(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Store filter properties to session.
|
||||
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
|
||||
SessionData_Admin.System.Logs.Filter.Year = model.LogsFilter.Year;
|
||||
SessionData_Admin.System.Logs.Filter.Month = "";
|
||||
//SessionData_Admin.System.Logs.Filter.Month = model.LogsFilter.Month;
|
||||
|
||||
// Identify folders in selected year folder.
|
||||
String logFolder = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder, model.LogsFilter.Year);
|
||||
foreach (String folder in Directory.EnumerateDirectories(logFolder))
|
||||
{
|
||||
String folderName = Path.GetFileName(folder);
|
||||
if (folderName.IsMatch("^\\d{6}$"))
|
||||
responseData.Months.Add(new LogFolderData() { Name = folderName });
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Log File List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_LogFileList : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_LogFileList(SaxModel_Admin model) : base(model) { }
|
||||
// Log file list.
|
||||
public List<LogFileData> Files { get; } = new List<LogFileData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/Logs/GetFileList")]
|
||||
[Route("System/Ajax/Logs/GetFiles")]
|
||||
public IActionResult Ajax_GetLogFileList(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetLogFileList";
|
||||
String messageTitle = Localiser["system.caption.logs"];
|
||||
AjaxResponse_LogFileList responseData = new AjaxResponse_LogFileList(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Store filter properties to session.
|
||||
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
|
||||
SessionData_Admin.System.Logs.Filter.Year = model.LogsFilter.Year;
|
||||
SessionData_Admin.System.Logs.Filter.Month = model.LogsFilter.Month;
|
||||
|
||||
// Identify folders in selected year folder.
|
||||
String logFolder = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder, model.LogsFilter.Year, model.LogsFilter.Month);
|
||||
DirectoryInfo folder = new DirectoryInfo(logFolder);
|
||||
foreach (FileInfo file in folder.EnumerateFiles())
|
||||
{
|
||||
responseData.Files.Add(new LogFileData() { Name = file.Name, UpdateFmt = file.LastWriteTime.ToString("d-MMM-yy HH:mm"), Size = file.Length });
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Log File ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
private class AjaxResponse_LogFile : AjaxResponse_Data
|
||||
{
|
||||
// Construction.
|
||||
public AjaxResponse_LogFile(SaxModel_Admin model) : base(model) { }
|
||||
// Log file.
|
||||
public class LogFileData
|
||||
{
|
||||
public String Path { get; set; }
|
||||
public String Name { get; set; }
|
||||
public String Data { get; set; }
|
||||
public String Html { get; set; } = "";
|
||||
}
|
||||
public LogFileData File { get; } = new LogFileData();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("System/Ajax/Logs/GetFile")]
|
||||
[Route("System/Ajax/Logs/GetLogFile")]
|
||||
public IActionResult Ajax_GetLogFile(Model_System model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetLogFile";
|
||||
String messageTitle = Localiser["system.caption.logs"];
|
||||
AjaxResponse_LogFile responseData = new AjaxResponse_LogFile(model);
|
||||
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Store filter properties to session.
|
||||
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
|
||||
SessionData_Admin.System.Logs.Filter.Year = model.LogsFilter.Year;
|
||||
SessionData_Admin.System.Logs.Filter.Month = model.LogsFilter.Month;
|
||||
|
||||
// Identify folders in selected year folder.
|
||||
responseData.File.Path = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder, model.LogsFilter.Year, model.LogsFilter.Month, model.LogsFilter.Filename);
|
||||
responseData.File.Name = Path.GetFileName(responseData.File.Path);
|
||||
try
|
||||
{
|
||||
String[] fileLines = FileUtil.ReadAllFileLines(responseData.File.Path);
|
||||
responseData.File.Data = String.Join("\n", fileLines);
|
||||
// Construct html data.
|
||||
foreach (var fileLine in fileLines)
|
||||
{
|
||||
String line = fileLine;
|
||||
List<String> classNames = new List<string>();
|
||||
if (fileLine.IsMatch("^\t{2}"))
|
||||
{
|
||||
line = Regex.Replace(fileLine, "^\t+", "");
|
||||
classNames.Add("tab2");
|
||||
}
|
||||
if (fileLine.IsMatch("^\t"))
|
||||
{
|
||||
line = Regex.Replace(fileLine, "^\t", "");
|
||||
classNames.Add("tab");
|
||||
}
|
||||
if (line.IsMatch("\\d{8}:\\d{6}\\s+(error|err)\\s+", RegexOptions.IgnoreCase))
|
||||
classNames.Add("err");
|
||||
else if (line.IsMatch("\\d{8}:\\d{6}\\s+(warn|wrn)\\s+", RegexOptions.IgnoreCase))
|
||||
classNames.Add("wrn");
|
||||
else if (line.IsMatch("\\d{8}:\\d{6}\\s+(info|inf)\\s+", RegexOptions.IgnoreCase))
|
||||
classNames.Add("inf");
|
||||
responseData.File.Html += $"<div class='{String.Join(" ", classNames)}'>" + line + "</div>";
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc, "reading log file", 0)}");
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Saxonite.Net.Core.Web.Admin.Models;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.Controllers;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Admin.Controllers.System
|
||||
{
|
||||
//=======================================================================
|
||||
public partial class SystemController : SaxController_Admin
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public SystemController(SaxLocaliser localiser, ILogger<HomeController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// System - Home.
|
||||
/// <para>- No 'System' home page for now, redirect to home page.</para>
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return RedirectToAction("", "");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// System - Database page.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult Database(Model_System model)
|
||||
{
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
AutoLoginUser();
|
||||
if (!ValidUserSession)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
model.AdditionalFiles_JS.Add("js/sax-sys-db.js");
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// System - Logs page.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult Logs(Model_System model)
|
||||
{
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
AutoLoginUser();
|
||||
if (!ValidUserSession)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
model.AdditionalFiles_JS.Add("js/sax-sys-logs.js");
|
||||
model.LogsFilter.Folder = StringUtil.OnBlank(SessionData_Admin.System.Logs.Filter.Folder, "Admin");
|
||||
model.LogsFilter.Year = StringUtil.OnBlank(SessionData_Admin.System.Logs.Filter.Year, DateTime.Today.ToString("yyyy"));
|
||||
model.LogsFilter.Month = StringUtil.OnBlank(SessionData_Admin.System.Logs.Filter.Month, DateTime.Today.ToString("yyyyMM"));
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
Reference in New Issue
Block a user