update TWA Management v0.0.1

This commit is contained in:
2025-11-05 10:08:06 +07:00
parent d4e91c7960
commit b4b191f829
73 changed files with 2249 additions and 418 deletions

View File

@ -10,13 +10,14 @@ using System.Threading.Tasks;
namespace AppLibs.Libs namespace AppLibs.Libs
{ {
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class AsyncGateAttribute : Attribute, IAsyncActionFilter public sealed class AsyncGateAttribute : Attribute, IAsyncActionFilter, IOrderedFilter
{ {
public int Order { get; set; } = 0;
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{ {
if (!HttpMethods.IsGet(context.HttpContext.Request.Method)) if (!HttpMethods.IsGet(context.HttpContext.Request.Method))
{ {
// Nếu không phải GET, cho qua luôn, không cần kiểm tra gì thêm
await next(); await next();
return; return;
} }

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace AppLibs.Libs.Crypt
{
public class UUID7
{
public static Guid NewUuid7()
{
// Unix time in milliseconds (48 bits)
long ms = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
Span<byte> b = stackalloc byte[16];
// put timestamp big-endian into first 6 bytes
b[0] = (byte)((ms >> 40) & 0xFF);
b[1] = (byte)((ms >> 32) & 0xFF);
b[2] = (byte)((ms >> 24) & 0xFF);
b[3] = (byte)((ms >> 16) & 0xFF);
b[4] = (byte)((ms >> 8) & 0xFF);
b[5] = (byte)(ms & 0xFF);
// 10 bytes ngẫu nhiên
Span<byte> rnd = stackalloc byte[10];
RandomNumberGenerator.Fill(rnd);
// version = 0b0111 (UUIDv7)
b[6] = (byte)((rnd[0] & 0x0F) | 0x70);
b[7] = rnd[1];
// variant (RFC 4122) 10xx
b[8] = (byte)((rnd[2] & 0x3F) | 0x80);
b[9] = rnd[3];
b[10] = rnd[4];
b[11] = rnd[5];
b[12] = rnd[6];
b[13] = rnd[7];
b[14] = rnd[8];
b[15] = rnd[9];
// Guid constructor expects specific byte order; dùng Guid(byte[]) là OK để lưu binary16
return new Guid(b);
}
}
}

View File

@ -5,8 +5,10 @@ namespace TWA_App.Controllers
{ {
public class ContactController : Controller public class ContactController : Controller
{ {
[PageInfor("6000", "Contact")]
public async Task<IActionResult> Index() public async Task<IActionResult> Index()
{ {
ViewData["PageID"] = "6000";
return await this.ViewAsync(); return await this.ViewAsync();
} }
} }

View File

@ -1,10 +1,22 @@
using AppLibs.Libs; using AppLibs.Libs;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Text.RegularExpressions;
using TWA_App.Models;
using TWA_App.Models.IServices;
namespace TWA_App.Controllers namespace TWA_App.Controllers
{ {
public class ServicesController : Controller public class ServicesController : Controller
{ {
private readonly IDataService<Service> iDataService;
public ServicesController(IDataService<Service> data)
{
iDataService = data;
}
[PageInfor("3000", "News A", "/Services/*")] [PageInfor("3000", "News A", "/Services/*")]
[Layout(LayoutOptions.Default)] [Layout(LayoutOptions.Default)]
public async Task<IActionResult> Index(string? id) public async Task<IActionResult> Index(string? id)
@ -19,6 +31,13 @@ namespace TWA_App.Controllers
{ {
ViewData["PageID"] = result.ID; ViewData["PageID"] = result.ID;
ViewData["Title"] = result.Name; ViewData["Title"] = result.Name;
Service s = await iDataService.GetDataCodeAsync(int.Parse(result.ID), u => u.Id);
var htmlContent = Regex.Replace(s.Content, @"~\/([^\s""'>]+)", match =>
{
var relativePath = match.Groups[1].Value; // ví dụ: "images/logo.png"
return Url.AbsoluteContent(relativePath);
});
ViewData["Content"] = htmlContent;
int pageIndex = list.SubItem.IndexOf(result); int pageIndex = list.SubItem.IndexOf(result);
for (int i = pageIndex - 1; i >= 0; i--) for (int i = pageIndex - 1; i >= 0; i--)
{ {

View File

@ -65,7 +65,7 @@
"url": "", "url": "",
"sub-item": [ "sub-item": [
{ {
"name": "Air Cargo", "name": "Cargo & Passenger GSSA",
"id": "G3001", "id": "G3001",
"group": 1 "group": 1
}, },
@ -78,50 +78,25 @@
}, },
{ {
"id": "3002", "id": "3002",
"name": "Aircraft Charter",
"url": "/Services/Aircraft-Charter",
"flexpage": true
},
{
"id": "3003",
"name": "Cargo Tracking",
"url": "/Services/Cargo-Tracking",
"flexpage": true
},
{
"id": "3004",
"name": "Groupage",
"url": "/Services/Groupage",
"flexpage": true
},
{
"name": "Air Passenger",
"id": "G3008",
"group": 1
},
{
"id": "3008",
"name": "Passenger GSSA", "name": "Passenger GSSA",
"url": "/Services/Passenger-GSSA", "url": "/Services/Passenger-GSSA",
"flexpage": true "flexpage": true
}, },
{
"name": "Aircraft Charter",
"id": "G3008",
"group": 1
},
{
"id": "3008",
"name": "Cargo Charter Service",
"url": "/Services/Cargo-Charter-Service",
"flexpage": true
},
{ {
"id": "3006", "id": "3006",
"name": "Ticketing & Reservation", "name": "Passenger Charter Service",
"url": "/Services/Ticketing-Reservation", "url": "/Services/Passenger-Charter-Service",
"flexpage": true
},
{
"id": "3007",
"name": "Check-in Services",
"url": "/Services/Check-In-Services",
"flexpage": true
},
{
"id": "3009",
"name": "Customer Service & Support",
"url": "/Services/Customer-Service-Support",
"flexpage": true "flexpage": true
}, },
{ {
@ -131,8 +106,8 @@
}, },
{ {
"id": "3010", "id": "3010",
"name": "Aviation Support", "name": "Logistics Service",
"url": "/Services/Aviation-Support", "url": "/Services/Logistics-Service",
"flexpage": true "flexpage": true
}, },
{ {
@ -140,6 +115,29 @@
"name": "Courier Service", "name": "Courier Service",
"url": "/Services/Courier-Service", "url": "/Services/Courier-Service",
"flexpage": true "flexpage": true
},
{
"name": "Aviation Support",
"id": "G3011",
"group": 1
},
{
"id": "3012",
"name": "Operating",
"url": "/Services/Operating",
"flexpage": true
},
{
"id": "3013",
"name": "Warehousing",
"url": "/Services/Warehousing",
"flexpage": true
},
{
"id": "3014",
"name": "Flight Support",
"url": "/Services/Flight-Support",
"flexpage": true
} }
] ]
}, },

File diff suppressed because one or more lines are too long

33
TWA-App/Models/Service.cs Normal file
View File

@ -0,0 +1,33 @@
using Newtonsoft.Json;
namespace TWA_App.Models
{
public class Service
{
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "content-page")]
public string Content { get; set; }
[JsonProperty(PropertyName = "lang")]
public string Lang { get; set; }
[JsonProperty(PropertyName = "scripts")]
public List<string> Scripts { get; set; }
[JsonProperty(PropertyName = "styles")]
public List<string> Styles { get; set; }
public Service()
{
Id = 0;
Content = "";
Scripts = new List<string>();
Styles = new List<string>();
Lang = "";
}
}
}

View File

@ -16,6 +16,10 @@ builder.Services.AddSingleton<IDataService<News>>(sp =>
{ {
return new DataService<News>("Json/news.json"); return new DataService<News>("Json/news.json");
}); });
builder.Services.AddSingleton<IDataService<Service>>(sp =>
{
return new DataService<Service>("Json/servicesData.json");
});
// Add services to the container. // Add services to the container.
builder.Services.AddControllers(options => builder.Services.AddControllers(options =>
{ {

View File

@ -9,17 +9,17 @@
<ItemGroup> <ItemGroup>
<Compile Remove="wwwroot\css\pages\**" /> <Compile Remove="wwwroot\css\pages\**" />
<Compile Remove="wwwroot\images\NewFolder\**" />
<Content Remove="wwwroot\css\pages\**" /> <Content Remove="wwwroot\css\pages\**" />
<Content Remove="wwwroot\images\NewFolder\**" />
<EmbeddedResource Remove="wwwroot\css\pages\**" /> <EmbeddedResource Remove="wwwroot\css\pages\**" />
<EmbeddedResource Remove="wwwroot\images\NewFolder\**" />
<None Remove="wwwroot\css\pages\**" /> <None Remove="wwwroot\css\pages\**" />
<None Remove="wwwroot\images\NewFolder\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.7" /> <Content Remove="wwwroot\css\atg-lib\atg-core.css" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.10" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
@ -74,9 +74,14 @@
<ItemGroup> <ItemGroup>
<Folder Include="wwwroot\images\3000\NewFolder\" /> <Folder Include="wwwroot\images\3000\NewFolder\" />
<Folder Include="wwwroot\images\6000\" />
<Folder Include="wwwroot\images\wait-section\" /> <Folder Include="wwwroot\images\wait-section\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="wwwroot\css\atg-lib\atg-core.css" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Update="Json\airpartner.json"> <Content Update="Json\airpartner.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>

View File

@ -1,3 +1,198 @@
@section jsLib { @inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment Env
<section class="con-parallax con-page-section">
<div class="bg-wrapper parallax" data-dept="0.1">
<img src="https://morz.vamtam.com/wp-content/uploads/2018/01/Shop-2.jpg" alt="Background" class="bg-img">
</div>
<div class="con section page-section section-break" data-content>
<div class="r">
<h1 class="title">Global Company - @ViewData["Title"]</h1>
</div>
</div>
</section>
<section class="section section-contact">
<div class="con">
<div class="r">
<div class="r-n-g">
<div class="c-12 c-m-6 c-l-4 p-20">
<div class="d-f f-c">
<h3 class="mb-4">Branches Offices</h3>
<div class="form-group mb-4">
<select class="aselect">
<option>Vietnam</option>
<option>Cambodia</option>
<option>Myanmar</option>
</select>
</div>
<div class="d-f f-c according mb-5">
<div class="d-f f-c con-item" data-dropdown>
<a class="item active">
Sai Gon - Headquarter
</a>
<div class="sub-item show">
<div class="d-f f-c">
<div class="contact-form d-f f-c mb-4">
<span class="hd1">Call Us</span>
<span class="hd2">or fill out the form below</span>
</div>
<div class="d-f contact-group a-i-center">
<i class="atg a-2x atg-phone"></i>
<div class="d-f f-c">
<span class="title"> + (84) 1900-777-888</span>
<p> Don't hesitate to contact us!</p>
</div>
</div>
<div class="d-f contact-group a-i-center">
<i class="atg a-2x atg-calendar"></i>
<div class="d-f f-c">
<span class="title">Working time</span>
<p>Mon - Fri: 9:00 - 19:00 / Closed on Weekend</p>
</div>
</div>
<div class="d-f contact-group a-i-center">
<i class="atg a-2x atg-location"></i>
<div class="d-f f-c">
<span class="title">Company Headquarters</span>
<p>39B Truong Son Street, Tan Son Nhat Ward, Ho Chi Minh City</p>
</div>
</div>
</div>
</div>
</div>
<div class="d-f f-c con-item" data-dropdown>
<a class="item">
Ha Noi - Branch
</a>
<div class="sub-item">
<div class="d-f f-c">
<p class="desc">
The B13A Form is required by the Canadian Border Services Agency (CBSA) to enforce the necessary export controls in Canada.
</p>
<p class="desc">
The form must be prepared and signed by a Canadian shipper (exporter) or their forwarding agent. The shipment description, value, weight, destination, and other basic information must be indicated for processing use by Customs. B13A information may be submitted electronically, by mail, or by fax. For more information, please review Info Source, published by the Treasury Board of Canada, or contact us directly.
</p>
<p class="desc">
The CBSA is directly responsible for any updates to this form. Please see our external resources section.
</p>
<div class="d-f mt-5 mb-3">
<button class="btn btn-primary">Download Document</button>
</div>
</div>
</div>
</div>
<div class="d-f f-c con-item" data-dropdown>
<a class="item">
Da Nang - Branch
</a>
<div class="sub-item">
<div class="d-f f-c">
<p class="desc">
The B13A Form is required by the Canadian Border Services Agency (CBSA) to enforce the necessary export controls in Canada.
</p>
<p class="desc">
The form must be prepared and signed by a Canadian shipper (exporter) or their forwarding agent. The shipment description, value, weight, destination, and other basic information must be indicated for processing use by Customs. B13A information may be submitted electronically, by mail, or by fax. For more information, please review Info Source, published by the Treasury Board of Canada, or contact us directly.
</p>
<p class="desc">
The CBSA is directly responsible for any updates to this form. Please see our external resources section.
</p>
<div class="d-f mt-5 mb-3">
<button class="btn btn-primary">Download Document</button>
</div>
</div>
</div>
</div>
<div class="d-f f-c con-item" data-dropdown>
<a class="item">
Cam Ranh - Branch
</a>
<div class="sub-item">
<div class="d-f f-c">
<p class="desc">
The B13A Form is required by the Canadian Border Services Agency (CBSA) to enforce the necessary export controls in Canada.
</p>
<p class="desc">
The form must be prepared and signed by a Canadian shipper (exporter) or their forwarding agent. The shipment description, value, weight, destination, and other basic information must be indicated for processing use by Customs. B13A information may be submitted electronically, by mail, or by fax. For more information, please review Info Source, published by the Treasury Board of Canada, or contact us directly.
</p>
<p class="desc">
The CBSA is directly responsible for any updates to this form. Please see our external resources section.
</p>
<div class="d-f mt-5 mb-3">
<button class="btn btn-primary">Download Document</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="c-12 c-m-6 c-l-8 p-20">
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3919.0292579178767!2d106.66165657591787!3d10.809070989341738!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x317529234921d913%3A0xc54a32ee61fd7597!2sTrans%20World%20Aviation%20(TWA)!5e0!3m2!1svi!2s!4v1761199673102!5m2!1svi!2s" width="100%" height="750" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
</div>
<div class="r-n-g mt-4 a-i-center">
<div class="c-12 c-m-6 c-l-5 p-20 o-2 o-m-1">
<div class="con-image mb-4">
<img class="w-100" src="~/images/6000/contact-form.png" />
</div>
</div>
<div class="c-12 c-m-6 c-l-7 p-20 o-1 o-m-2">
<div class="cfull">
<div class="r-n-g">
<div class="c-12">
<div class="d-f mb-4">
<h2>Contact With Us</h2>
</div>
</div>
<div class="form-group f-c c-12 c-m-6">
<label>Name</label>
<input class="form-control" type="text" id="ip_name" placeholder="Name">
</div>
<div class="form-group f-c c-12 c-m-6">
<label>Company</label>
<input class="form-control" type="text" id="ip_company" placeholder="Company">
</div>
<div class="form-group f-c c-12 c-m-6">
<label>Email Address</label>
<input class="form-control" type="email" name="ip_email" placeholder="Email Address">
</div>
<div class="form-group f-c f-c c-12 c-m-6">
<label>Phone</label>
<input class="form-control" type="tel" id="ip_phone" placeholder="Phone">
</div>
<div class="form-group f-c c-12">
<label>Content</label>
<textarea class="form-group" placeholder="How can we help?" rows="6">
</textarea>
</div>
</div>
<div class="r-n-g">
<div class="form-group c-12 d-f a-i-center">
<label class="mb-0">Additional Notes: </label>
<input type="radio" id="rd_PC" class="ml-4 mr-2" name="rdCustomer">
<label class="mb-0" for="rd_PC">Personal</label>
<input type="radio" id="rd_BC" class="ml-4 mr-2" name="rdCustomer">
<label class="mb-0" for="rd_BC">Bussiness</label>
</div>
<div class="c-12">
<button class="mt-2 mb-3 btn btn-primary" type="submit">Submit</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
@section jsLib {
<script type="module" src="@Url.AbsoluteContent("~/js/js-page/6000.js")" js-lib></script> <script type="module" src="@Url.AbsoluteContent("~/js/js-page/6000.js")" js-lib></script>
} }

View File

@ -21,99 +21,13 @@
</div> </div>
</div> </div>
<div class="c-12 c-l-8 c-x-9 p-20"> <div class="c-12 c-l-8 c-x-9 p-20">
<div class="d-f f-c">
<p class="sub-title">
We Are Air Cargo GSSA
</p>
<h2 class="title"> If Youve Ever Heard of A Cargo GSSA Before, This Is How It Works</h2>
<img class="w-100" src="~/images/3000/3001/gssa-overview.png" alt="Air Cargo GSSA illustration" />
</div>
<div class="r-n-g">
<div class="c-12 c-x-6 c-xm-4 p-20">
<div class="d-f">
<h3 class="mb-4 title underline">Being a Cargo GSSA/GSA means</h3>
</div>
<ul class="order-list mb-4">
<li class="d-f a-i-start">
<i class="atg atg-radiobutton a-2x"></i>
General Sales & Service Agents (GSSAs)/General Sales Agents (GSAs) represent airlines commercializing air freight capacity and supervise complex local cargo operations and administration and financial services for a assigned specific territory or region
</li>
<li class="d-f a-i-start mt-4">
<i class="atg atg-radiobutton a-2x"></i>
Represent for Airlines typically work with freight forwarders and handling agents (GHAs) to deliver transportation services to shippers.
</li>
<li class="d-f a-i-start mt-4">
<i class="atg atg-radiobutton a-2x"></i>
A extremely helpful contract with minimum cost, resources and top efficiency for Airlines in looking to expand into new markets or streamline costs in an existing location
</li>
</ul>
</div>
<div class="c-12 c-x-6 c-xm-8 p-20">
<div class="con-image">
<img class="w-100" src="~/images/3000/3001/gssa-operations-team.png" />
</div>
</div>
</div>
<div class="d-f mt-4">
<h3 class="mb-5 title underline">Why any Airlines should operate as Cargo GSSAs/GSAs model</h3>
</div>
<div class="cfull">
<div class="r-n-g">
<div class="c-12 c-m-6 c-x-4">
<div class="d-f p-1">
<img class="m-2" decoding="async" width="60" height="60" src="https://aircgc.com/wp-content/uploads/2024/04/duty-and-tax-calculation.svg" class="style-svg" alt="">
<div class="d-f f-c">
<h3>Cost Reduction</h3>
<p class="desc">
Using a GSSA eliminates the need for airlines to establish local offices or hire dedicated sales teams in each region. This reduces overhead costs while ensuring a strong market presence
</p>
</div>
</div>
</div>
<div class="c-12 c-m-6 c-x-4">
<div class="d-f p-1">
<img class="m-2" decoding="async" width="60" height="60" src="https://aircgc.com/wp-content/uploads/2024/03/enhanced-security.svg" class="style-svg" alt="">
<div class="d-f f-c">
<h3>Local Expertise and Market Knowledge</h3>
<p class="desc">
GSSAs possess in-depth knowledge of local markets and maintain strong relationships with key stakeholders, enabling them to maximize revenue for airlines by tapping into regional opportunities </p>
</div>
</div>
</div>
<div class="c-12 c-m-6 c-x-4">
<div class="d-f p-1">
<img class="m-2" decoding="async" width="60" height="60" src="https://aircgc.com/wp-content/uploads/2024/03/comprehensive-support-and-expertise-from-start-to-finish-1.svg" class="style-svg" alt="">
<div class="d-f f-c">
<h3>Flexibility and Innovation</h3>
<p class="desc">
GSSAs provide flexible solutions that adapt to changing market conditions. From digital booking systems to real-time data analytics, GSSAs help airlines and brokers optimize routes, reduce costs and ensure efficient cargo handling
</p>
</div>
</div>
</div>
</div>
</div>
<div class="d-f">
<h2 class="mt-2 mb-4 title underline">
Our GSSA Services at TWA
</h2>
</div>
<p class="mb-3">
Air Cargo Green Capabilities offers a range of specialized GSSA services to help airlines and brokers streamline their cargo operations and maximize revenue:
</p>
<ul class="order-list mb-4">
<li class="d-f a-i-center"><i class="atg atg-radiobutton a-2x"></i>GSSA Counseling: Air Cargo Green Capabilities provides airlines with innovative solutions to optimize costs and improve operational efficiency. By implementing eco-friendly approaches, we help airlines reduce environmental impact and capture a larger share in premium cargo segments, such as pharmaceuticals and oversized goods.</li>
<li class="d-f a-i-center mt-4"><i class="atg atg-radiobutton a-2x"></i>GSSA Pharma: Focusing on the safe transport of pharmaceutical goods, GSSA Pharma offers specialized risk assessment and logistics solutions to ensure the integrity of sensitive shipments. Our collaboration with major global organizations like UNICEF and the Red Cross enables us to support humanitarian projects while expanding the reach of pharma logistics.</li>
<li class="d-f a-i-center mt-4"><i class="atg atg-radiobutton a-2x"></i>GSSA for Airlines: We help airlines improve their cargo operations by maximizing the use of high-demand cargo segments like pharma, e-commerce, and oversized goods. Air Cargo Green Capabilities assists airlines with capacity management and long-term planning, ensuring they meet growing market demands while optimizing revenue.</li>
<li class="d-f a-i-center mt-4"><i class="atg atg-radiobutton a-2x"></i>GSSA for Charter Brokers: This service supports charter brokers in expanding their market reach and improving operational flexibility. We help brokers discover new charter opportunities and manage the logistics of complex operations, ensuring they can meet the needs of their clients efficiently and profitably.</li></ul>
@Html.Raw(ViewBag.Content)
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@Html.Raw(ViewBag.Content)
@section jsLib { @section jsLib {
<script src="@Url.AbsoluteContent("~/js/ext_libs/swiper-bundle.min.js")" js-lib></script> <script src="@Url.AbsoluteContent("~/js/ext_libs/swiper-bundle.min.js")" js-lib></script>

View File

@ -11,7 +11,7 @@
height: auto !important height: auto !important
} }
.no-wrap{ .no-wrap {
flex-wrap: nowrap !important flex-wrap: nowrap !important
} }
@ -111,7 +111,7 @@ h1, h2, h3, h4, h5, h6, p {
height: 100% !important; height: 100% !important;
} }
.h-a{ .h-a {
height: auto height: auto
} }
@ -119,8 +119,8 @@ h1, h2, h3, h4, h5, h6, p {
width: 100% !important width: 100% !important
} }
.w-n{ .w-n {
width:none !important width: none !important
} }
.cfull, .con { .cfull, .con {
@ -137,11 +137,11 @@ h1, h2, h3, h4, h5, h6, p {
max-width: 100%; max-width: 100%;
} }
.w-s-100{ .w-s-100 {
width: 100% !important width: 100% !important
} }
.w-s-n{ .w-s-n {
width: none !important width: none !important
} }
} }
@ -150,7 +150,7 @@ h1, h2, h3, h4, h5, h6, p {
.con .r { .con .r {
max-width: 720px; max-width: 720px;
margin-left: auto; margin-left: auto;
margin-right:auto; margin-right: auto;
} }
} }
@ -164,7 +164,6 @@ h1, h2, h3, h4, h5, h6, p {
.con .r { .con .r {
max-width: 1110px; max-width: 1110px;
} }
} }
@media (min-width:1400px) { @media (min-width:1400px) {
@ -201,6 +200,11 @@ h1, h2, h3, h4, h5, h6, p {
margin-left: 0; margin-left: 0;
} }
.r-n-g.n {
margin-right: -20px;
margin-left: -20px
}
.r > .c { .r > .c {
padding-right: 0; padding-right: 0;
padding-left: 0; padding-left: 0;
@ -768,10 +772,11 @@ h1, h2, h3, h4, h5, h6, p {
gap: 3rem !important; gap: 3rem !important;
} }
.o-1{ .o-1 {
order: 1 order: 1
} }
.o-2{
.o-2 {
order: 2 order: 2
} }
@ -1067,7 +1072,6 @@ h1, h2, h3, h4, h5, h6, p {
.gap-s-5 { .gap-s-5 {
gap: 3rem !important; gap: 3rem !important;
} }
} }
@media (min-width:768px) { @media (min-width:768px) {
@ -1354,16 +1358,19 @@ h1, h2, h3, h4, h5, h6, p {
.ml-m-3 { .ml-m-3 {
margin-left: 1rem !important margin-left: 1rem !important
} }
.mr-m-3{
.mr-m-3 {
margin-right: 1rem !important margin-right: 1rem !important
} }
.pt-m-0{ .pt-m-0 {
padding-top: 0!important padding-top: 0 !important
} }
.pb-m-0{
padding-bottom: 0!important .pb-m-0 {
padding-bottom: 0 !important
} }
.pl-m-5 { .pl-m-5 {
padding-left: 3rem !important; padding-left: 3rem !important;
} }
@ -1371,6 +1378,7 @@ h1, h2, h3, h4, h5, h6, p {
.pr-m-5 { .pr-m-5 {
padding-right: 3rem !important; padding-right: 3rem !important;
} }
.pr-m-4 { .pr-m-4 {
padding-right: 1.5rem !important; padding-right: 1.5rem !important;
} }
@ -1398,6 +1406,7 @@ h1, h2, h3, h4, h5, h6, p {
.gap-m-5 { .gap-m-5 {
gap: 3rem !important; gap: 3rem !important;
} }
.o-m-1 { .o-m-1 {
order: 1 order: 1
} }
@ -1406,7 +1415,7 @@ h1, h2, h3, h4, h5, h6, p {
order: 2 order: 2
} }
.text-m-right{ .text-m-right {
text-align: right text-align: right
} }
} }
@ -1680,7 +1689,7 @@ h1, h2, h3, h4, h5, h6, p {
align-self: stretch !important; align-self: stretch !important;
} }
.mt-l-0{ .mt-l-0 {
margin-top: 0 !important margin-top: 0 !important
} }
@ -1691,21 +1700,25 @@ h1, h2, h3, h4, h5, h6, p {
.ml-l-3 { .ml-l-3 {
margin-left: 1rem !important margin-left: 1rem !important
} }
.ml-l-5 { .ml-l-5 {
margin-left: 3rem !important margin-left: 3rem !important
} }
.mr-l-5{
.mr-l-5 {
margin-right: 3rem !important margin-right: 3rem !important
} }
.mr-l-0 { .mr-l-0 {
margin-right: 0 !important margin-right: 0 !important
} }
.mr-l-3{
.mr-l-3 {
margin-right: 1rem !important margin-right: 1rem !important
} }
.mb-l-0{ .mb-l-0 {
margin-bottom: 0!important; margin-bottom: 0 !important;
} }
.pr-l-5 { .pr-l-5 {
@ -1740,7 +1753,7 @@ h1, h2, h3, h4, h5, h6, p {
gap: 3rem !important; gap: 3rem !important;
} }
.h-l-100{ .h-l-100 {
height: 100% height: 100%
} }
} }
@ -2014,7 +2027,7 @@ h1, h2, h3, h4, h5, h6, p {
align-self: stretch !important; align-self: stretch !important;
} }
.mt-x-0{ .mt-x-0 {
margin-top: 0 !important margin-top: 0 !important
} }
@ -2022,11 +2035,11 @@ h1, h2, h3, h4, h5, h6, p {
margin-left: auto !important; margin-left: auto !important;
} }
.ml-x-5{ .ml-x-5 {
margin-left: 3rem !important margin-left: 3rem !important
} }
.mr-x-5{ .mr-x-5 {
margin-right: 3rem !important margin-right: 3rem !important
} }
@ -2448,11 +2461,13 @@ html {
font-size: 16px; font-size: 16px;
} }
} }
@media (min-width: 1400px) { @media (min-width: 1400px) {
html { html {
font-size: 18px; font-size: 18px;
} }
} }
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
font-family: "Playfair Display", serif; font-family: "Playfair Display", serif;
font-weight: 800 font-weight: 800
@ -2535,8 +2550,8 @@ h3 {
} }
@media (min-width: 576px){ @media (min-width: 576px) {
:root{ :root {
--margin-base: 50px --margin-base: 50px
} }
} }
@ -2721,6 +2736,6 @@ h3 {
transition: .35s all ease-in-out transition: .35s all ease-in-out
} }
.con-image:hover img{ .con-image:hover img {
transform: scale(1.1) transform: scale(1.1)
} }

View File

@ -179,6 +179,7 @@ h5{
.according .item { .according .item {
font-weight: 500; font-weight: 500;
padding: 20px; padding: 20px;
transition: all .3s ease-in-out
} }
.according .item::before, .according .item::after { .according .item::before, .according .item::after {
@ -197,10 +198,11 @@ h5{
transform: rotate(90deg); transform: rotate(90deg);
} }
.according .item:hover{ .according .item:hover, .according .item.active{
font-size: 1rem;
color: var(--color-primary) color: var(--color-primary)
} }
.according .item::before:hover, .according .item::after:hover{ .according .item::before:hover, .according .item::after:hover, .according .item.active::before, .according .item.active::after {
background: var(--color-primary) background: var(--color-primary)
} }
@ -2129,6 +2131,7 @@ video.bg-video {
padding: 25px; padding: 25px;
background: var(--text-color-lW3); background: var(--text-color-lW3);
border-radius: var(--radius); border-radius: var(--radius);
z-index: 2;
transition: top .3s ease-in-out transition: top .3s ease-in-out
} }
.sidebarR .nav-item { .sidebarR .nav-item {
@ -2676,3 +2679,42 @@ video.bg-video {
bottom: -10px; bottom: -10px;
} }
/*********************End Section*******************/ /*********************End Section*******************/
.section-contact{
padding-top:40px
}
.contact-form .hd1 {
font-size: 1.2rem;
font-weight: 600;
}
.contact-form .hd2 {
font-size: 1.4rem;
font-weight: 700;
}
.contact-group {
margin-bottom: 15px
}
.contact-group i{
display: flex;
align-items: center;
justify-content: center;
width:45px;
height: 45px;
background: var(--color-primary);
color: white;
margin-right: 15px;
border-radius: var(--radius)
}
.contact-group div{
width: calc(100% - 60px)
}
.contact-group .title{
font-weight: 700;
margin-bottom: 2px;
color: var(--text-color-dark)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

View File

@ -28,10 +28,10 @@ export default class AElementFixed extends window.AObject {
this._snapLoopId = null; // rAF: monitor chạm đáy CHA khi đang snap this._snapLoopId = null; // rAF: monitor chạm đáy CHA khi đang snap
this._needIntroPose = true;// hiệu ứng intro khi snap this._needIntroPose = true;// hiệu ứng intro khi snap
this._snapWidth = null; // lock width (px) khi portal this._snapWidth = null; // lock width (px) khi portal
this._isInit = false;
this._bind(); this._bind();
this._initObservers();
this._onResize(); this._onResize();
} }
update(opts = {}) { update(opts = {}) {
@ -59,10 +59,17 @@ export default class AElementFixed extends window.AObject {
_onResize() { _onResize() {
const desktop = window.innerWidth >= this.o.breakpoint; const desktop = window.innerWidth >= this.o.breakpoint;
if (!desktop) { if (desktop) {
if (!this._isInit) {
this._initObservers();
this._isInit = true;
}
} else {
this._isInit = false;
this._stopInViewLoop(); this._stopInViewLoop();
this._stopSnapMonitor(); this._stopSnapMonitor();
this._unsnapAbsolute(); this._unsnapAbsolute();
this._teardownObservers();
} }
} }
@ -102,7 +109,6 @@ export default class AElementFixed extends window.AObject {
const vpBottom = e.rootBounds ? e.rootBounds.bottom : document.documentElement.clientHeight; const vpBottom = e.rootBounds ? e.rootBounds.bottom : document.documentElement.clientHeight;
const EPS = 0.75; const EPS = 0.75;
if (e.isIntersecting) { if (e.isIntersecting) {
console.log(e.target.style.bottom);
if (e.target.style.bottom === '0px') { if (e.target.style.bottom === '0px') {
this._startInViewLoop(); this._startInViewLoop();
@ -258,7 +264,7 @@ export default class AElementFixed extends window.AObject {
this.el.style.left = ''; this.el.style.left = '';
this.el.style.top = ''; this.el.style.top = '';
this.el.style.bottom = ''; this.el.style.bottom = '';
this.el.style.width = '';
this._restoreToHostBeforeProbe(); this._restoreToHostBeforeProbe();
} }
} }

View File

@ -130,7 +130,7 @@ export default class AMenu extends window.AObject {
el.classList.remove("active"); el.classList.remove("active");
}) })
} else { } else {
this.dropdown.closeDropdown(); this.dropdown.checkCloseDropdown();
} }
}).bind(this)); }).bind(this));
}).bind(this), 100); }).bind(this), 100);

View File

@ -0,0 +1,17 @@
using AppLibs.Libs;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace TWASys_App.Controllers
{
[AllowAnonymous]
public class LoginController : Controller
{
[Layout(LayoutOptions.Custom, "Login")]
[PageInfor("10001", "Login")]
public async Task<IActionResult> Index()
{
return await this.ViewAsync();
}
}
}

View File

@ -1,13 +1,9 @@
using AppLibs.Libs; using AppLibs.Libs;
using TWASys_App.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Connections.Features;
using System.Diagnostics.Metrics;
using System.Security.Cryptography;
using System.Text.Encodings;
using System.Text;
using AppLibs.Libs.Crypt; using AppLibs.Libs.Crypt;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using System.Security.Cryptography;
using TWASys_App.Models;
using TWASys_App.Models.ServerInfo; using TWASys_App.Models.ServerInfo;
namespace TWASys_App.Controllers namespace TWASys_App.Controllers
@ -83,36 +79,40 @@ namespace TWASys_App.Controllers
return Json(await model.UpdateAsync()); return Json(await model.UpdateAsync());
} }
[HttpPost]
public async Task<IActionResult> SetStorageData([FromForm] StorageModel model)
{
return Json(await model.AddAsync());
}
[HttpGet] [HttpGet]
public async Task<IActionResult> GenerationAccessToken() public async Task<IActionResult> GenerationAccessToken()
{ {
var t1 = Task<string>.Run(() => var t1 = Task<Guid>.Run(() =>
{ {
var g = Guid.NewGuid(); return UUID7.NewUuid7();
return Base64UrlTextEncoder.Encode(g.ToByteArray().Concat(BitConverter.GetBytes(DateTime.UtcNow.ToFileTime())).ToArray());
}); });
var t2 = Task<string>.Run(() => { var t2 = Task<string>.Run(() => {
var g = Guid.NewGuid(); var tokenValue = Base64UrlTextEncoder.Encode(
var bytes = BitConverter.GetBytes(DateTime.UtcNow.Ticks); Guid.NewGuid().ToByteArray()
.Concat(BitConverter.GetBytes(DateTime.UtcNow.ToFileTimeUtc()))
.ToArray()
);
using var sha = SHA256.Create();
var idToken = WebEncoders.Base64UrlEncode(sha.ComputeHash(
System.Text.Encoding.UTF8.GetBytes(tokenValue)
));
Array.Resize(ref bytes, 16); return idToken;
byte[] data = new byte[32];
bytes.CopyTo(data, 0);
g.ToByteArray().CopyTo(data, 16);
var hash = new CRC64();
return hash.HashToString(data);
}); });
await Task.WhenAll(t1, t2); await Task.WhenAll(t1, t2);
return Json(new return Json(new
{ {
idToken = t2.Result.ToLower(), idToken = t1.Result.ToString(),
tokenValue = t1.Result tokenValue = t2.Result.ToLower()
}); });
} }

View File

@ -4,7 +4,7 @@ namespace TWASys_App.DBModels
{ {
public class DBManagement public class DBManagement
{ {
public static string GetConnectionString() public static string GetConnectionString(bool allowVar = false)
{ {
string fp = Path.GetFullPath("Keys/db/"); string fp = Path.GetFullPath("Keys/db/");
@ -28,6 +28,7 @@ namespace TWASys_App.DBModels
SslCa = Path.Combine(fp, "ca-cert.pem"), SslCa = Path.Combine(fp, "ca-cert.pem"),
SslCert = Path.Combine(fp, "client-cert.pem"), SslCert = Path.Combine(fp, "client-cert.pem"),
SslKey = Path.Combine(fp, "client-key.pem"), SslKey = Path.Combine(fp, "client-key.pem"),
AllowUserVariables = allowVar
}; };
//172.168.192.204 //172.168.192.204
return builder.ConnectionString; return builder.ConnectionString;

View File

@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class Files
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ulong IdFiles { get; set; }
[ForeignKey("IdFolders")]
public ulong IdFolders { get; set; } // BIGINT FK → Folders.idFolders
public string Code { get; set; } = ""; // VARCHAR(100)
public string Name { get; set; } = ""; // VARCHAR(100)
public string Path { get; set; } = ""; // TEXT
public string Options { get; set; } = ""; // LONGTEXT (JSON, metadata)
public DateTime CreateDate { get; set; } = DateTime.UtcNow; // DATETIME (UTC khuyên dùng)
public DateTime? LastModified { get; set; } = null; // DATETIME NULL
public int Status { get; set; } // INT(11)
public Folder? Folder { get; set; } = null!; // nav
}
}

View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class Folder
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ulong IdFolders { get; set; } // BIGINT PK
public ulong? IdParent { get; set; } // BIGINT FK, null = root
public string Name { get; set; } = ""; // VARCHAR(45)
public string Code { get; set; } = ""; // VARCHAR(100)
public string Path { get; set; } = ""; // TEXT
public string Options { get; set; } = ""; // LONGTEXT
public DateTime CreateDate { get; set; } = DateTime.UtcNow; // DATETIME
public DateTime? LastModified { get; set; } = null; // DATETIME NULL
public int Status { get; set; } // INT(11)
public Folder? Parent { get; set; } = null;
public ICollection<Folder> Children { get; set; } = new List<Folder>();
}
}

View File

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class Folders_has_StorageArea
{
[ForeignKey("")]
public ulong IdFolders { get; set; }
[ForeignKey("")]
public long IdStorage { get; set; }
public Folder Folder { get; set; } = null!;
public StorageArea Storage { get; set; } = null!;
}
}

View File

@ -7,66 +7,30 @@ namespace TWASys_App.DBModels
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("idLocalServer")] public long IdLocalServer { get; set; } = 0;
public int IdLocalServer { get; set; } = 0;
[StringLength(20)]
[Column("ipPrivateServer")]
public string IpPrivateServer { get; set; } = ""; public string IpPrivateServer { get; set; } = "";
public string IpPrivateServerv6 { get; set; } = "";
[StringLength(100)]
[Column("ipPrivateServer6")]
public string IpPrivateServer6 { get; set; } = "";
[StringLength(100)]
[Column("ipPublicServer")]
public string IpPublicServer { get; set; } = ""; public string IpPublicServer { get; set; } = "";
[StringLength(45)]
[Column("pathServer")]
public string PathServer { get; set; } = ""; public string PathServer { get; set; } = "";
[StringLength(100)]
[Column("serialNumber")]
public string SerialNumber { get; set; } = ""; public string SerialNumber { get; set; } = "";
// Ảnh gốc ghi "osVesion" (sai chính tả). Giữ nguyên để khớp DB.
[StringLength(100)]
[Column("osVesion")]
public string OsVersion { get; set; } = ""; public string OsVersion { get; set; } = "";
[StringLength(100)]
[Column("osName")]
public string OsName { get; set; } = ""; public string OsName { get; set; } = "";
[StringLength(45)]
[Column("osArch")]
public string OsArch { get; set; } = ""; public string OsArch { get; set; } = "";
// Ảnh gốc ghi "osKernal". Giữ nguyên để khớp DB. public string OsKernal { get; set; } = "";
[StringLength(100)]
[Column("osKernal")]
public string OsKernel { get; set; } = "";
[Column("socketNum")]
public int SocketNum { get; set; } = 0; public int SocketNum { get; set; } = 0;
[StringLength(150)]
[Column("cpuName")]
public string CpuName { get; set; } = ""; public string CpuName { get; set; } = "";
public float TotalRam { get; set; } = 0;
[Column("totalRam")] public string BiosVender { get; set; } = "";
public int TotalRam { get; set; } = 0;
[StringLength(100)]
[Column("biosVendor")]
public string BiosVendor { get; set; } = "";
[StringLength(100)]
[Column("productUUID")]
public string ProductUuid { get; set; } = ""; public string ProductUuid { get; set; } = "";
[Column("status")]
public int Status { get; set; } = 0; public int Status { get; set; } = 0;
} }

View File

@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class MicrosoftAccount
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long IdCloudAccount { get; set; }
public string AccName { get; set; } = null!;
public string ClientID { get; set; } = null!; // GUID string
public string TenantID { get; set; } = null!;
public string? ClientSecret { get; set; } // encrypted at rest
public string? SiteID { get; set; }
public string? DriveID { get; set; }
public string? PathSharePoint { get; set; }
public string? RefreshToken { get; set; } // encrypted
public string? AccessToken { get; set; } // optional cache
public DateTime? ExpiresAt { get; set; }
public string? Scopes { get; set; }
public DateTime CreateDate { get; set; }
public DateTime? LastModified { get; set; }
public int Status { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class RefreshToken
{
[Key]
public string IdRefreshToken { get; set; } = "";
[ForeignKey("IdServerAuthorization")]
public ulong IdServerAuthorization { get; set; }
public string __RefreshToken { get; set; } = ""; // refreshToken
public DateTime CreateDate { get; set; }
public DateTime ExpireDate { get; set; }
public int Status { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class RefreshToken_Log
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ulong IdRefreshTokenLog { get; set; }
public string IdRefreshToken { get; set; } = null!;
public DateTime DateRenew { get; set; } // UTC
public int Count { get; set; } = 1;
public int LifeTime { get; set; }
public int Status { get; set; } = 1;
public RefreshToken RefreshToken { get; set; } = null!;
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class ServerAuthorization
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public ulong IdServerAuthorization { get; set; }
[ForeignKey("IdStorageServer")]
public long IdStorageServer { get; set; }
public DateTime CreateDate { get; set; }
public int Count { get; set; }
public int Status { get; set; }
[NotMapped()]
public ICollection<ServerAuthorization_has_Token> Tokens { get; set; } = new List<ServerAuthorization_has_Token>();
}
}

View File

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class ServerAuthorization_has_Token
{
[ForeignKey("")]
public ulong IdServerAuthorization { get; set; }
[ForeignKey("")]
public string IdToken { get; set; } = "";
public int Count { get; set; }
public int Status { get; set; }
public ServerAuthorization ServerAuthorization { get; set; } = null!;
public Token Token { get; set; } = null!;
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class StorageArea
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long IdStorage { get; set; } // PK (AUTO_INCREMENT nếu DB đang để vậy)
public ulong? IdEmp { get; set; } = null; // FK -> Emp (nếu có)
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
public double TotalSize { get; set; } = 0; // map FLOAT MySQL -> double
public int Status { get; set; } = 0;
}
}

View File

@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class StorageArea_has_StorageServer
{
[ForeignKey("IdStorage")]
public long IdStorage { get; set; }
[ForeignKey("IdStorageServer")]
public long IdStorageServer { get; set; }
[Column("priority", TypeName = "int")]
public int Priority { get; set; } = 0;
[Column("createDate", TypeName = "datetime")]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
[Column("status", TypeName = "int")]
public int Status { get; set; } = 0;
}
}

View File

@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class StorageServer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long IdStorageServer { get; set; }
public long? IdEmp { get; set; }
[ForeignKey("IdTypeStorageServer")]
public long IdTypeStorageServer { get; set; }
public string StorageName { get; set; } = "";
public DateTime? CreateDate { get; set; }
public string ControllerID { get; set; } = "";
public int Status { get; set; } = 0;
}
}

View File

@ -2,22 +2,22 @@
namespace TWASys_App.DBModels namespace TWASys_App.DBModels
{ {
public class StorageServerHasLocalServer public class StorageServer_has_LocalServer
{ {
[Column("idStorageServer")] [ForeignKey("IdStorageServer")]
public int IdStorageServer { get; set; } = 0; public long IdStorageServer { get; set; } = 0;
[ForeignKey("IdLocalServer")]
public long IdLocalServer { get; set; } = 0;
[Column("idLocalServer")]
public int IdLocalServer { get; set; } = 0;
[Column("createDate")]
public DateTime CreateDate { get; set; } = DateTime.UtcNow; // nên lưu UTC public DateTime CreateDate { get; set; } = DateTime.UtcNow; // nên lưu UTC
[Column("modifyDate")]
public DateTime? ModifyDate { get; set; } = null; // nên lưu UTC public DateTime? ModifyDate { get; set; } = null; // nên lưu UTC
[Column("status")]
public int Status { get; set; } = 0; public int Status { get; set; } = 0;
} }
} }

View File

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class StorageServer_has_MicrosoftAccount
{
[ForeignKey("")]
public long IdStorageServer { get; set; }
[ForeignKey("")]
public long IdCloudAccount { get; set; }
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
public int Status { get; set; } = 1;
public StorageServer StorageServer { get; set; } = null!;
public MicrosoftAccount CloudAccount { get; set; } = null!;
}
}

View File

@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class StorageServers_ValidationDomain
{
[ForeignKey("")]
public long IdStorageServer { get; set; }
[ForeignKey("")]
public long IdValidationDomain { get; set; }
[Column("modifyDate", TypeName = "datetime")]
public DateTime? ModifyDate { get; set; } = null;
[Column("createDate", TypeName = "datetime")]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
[Column("status", TypeName = "int")]
public int Status { get; set; } = 0;
}
}

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class SyncFile_Log
{
public ulong Id { get; set; }
[ForeignKey("")]
public long IdFiles { get; set; }
[ForeignKey("")]
public long IdStorageServer { get; set; }
[ForeignKey("")]
public long IdLocalServer { get; set; }
[ForeignKey("")]
public long IdCloudAccount { get; set; } // FK -> CloudAccounts.id
public DateTime SyncDate { get; set; } = DateTime.UtcNow; // UTC
public string? PathOnServer { get; set; } // đường dẫn lưu trên đích
public int Status { get; set; } // 0=pending,1=ok,2=retry,3=failed,...
// nav
public Files File { get; set; } = null!;
}
}

View File

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels
{
public class Token
{
[Key]
public string IdToken { get; set; } = "";
public string AccessToken { get; set; } = "";
public DateTime CreateDate { get; set; }
public DateTime ExpireDate { get; set; }
public int Status { get; set; }
}
}

View File

@ -1,10 +1,12 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels namespace TWASys_App.DBModels
{ {
public class TypeStorageServer public class TypeStorageServer
{ {
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IdTypeStorageServer { get; set; } public int IdTypeStorageServer { get; set; }
public string TypeName { get; set; } public string TypeName { get; set; }

View File

@ -1,11 +1,13 @@
using System.ComponentModel.DataAnnotations;  using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TWASys_App.DBModels namespace TWASys_App.DBModels
{ {
public class ValidationDomain public class ValidationDomain
{ {
[Key] [Key]
public int IdValidationDomain { set; get; } [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long IdValidationDomain { set; get; }
public string Protocol { set; get; } public string Protocol { set; get; }
public int PortNumber { set; get; } public int PortNumber { set; get; }

View File

@ -0,0 +1,329 @@
using Dapper;
using Microsoft.AspNetCore.Identity;
using MySqlConnector;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Intrinsics.Arm;
using System.Text;
namespace TWASys_App.Dapper.AExtentions
{
public class DataValue
{
public object? Data { set; get; }
public string Name { set; get; } = "";
public string PKName { set; get; } = "";
}
public class BatchInsert
{
private IDbConnection _connect;
private IDbTransaction _transaction;
private Dictionary<string, DataValue> _dataKey;
private List<DataValue> _list;
private StringBuilder queryBatch = new();
public int IdTable = 100;
public BatchInsert(IDbConnection connect, IDbTransaction transaction)
{
_connect = connect;
_transaction = transaction;
_dataKey = new Dictionary<string, DataValue>();
_list = new List<DataValue>();
}
public void AddRow<T>(T obj)
{
var dt = new DataValue
{
Name = "",
PKName = "",
Data = obj
};
_list.Add(dt);
var p = DapperCommand.QueryInsert(obj, _dataKey, IdTable);
IdTable++;
dt.Name = p.Name;
dt.PKName = p.KeyVar;
if (p.IsKeyAI)
{
_dataKey.TryAdd(p.KeyVar, dt);
}
queryBatch.AppendLine(p.Query);
}
public string GetQuery()
{
string query = string.Empty;
foreach (var (key, value) in _dataKey)
{
query += $"Select @tb{value.Name}_{key} As {key}; \n";
}
queryBatch.AppendLine(query);
return queryBatch.ToString();
}
public async Task ExcuteQuery()
{
var dynamic = ToDynamicParametersSeqPrefix(_list);
var str = GetQuery();
Console.WriteLine(str);
using var grid = await _connect.QueryMultipleAsync(str, dynamic, _transaction);
await MapAutoIncrementIdsAsync(grid, _dataKey.Values);
}
public static async Task MapAutoIncrementIdsAsync(SqlMapper.GridReader grid,IEnumerable<DataValue> items)
{
foreach (var dv in items)
{
var obj = dv?.Data;
if (obj is null) continue;
var (pk, isAuto) = FindPrimaryKey(obj.GetType());
if (pk is null || !isAuto) continue; // không phải auto-inc → không đọc scalar
// nếu object đã có ID (không rỗng) thì bỏ qua để không lệch thứ tự result set
var cur = pk.GetValue(obj);
if (!IsEmptyId(cur)) continue;
// đọc scalar từ SELECT kế tiếp và gán lại cho property khóa
var raw = await grid.ReadSingleAsync<object>();
var targetType = Nullable.GetUnderlyingType(pk.PropertyType) ?? pk.PropertyType;
if (raw is IDictionary<string, object> row)
{
if (!row.TryGetValue(dv?.PKName, out raw))
throw new InvalidOperationException($"Missing column {dv?.PKName}");
}
// MySqlDecimal → decimal
if (raw?.GetType().FullName == "MySql.Data.Types.MySqlDecimal")
{
var t = raw.GetType();
raw = (decimal)t.GetProperty("Value")!.GetValue(raw)!;
}
// Giờ o phải là số: long/ulong/int/decimal/…
var target = Nullable.GetUnderlyingType(pk.PropertyType) ?? pk.PropertyType;
pk.SetValue(obj, ConvertNumericTo(target, raw));
}
}
static object ConvertNumericTo(Type target, object? x)
{
// ưu tiên xử lý 2 nhánh chính
if (target == typeof(ulong) || target == typeof(uint) || target == typeof(ushort) || target == typeof(byte))
{
ulong u = x switch
{
ulong v => v,
long v => v < 0 ? throw new OverflowException() : (ulong)v,
int v => v < 0 ? throw new OverflowException() : (ulong)v,
decimal v => v < 0 ? throw new OverflowException() : checked((ulong)v),
_ => Convert.ToUInt64(x, System.Globalization.CultureInfo.InvariantCulture)
};
return target == typeof(ulong) ? u :
target == typeof(uint) ? checked((uint)u) :
target == typeof(ushort) ? checked((ushort)u) :
checked((byte)u);
}
else
{
long s = x switch
{
long v => v,
ulong v => v > (ulong)long.MaxValue ? throw new OverflowException() : (long)v,
int v => v,
decimal v => checked((long)v),
_ => Convert.ToInt64(x, System.Globalization.CultureInfo.InvariantCulture)
};
return target == typeof(long) ? s :
target == typeof(int) ? checked((int)s) :
target == typeof(short) ? checked((short)s) :
checked((sbyte)s);
}
}
public static DynamicParameters ToDynamicParametersSeqPrefix(
IEnumerable<DataValue> inputs // 0 coi là "trống" (trừ property tên Status)
)
{
var dp = new DynamicParameters();
var seen = new HashSet<string>(StringComparer.Ordinal);
foreach (var src in inputs)
{
var t = src.Data?.GetType();
var props = t?.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var p in props)
{
if (!p.CanRead) continue;
if (IsNotMapped(p)) continue;
if (IsKeyOrForeignKey(p, src.Data))
continue;
var paramName = "tb" + src.Name + "_" + p.Name;
var val = p.GetValue(src.Data);
dp.Add("@" + paramName, val);
}
}
return dp;
}
private static (PropertyInfo? Key, bool IsAutoInc) FindPrimaryKey(Type t)
{
PropertyInfo? key = null; bool auto = false;
foreach (var p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (HasAttr(p, "System.ComponentModel.DataAnnotations.KeyAttribute", "KeyAttribute",
"Dapper.Contrib.Extensions.KeyAttribute"))
{ key = p; auto = IsDatabaseGeneratedIdentity(p); break; }
if (HasAttr(p, "Dapper.Contrib.Extensions.ExplicitKeyAttribute", "ExplicitKeyAttribute"))
{ key = p; auto = false; break; }
}
// fallback theo tên "Id" nếu không có attribute
key ??= t.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance);
// nếu không tìm được attribute, đoán auto-inc cho kiểu số
if (key != null && !auto)
{
var k = Nullable.GetUnderlyingType(key.PropertyType) ?? key.PropertyType;
auto = k == typeof(int) || k == typeof(long) || k == typeof(uint) || k == typeof(ulong);
}
return (key, auto);
}
private static bool IsDatabaseGeneratedIdentity(PropertyInfo p)
{
// [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
foreach (var ca in p.CustomAttributes)
{
var full = ca.AttributeType.FullName ?? ca.AttributeType.Name;
if (full is "System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute"
or "DatabaseGeneratedAttribute")
{
if (ca.ConstructorArguments.Count == 1 && ca.ConstructorArguments[0].ArgumentType.IsEnum)
return Convert.ToInt32(ca.ConstructorArguments[0].Value) == 1; // Identity = 1
}
}
return false;
}
private static bool IsNotMapped(PropertyInfo p)
{
return HasAttr(p, "System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute", "NotMappedAttribute");
}
private static bool IsKey(PropertyInfo p, object? obj)
{
if( HasAttr(p,
"System.ComponentModel.DataAnnotations.KeyAttribute", "KeyAttribute",
"Dapper.Contrib.Extensions.KeyAttribute"))
{
var val = p.GetValue(obj);
ulong t = 0;
if (val == null && string.IsNullOrWhiteSpace(val?.ToString()))
{
return true;
}
else
{
if (ulong.TryParse(val?.ToString(), out t) && t == 0)
{
return true;
}
}
}
return false;
}
private static bool IsKeyOrForeignKey(PropertyInfo p, object? obj)
{
return IsKey(p, obj) || IsForeignKeyProperty(p, obj);
}
private static bool HasAttr(MemberInfo m, params string[] names)
{
return m.CustomAttributes.Any(ca =>
{
var full = ca.AttributeType.FullName ?? ca.AttributeType.Name;
var shortName = ca.AttributeType.Name;
return names.Contains(full) || names.Contains(shortName);
});
}
private static bool IsForeignKeyProperty(PropertyInfo p, object? obj)
{
// [ForeignKey] ngay trên property
if (HasAttr(p, "System.ComponentModel.DataAnnotations.Schema.ForeignKeyAttribute", "ForeignKeyAttribute"))
{
var val = p.GetValue(obj);
ulong t = 0;
if (val == null && string.IsNullOrWhiteSpace(val?.ToString()))
{
return true;
}
else
{
if (ulong.TryParse(val?.ToString(), out t) && t == 0)
{
return true;
}
}
}
//// [ForeignKey("PropName")] đặt trên navigation khác, trỏ tới property này
//var my = p.Name;
//foreach (var nav in allProps)
//{
// if (ReferenceEquals(nav, p)) continue;
// var fkAttr = nav.CustomAttributes.FirstOrDefault(ca =>
// (ca.AttributeType.FullName ?? ca.AttributeType.Name) is
// "System.ComponentModel.DataAnnotations.Schema.ForeignKeyAttribute" or "ForeignKeyAttribute");
// if (fkAttr == default) continue;
// var nameArg = fkAttr.ConstructorArguments.FirstOrDefault().Value?.ToString();
// if (string.IsNullOrWhiteSpace(nameArg))
// nameArg = fkAttr.NamedArguments.FirstOrDefault(a => a.MemberName == "Name").TypedValue.Value?.ToString();
// if (string.IsNullOrWhiteSpace(nameArg)) continue;
// var names = nameArg.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
// .Select(s => s.Trim());
// if (names.Any(n => string.Equals(n, my, StringComparison.OrdinalIgnoreCase)))
// return true;
//}
return false;
}
private static bool IsEmptyId(object? v)
{
if (v is null) return true;
var t = Nullable.GetUnderlyingType(v.GetType()) ?? v.GetType();
if (!t.IsValueType) return false;
try { return v.Equals(Activator.CreateInstance(t)); } catch { return false; }
}
}
public sealed class PropMeta
{
public required PropertyInfo Prop { get; init; }
public required Func<object, object?> Getter { get; init; }
public required Type UnderlyingType { get; init; }
public required bool IsNotMapped { get; init; }
public required bool IsKeyOrForeignKey { get; init; }
}
}

View File

@ -1,12 +1,80 @@
using Dapper; using Dapper;
using Newtonsoft.Json.Linq;
using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Net.WebSockets;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
namespace TWASys_App.Dapper.AExtentions namespace TWASys_App.Dapper.AExtentions
{ {
public static class DapperCommand public static class DapperCommand
{ {
public static async Task Insert<T>(this DbConnection connection, T obj, bool identityInsert = true)
public static dynamic QueryInsert<T>(T obj)
{
return QueryInsert(obj, new Dictionary<string, DataValue>());
}
public static dynamic QueryInsert<T>(T obj, Dictionary<string, DataValue> data, int IdTable = 100)
{
var type = typeof(T);
var tableName = TableMapper.GetTableName(type);
var allProperties = PropertiesCache.TypePropertiesCache(type);
var keyProperties = PropertiesCache.KeyPropertiesCache(type);
var fkProperties = PropertiesCache.FKPropertiesCache(type);
var computedProperties = PropertiesCache.ComputedPropertiesCache(type);
var columns = PropertiesCache.GetColumnNamesCache(type);
var insertProperties = allProperties.Except(computedProperties).Except(fkProperties).ToList();
var insertPropertiesString = string.Empty;
var stringKeyProperty = string.Empty;
var keyAI = string.Empty;
var flag = false;
var q = string.Empty;
if (keyProperties.Count > 0)
{
flag = true;
insertProperties = insertProperties.Except(keyProperties).ToList();
stringKeyProperty = GetColumnsString(keyProperties.ToList(), columns);
keyAI = $@"SET @{GetColumnsString(keyProperties.ToList(), columns, IdTable.ToString(), "")} := LAST_INSERT_ID();";
}
insertPropertiesString = GetColumnsString(insertProperties, columns);
var fkPropertiesString = string.Empty;
var fkValuesString = string.Empty;
if (data.Count > 0)
{
fkPropertiesString = GetColumnsString(fkProperties, columns);
fkValuesString = string.Join(", ", fkProperties.Select(u => {
var ok = data.TryGetValue(u.Name, out var v);
if (ok)
{
return $"@tb{v?.Name}_{u.Name}";
}
else
{
return $"@tb{IdTable}_{u.Name}";
}
}));
fkPropertiesString += (string.IsNullOrEmpty(fkPropertiesString))?"": ", ";
fkValuesString += (string.IsNullOrEmpty(fkValuesString)) ? "" : ", ";
}
q = $@"INSERT INTO {FormatTableName(tableName)} ({fkPropertiesString + insertPropertiesString}) VALUES ({fkValuesString + GetColumnsString(insertProperties, columns, IdTable.ToString(), "@")});
{keyAI}";
return new
{
IsKeyAI = flag,
Query = q,
Name = IdTable.ToString(),
KeyVar = stringKeyProperty
};
}
public static async Task Insert<T>(this DbConnection connection, T obj, IDbTransaction? tx = null)
{ {
var type = typeof(T); var type = typeof(T);
var tableName = TableMapper.GetTableName(type); var tableName = TableMapper.GetTableName(type);
@ -17,13 +85,14 @@ namespace TWASys_App.Dapper.AExtentions
var insertProperties = allProperties.Except(computedProperties).ToList(); var insertProperties = allProperties.Except(computedProperties).ToList();
var insertPropertiesString = string.Empty; var insertPropertiesString = string.Empty;
if (identityInsert)
if (keyProperties.Count > 0)
{ {
insertProperties = insertProperties.Except(keyProperties).ToList(); insertProperties = insertProperties.Except(keyProperties).ToList();
insertPropertiesString = GetColumnsString(insertProperties, columns); insertPropertiesString = GetColumnsString(insertProperties, columns);
var insertedId = await connection.QueryFirstAsync<int>($@" var insertedId = await connection.QueryFirstAsync<int>($@"
INSERT INTO {FormatTableName(tableName)} ({insertPropertiesString}) VALUES ({GetColumnsString(insertProperties, columns, "@")}); INSERT INTO {FormatTableName(tableName)} ({insertPropertiesString}) VALUES ({GetColumnsString(insertProperties, columns, "@")});
SELECT LAST_INSERT_ID()", obj); SELECT LAST_INSERT_ID()", obj, tx);
keyProperties[0].SetValue(obj, insertedId); keyProperties[0].SetValue(obj, insertedId);
} }
@ -32,7 +101,7 @@ namespace TWASys_App.Dapper.AExtentions
insertPropertiesString = GetColumnsString(insertProperties, columns); insertPropertiesString = GetColumnsString(insertProperties, columns);
await connection.QueryFirstAsync($@" await connection.QueryFirstAsync($@"
INSERT INTO {FormatTableName(tableName)}({insertPropertiesString}) INSERT INTO {FormatTableName(tableName)}({insertPropertiesString})
VALUES ({GetColumnsString(insertProperties, columns, "@")})", obj); VALUES ({GetColumnsString(insertProperties, columns, "@")})", obj, tx);
} }
} }
@ -71,17 +140,24 @@ namespace TWASys_App.Dapper.AExtentions
{ {
return string.Join(prefix, properties.Select(p => $"{columnNames[p.Name]} = @{p.Name}")); return string.Join(prefix, properties.Select(p => $"{columnNames[p.Name]} = @{p.Name}"));
} }
private static string GetColumnsString(IEnumerable<PropertyInfo> properties, IReadOnlyDictionary<string, string> columnNames, string tablePrefix = null)
private static string GetColumnsString(IEnumerable<PropertyInfo> properties, IReadOnlyDictionary<string, string> columnNames, string tbName, string tablePrefix = "")
{ {
if (tbName != string.Empty) tbName = "tb" + tbName + "_";
if (tablePrefix == "target.") if (tablePrefix == "target.")
{ {
return string.Join(", ", properties.Select(property => $"{tablePrefix}{columnNames[property.Name]} as {property.Name}")); return string.Join(", ", properties.Select(property => $"{tablePrefix}{columnNames[property.Name]} as {property.Name}"));
} }
else if(tablePrefix == "@") else if(tablePrefix == "@")
{ {
return string.Join(", ", properties.Select(property => $"{tablePrefix}{property.Name}")); return string.Join(", ", properties.Select(property => $"{tablePrefix}{tbName + property.Name}"));
} }
return string.Join(", ", properties.Select(property => $"{tablePrefix}{columnNames[property.Name]}")); return string.Join(", ", properties.Select(property => $"{tablePrefix}{tbName + columnNames[property.Name].Replace("__", "")}"));
}
private static string GetColumnsString(IEnumerable<PropertyInfo> properties, IReadOnlyDictionary<string, string> columnNames, string tablePrefix = null)
{
return GetColumnsString(properties, columnNames, "", tablePrefix);
} }
private static string FormatTableName(string table) private static string FormatTableName(string table)
{ {

View File

@ -1,4 +1,6 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -7,10 +9,12 @@ namespace TWASys_App.Dapper.AExtentions
public class PropertiesCache public class PropertiesCache
{ {
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new(); private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> FKProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new(); private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ComputedProperties = new(); private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ComputedProperties = new();
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IReadOnlyDictionary<string, string>> ColumnNames = new(); private static readonly ConcurrentDictionary<RuntimeTypeHandle, IReadOnlyDictionary<string, string>> ColumnNames = new();
public static List<PropertyInfo> TypePropertiesCache(Type type) public static List<PropertyInfo> TypePropertiesCache(Type type)
{ {
if (TypeProperties.TryGetValue(type.TypeHandle, out var cachedProps)) if (TypeProperties.TryGetValue(type.TypeHandle, out var cachedProps))
@ -60,6 +64,28 @@ namespace TWASys_App.Dapper.AExtentions
return result; return result;
} }
public static bool IsPkAutoIncrement(PropertyInfo p)
{
// ===== Fallback by-name (nếu không thể tham chiếu typed)
var attrs = p.GetCustomAttributes(true);
bool hasKeyByName = attrs.Any(a => a.GetType().Name is "KeyAttribute");
var dbgenByName = attrs.FirstOrDefault(a => a.GetType().Name is "DatabaseGeneratedAttribute");
bool isIdentityByName = false;
if (dbgenByName != null)
{
// Tìm property "DatabaseGeneratedOption" (enum) và đọc giá trị "Identity"
var prop = dbgenByName.GetType().GetProperty("DatabaseGeneratedOption");
var val = prop?.GetValue(dbgenByName)?.ToString(); // ví dụ "Identity"
isIdentityByName = string.Equals(val, "Identity", StringComparison.OrdinalIgnoreCase);
}
if (hasKeyByName && isIdentityByName) return true;
return false;
}
public static List<PropertyInfo> KeyPropertiesCache(Type type) public static List<PropertyInfo> KeyPropertiesCache(Type type)
{ {
if (KeyProperties.TryGetValue(type.TypeHandle, out var cachedProps)) if (KeyProperties.TryGetValue(type.TypeHandle, out var cachedProps))
@ -68,21 +94,44 @@ namespace TWASys_App.Dapper.AExtentions
} }
var allProperties = TypePropertiesCache(type); var allProperties = TypePropertiesCache(type);
var keyProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a.GetType().Name == "KeyAttribute")).ToList(); var keyProperties = allProperties.Where(IsPkAutoIncrement).ToList();
KeyProperties[type.TypeHandle] = keyProperties; // ghi cache khi miss
if (keyProperties.Count == 0)
{
var idProp = allProperties.Find(p => string.Equals(p.Name, "id", StringComparison.CurrentCultureIgnoreCase));
if (idProp != null)
{
keyProperties.Add(idProp);
}
}
KeyProperties[type.TypeHandle] = keyProperties;
return keyProperties; return keyProperties;
} }
public static List<PropertyInfo> FKPropertiesCache(Type type)
{
if (FKProperties.TryGetValue(type.TypeHandle, out var cachedProps))
{
return cachedProps.ToList();
}
var allProperties = TypePropertiesCache(type);
var keyProperties = allProperties.Where(HasForeignKeyAttribute).ToList();
FKProperties[type.TypeHandle] = keyProperties;
return keyProperties;
}
static bool HasForeignKeyAttribute(PropertyInfo p)
{
if (p.GetCustomAttribute<ForeignKeyAttribute>() != null) return true;
// FK đặt trên navigation và chỉ ra tên prop FK
var t = p.DeclaringType!;
foreach (var nav in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
// Fallback by-name nếu không tham chiếu DataAnnotations (tùy dự án)
var attr = nav.GetCustomAttributes(true).FirstOrDefault(a => a.GetType().Name == "ForeignKeyAttribute");
if (attr != null)
{
var val = attr.GetType().GetProperty("Name")?.GetValue(attr);
if (val == null && string.IsNullOrWhiteSpace(val?.ToString()))
return true;
}
}
return false;
}
public static List<PropertyInfo> ComputedPropertiesCache(Type type) public static List<PropertyInfo> ComputedPropertiesCache(Type type)
{ {
if (ComputedProperties.TryGetValue(type.TypeHandle, out var cachedProps)) if (ComputedProperties.TryGetValue(type.TypeHandle, out var cachedProps))
@ -95,7 +144,7 @@ namespace TWASys_App.Dapper.AExtentions
return computedProperties; return computedProperties;
} }
private static IReadOnlyDictionary<string, string> GetColumnNames(IEnumerable<PropertyInfo> props) public static IReadOnlyDictionary<string, string> GetColumnNames(IEnumerable<PropertyInfo> props)
{ {
var ret = new Dictionary<string, string>(); var ret = new Dictionary<string, string>();
foreach (var prop in props) foreach (var prop in props)

View File

@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Text.Encodings.Web;
namespace TWASys_App.Models.Login
{
public sealed class LoginCheckFilter : IAsyncActionFilter, IOrderedFilter
{
public int Order => int.MinValue;
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var http = context.HttpContext;
// 1) Bỏ qua nếu endpoint cho phép anonymous
var endpoint = http.GetEndpoint();
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null ||
context.Filters.OfType<IAllowAnonymousFilter>().Any())
{
await next();
return;
}
// 2) Nếu chưa đăng nhập → redirect (web) hoặc 401 (API/Ajax)
var uid = http.Session.GetString("userID");
if (string.IsNullOrEmpty(uid))
{
// API/Ajax → trả 401 JSON; Web → redirect login
var accepts = http.Request.Headers.Accept.ToString();
var isApi = accepts.Contains("application/json", StringComparison.OrdinalIgnoreCase);
if (isApi)
{
context.Result = new JsonResult(new MessageHeader
{
ID = 1000,
Message = "Required Login",
Status = 0
});
}
else
{
var returnUrl = UrlEncoder.Default.Encode(http.Request.Path + http.Request.QueryString);
context.Result = new RedirectResult($"/login?returnUrl={returnUrl}");
}
return;
}
await next();
}
}
}

View File

@ -1,4 +1,6 @@
namespace TWASys_App.Models using MySqlConnector;
namespace TWASys_App.Models
{ {
public abstract class ModelBase: IPaging public abstract class ModelBase: IPaging
{ {
@ -12,6 +14,6 @@
public int PageNumber { get; set; } public int PageNumber { get; set; }
public int MaxRow { get; set; } public int MaxRow { get; set; }
protected MySqlConnection _connection = null!;
} }
} }

View File

@ -0,0 +1,12 @@
namespace TWASys_App.Models
{
public class Shared
{
private static List<string> _list = new List<string> { "80633.jpg", "806372.jpg", "806459.jpg" };
public static string RandomImages()
{
var r = new Random();
return _list[r.Next(0, _list.Count - 1)];
}
}
}

View File

@ -0,0 +1,159 @@

using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Microsoft.VisualBasic;
using MySqlConnector;
using System.Data.Common;
using System.Xml.Linq;
using TWASys_App.Dapper.AExtentions;
using TWASys_App.DBModels;
namespace TWASys_App.Models
{
public class StorageModel : ModelBase
{
public long StorageTypeID { set; get; }
public string ControllerName { set; get; } = "";
public string StorageName { set; get; } = "";
public ICollection<ValidationDomain> ValidationDomains { set; get; } = new List<ValidationDomain>();
public string IpPublic { set; get; } = "";
public string IpPrivatev4 { set; get; } = "";
public string IpPrivatev6 { set; get; } = "";
public string OsName { set; get; } = "";
public string OsPlatform { set; get; } = "";
public string OsVersion { set; get; } = "";
public string OsKernel { set; get; } = "";
public string OsArch { set; get; } = "";
public string BiosSN { set; get; } = "";
public string BiosVendor { set; get; } = "";
public string BiosUUID { set; get; } = "";
public int SocketN { set; get; }
public string CpuName { set; get; } = "";
public float TotalRam { set; get; }
public string TokenID { set; get; } = "";
public string TokenValue { set; get; } = "";
public string RTokenID { set; get; } = "";
public string RTokenValue { set; get; } = "";
public override async Task<MessageHeader> AddAsync()
{
await using var con = new MySqlConnector.MySqlConnection(DBManagement.GetConnectionString(true));
await con.OpenAsync();
var trans = await con.BeginTransactionAsync();
var f = new MessageHeader();
try
{
var localServer = new LocalServer
{
IpPrivateServer = IpPrivatev4,
IpPrivateServerv6 = IpPrivatev6,
IpPublicServer = IpPublic,
PathServer = "",
SerialNumber = BiosSN,
OsVersion = OsVersion,
OsName = OsName,
OsArch = OsArch,
OsKernal = OsKernel,
SocketNum = SocketN,
CpuName = CpuName,
TotalRam = TotalRam,
BiosVender = BiosVendor,
ProductUuid = BiosUUID,
Status = 0
};//idTypeStorageServer
var storageServer = new StorageServer
{
IdEmp = null,
IdTypeStorageServer = StorageTypeID,
StorageName = StorageName,
CreateDate = DateTime.UtcNow,
ControllerID = ControllerName,
Status = 0
};
var ss_lc = new StorageServer_has_LocalServer
{
IdStorageServer = storageServer.IdStorageServer,
IdLocalServer = localServer.IdLocalServer,
CreateDate = DateTime.UtcNow,
ModifyDate = null,
Status = 0
};
var serverAuthorization = new ServerAuthorization
{
IdStorageServer = storageServer.IdStorageServer,
CreateDate = DateTime.UtcNow,
Count = 1,
Status = 0
};
var token = new Token
{
IdToken = TokenID,
AccessToken = TokenValue,
CreateDate = DateTime.UtcNow,
ExpireDate = DateTime.UtcNow.AddMonths(3),
Status = 0
};
var rT = new RefreshToken
{
IdRefreshToken = RTokenID,
IdServerAuthorization = serverAuthorization.IdServerAuthorization,
__RefreshToken = RTokenValue,
CreateDate = DateTime.UtcNow,
ExpireDate = DateTime.UtcNow.AddMonths(9),
Status = 0
};
BatchInsert bi = new BatchInsert(con, trans);
bi.AddRow(localServer);
bi.AddRow(storageServer);
bi.AddRow(ss_lc);
bi.AddRow(serverAuthorization);
bi.AddRow(token);
bi.AddRow(rT);
await bi.ExcuteQuery();
await trans.CommitAsync();
f.Status = 1;
f.Message = "OK";
}
catch (DbException ex)
{
await trans.RollbackAsync();
await trans.DisposeAsync();
f.Status = 0;
f.Message = ex.Message;
f.ID = 61031;
}
return f;
}
public override Task<MessageHeader> DeleteAsync()
{
throw new NotImplementedException();
}
public override Task<MessageHeader> UpdateAsync()
{
throw new NotImplementedException();
}
}
}

View File

@ -1,8 +1,29 @@
using AppLibs.Libs;
using AppLibs.Libs; using Microsoft.AspNetCore.Http.Features;
using System.Net;
using System.Text;
await WSNavigation.LoadJson(); await WSNavigation.LoadJson();
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<FormOptions>(o => {
o.MultipartBodyLengthLimit = 200_000_000; // 200 MB
});
builder.Services.AddControllers(options =>
{
options.Filters.Add<AsyncGateAttribute>();
}).AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
builder.Services.AddDistributedMemoryCache(); // IDistributedCache in-memory
builder.Services.AddSession(o =>
{
o.IdleTimeout = TimeSpan.FromHours(8);
o.Cookie.HttpOnly = true;
o.Cookie.IsEssential = true;
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
builder.Services.AddControllersWithViews().AddRazorRuntimeCompilation(); builder.Services.AddControllersWithViews().AddRazorRuntimeCompilation();
// Add services to the container. // Add services to the container.
@ -23,11 +44,10 @@ app.UseRouting();
app.UseAuthorization(); app.UseAuthorization();
app.MapStaticAssets(); app.MapStaticAssets();
app.UseSession();
app.MapControllerRoute( app.MapControllerRoute(
name: "default", name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}") pattern: "{controller=Home}/{action=Index}/{id?}")
.WithStaticAssets(); .WithStaticAssets();
app.Run(); app.Run();

View File

@ -1,14 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net9.0-windows10.0.19041.0</TargetFrameworks> <TargetFrameworks>net9.0-windows;net9.0</TargetFrameworks>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>TWASys_App</RootNamespace> <RootNamespace>TWASys_App</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="$([System.String]::Copy('$(TargetFramework)').Contains('-windows'))">
<PackageReference Include="System.Management" Version="9.0.0" /> <ItemGroup Condition="'$(TargetFramework)'=='net9.0-windows'">
<PackageReference Include="System.Management" Version="9.0.10" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net9.0-windows'">
<DefineConstants>$(DefineConstants);WINDOWS</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="wwwroot\css\atg-font\atg-admin-font.css" /> <None Include="wwwroot\css\atg-font\atg-admin-font.css" />
<None Include="wwwroot\css\atg-font\atg-font.css" /> <None Include="wwwroot\css\atg-font\atg-font.css" />
@ -23,10 +29,6 @@
<None Include="wwwroot\css\site.css" /> <None Include="wwwroot\css\site.css" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AppLibs\AppLibs\AppLibs.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper" Version="2.1.66" /> <PackageReference Include="Dapper" Version="2.1.66" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.10" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.10" />
@ -35,6 +37,10 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AppLibs\AppLibs\AppLibs.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Keys\db\ca-cert.pem"> <None Update="Keys\db\ca-cert.pem">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>

View File

@ -6,3 +6,7 @@
<h1 class="display-4">Welcome</h1> <h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div> </div>
@section jsLib {
<script type="module" src="@Url.AbsoluteContent("~/js/js-page/1000.js")" js-lib></script>
}

View File

@ -1,43 +1,122 @@
<div class="c"> <div class="main-login">
<div class="r j-c-center h-100vh pt-5 pb-5"> <div class="con-img">
<div class="c-s-12 c-m-10 c-l-8 c-x-10 c-sx-8 w-login mt-auto mb-auto"> <img src="~/images/login/@Shared.RandomImages()" />
<div class="r-n-g j-c-center a-i-center">
<div class="d-n c-x-6 d-x-b">
<img src="~/images/img1.jpg" class="w-100" />
</div> </div>
<div class="c-12 c-x-6"> <div class="c-login">
<div class="h-100 d-f a-i-center"> <div class="frm-login">
<div class="ws-login d-f f-c a-i-center"> <div class="ws-login">
<span class="ws-title">Đăng Nhập</span> <div class="frm-header d-f f-c a-i-center">
<div class="ws-input mb-4"> <a app-nav href="@Url.AbsoluteContent("~/")" class="c_logo d-f j-c-center">
<span class="label-input">Tài khoản</span> <img src="@Url.AbsoluteContent("~/images/logo/slogo.png")">
<div class="c-input d-f a-i-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M313.6 304c-28.7 0-42.5 16-89.6 16-47.1 0-60.8-16-89.6-16C60.2 304 0 364.2 0 438.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-25.6c0-74.2-60.2-134.4-134.4-134.4zM400 464H48v-25.6c0-47.6 38.8-86.4 86.4-86.4 14.6 0 38.3 16 89.6 16 51.7 0 74.9-16 89.6-16 47.6 0 86.4 38.8 86.4 86.4V464zM224 288c79.5 0 144-64.5 144-144S303.5 0 224 0 80 64.5 80 144s64.5 144 144 144zm0-240c52.9 0 96 43.1 96 96s-43.1 96-96 96-96-43.1-96-96 43.1-96 96-96z" /></svg> </a>
<input type="text" class="input" placeholder="Nhập tài khoản" />
</div>
<div class="AWizard tabcontents">
<div class="tabcontent">
<div class="frm-header">
<h2 class="title">Sign In</h2>
<span class="d-f f-wrap desc">Bạn chưa có tài khoản? <a href="javascript:void(0)" class="ml-2" id="navSignUp">Đăng ký ngay</a></span>
</div>
<div class="frm-input mt-4">
<label>Username</label>
<div class="c-input">
<span class="ico atg a-1x atg-user"></span>
<input type="text" class="input" placeholder="Your username" />
<span class="line"></span>
</div> </div>
</div> </div>
<div class="ws-input"> <div class="frm-input">
<span class="label-input">Mật khẩu</span> <label>Password</label>
<div class="c-input d-f a-i-center"> <div class="c-input">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zM264 392c0 22.1-17.9 40-40 40s-40-17.9-40-40v-48c0-22.1 17.9-40 40-40s40 17.9 40 40v48zm32-168H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z" /></svg> <span class="ico atg a-1x atg-user"></span>
<input type="password" class="input" placeholder="Your password" />
<span class="line"></span>
</div>
</div>
<div class="frm-header d-f f-wrap j-c-between a-i-center">
<a href="javascript:void(0)" id="forgetPass">Forget the password?</a>
<button class="btn btn-primary btn-effect">Sign In</button>
</div>
</div>
<div class="tabcontent">
<div class="frm-header">
<h2 class="title">Don't You Remember Password?</h2>
</div>
<div class="frm-input mt-4">
<label>Địa chỉ email</label>
<div class="c-input">
<span class="ico atg a-1x atg-user"></span>
<input type="text" class="input" placeholder="Nhập email" />
<span class="line"></span>
</div>
</div>
<div class="frm-header d-f j-c-end">
<button class="btn btn-secondary btnCB btn-effect mr-2">Quay lại</button>
<button class="btn btn-primary btn-effect">Lấy lại mật khẩu</button>
</div>
<div class="frm-header d-f f-c">
<span class="hint">
Mật khẩu mới sẽ được gửi về hộp thư trong email của bạn.
</span>
<span class="hint mt-2">
Sau khi được cấp mật khẩu mới vui lòng đổi lại mật khẩu trong trang thông tin tài khoản.
</span>
<span class="mt-2 hint">Trường hợp không nhận được email? <a href="javascript:void(0)" class="">Hãy nhắn tin cho chúng tôi!</a></span>
</div>
</div>
<div class="tabcontent">
<div class="frm-header">
<h2 class="title">Sign Up</h2>
</div>
<div class="frm-input mt-4">
<label>Địa chỉ email</label>
<div class="c-input">
<span class="ico atg a-1x atg-user"></span>
<input type="text" class="input" placeholder="Nhập email" />
<span class="line"></span>
</div>
</div>
<div class="frm-input">
<label>Mật khẩu</label>
<div class="c-input">
<span class="ico atg a-1x atg-user"></span>
<input type="password" class="input" placeholder="Nhập mật khẩu" /> <input type="password" class="input" placeholder="Nhập mật khẩu" />
<span class="line"></span>
</div> </div>
</div> </div>
<div class="ws-forgot ml-auto"> <div class="frm-input">
<a href="#">Quên mật khẩu?</a> <label>Nhập lại mật khẩu</label>
<div class="c-input">
<span class="ico atg a-1x atg-user"></span>
<input type="password" class="input" placeholder="Nhập lại mật khẩu" />
<span class="line"></span>
</div> </div>
<div class="ws-btn d-f j-c-center">
<div class="btn-mask"></div>
<button class="btn btn-effect">Đăng nhập</button>
</div>
<div class="ws-signup d-f f-c a-i-center">
<span>Đăng ký tài khoản bằng ID</span>
<a href="#">Đăng Ký</a>
</div> </div>
<div class="frm-input">
<label>Họ & tên</label>
<div class="c-input">
<span class="ico atg a-1x atg-user"></span>
<input type="text" class="input" placeholder="Nhập họ tên" />
<span class="line"></span>
</div>
</div>
<div class="frm-header d-f j-c-end">
<button class="btn btn-secondary btnCB btn-effect mr-2">Quay lại</button>
<button class="btn btn-primary btn-effect">Đăng ký</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@section jsLib {
<script type="module" src="@Url.AbsoluteContent("~/js/js-page/10001.js")" js-lib></script>
}

View File

@ -1,7 +1,7 @@
<div class="c-logo"> <div class="c-logo">
<div class="logo d-f f-c a-i-center"> <div class="logo d-f f-c a-i-center">
<img src="~/images/logo3.svg" /> <img src="~/images/logo/slogo.png" />
<span class="mt-1 logo-name">Resort Management</span> <span class="pt-3 pb-2 logo-name">Management System</span>
</div> </div>
<div class="d-b d-x-n hd-close"> <div class="d-b d-x-n hd-close">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24" stroke="#7367f0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="d-block d-xl-none feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24" stroke="#7367f0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="d-block d-xl-none feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
@ -18,7 +18,7 @@
</div> </div>
<div class="m-footer d-f f-c"> <div class="m-footer d-f f-c">
<span> <span>
<b>Copyright<sup>©</sup></b> 2024 ATG Technology <b>Copyright<sup>©</sup></b> 2025 TWA
</span> </span>
<span class="mt-1"><b>Version: </b> 0.0.1a</span> <span class="mt-1"><b>Version: </b> 0.0.1a</span>
</div> </div>

View File

@ -62,6 +62,7 @@
<div id="footer"> <div id="footer">
@await Html.PartialAsync("~/Views/Partial/Footer.cshtml") @await Html.PartialAsync("~/Views/Partial/Footer.cshtml")
</div> </div>
@section jsLib { @section jsLib {
<script type="module" src="@Url.AbsoluteContent("~/js/js-page/asyncLayout.js")" js-lib></script> <script type="module" src="@Url.AbsoluteContent("~/js/js-page/asyncLayout.js")" js-lib></script>
} }

View File

@ -0,0 +1,9 @@
<section id="header">
</section>
<section id="footer">
</section>
@section jsLib {
<script type="module" src="@Url.AbsoluteContent("~/js/js-page/asyncLoginLayout.js")" js-lib></script>
}

View File

@ -23,7 +23,7 @@
user-select: none user-select: none
} }
a { a {,
text-decoration: none text-decoration: none
} }
@ -197,6 +197,11 @@ h1, h2, h3, h4, h5, h6, p {
margin-left: 0; margin-left: 0;
} }
.r-n-g.n{
margin-left:-20px;
margin-right:-20px
}
.r > .c, .r > [class^=c-] { .r > .c, .r > [class^=c-] {
padding-right: 0; padding-right: 0;
padding-left: 0; padding-left: 0;

View File

@ -0,0 +1,165 @@
.con-img {
width: 100%;
min-height: 100%;
overflow-x: hidden;
}
.con-img > img {
height: 100%;
width: auto;
min-height: 100vw;
}
@media (min-width: 1400px) {
.con-img > img {
width: 100% !important;
max-width: 100vw;
height: auto;
min-height: auto;
max-height: 100vh;
}
}
.c-login {
position: absolute;
width: 100%;
transform: translateY(-50%);
top: 50%;
right: 0;
margin: 0 auto
}
.frm-login {
max-width: 100%;
margin: 0 25px;
}
@media (min-width: 568px) {
.frm-login {
max-width: 380px !important;
margin: 0 auto !important;
}
}
@media (min-width: 768px) {
.c-login {
right: 100px;
width: 420px;
}
.frm-login {
max-width: 100% !important
}
}
.ws-login {
box-shadow: 0 2px 3px rgba(96, 96, 96, 0.1);
background-color: #fff;
border-radius: var(--radius);
padding: 55px 35px;
}
.ws-login .c_logo {
height: 60px
}
.ws-login .title {
color: var(--color-primary)
}
.ws-login .ico {
position: absolute;
top: 40%;
transform: translateY(-50%)
}
.ws-login input {
padding-left: 25px;
}
.ws-login .title, .ws-login .desc {
}
.ws-login .hint {
font-size: .82rem;
}
.frm-inline.search button {
padding: 4px 15px;
}
.frm-inline button {
margin-left: calc(var(--radius) * -1);
}
.frm-inline input {
border-radius: var(--radius);
border: 1px solid var(--border-color);
padding: 10px 12px;
width: 100%;
font-weight: 500;
color: var(--text-color-primary)
}
.frm-group {
margin-left: -15px;
margin-right: -15px;
}
.frm-input, .frm-header {
padding: 0 15px;
margin-bottom: 25px;
}
.frm-input {
font-weight: 500;
}
.frm-input label {
color: var(--text-color-primary);
font-size: .92rem;
margin-bottom: 2px;
font-weight: 500
}
.frm-input input, .frm-input .aselect {
width: 100%;
border-bottom: 1px solid #444C6185;
background-color: #fff;
transition: .3s all ease-in-out;
font-size: .9rem;
font-weight: 400
}
.frm-input .con-aselect input {
border-radius: var(--radius);
padding: 0 15px;
border: 1px solid #444C6185
}
.frm-input input {
min-height: 35px;
line-height: 35px;
}
.frm-input .line {
position: absolute;
height: 1px;
bottom: 0;
left: 0;
z-index: 2;
width: 0;
background-color: var(--color-primary);
transition: width .4s cubic-bezier( 0.19, 0.6, 0.86, 0.01 )
}
.frm-input input:focus + .line, .frm-input input:hover + .line {
width: 100%;
}
.frm-input .c-input:not(.line) input:hover, .frm-input .c-input:not(.line) input:focus, .frm-input .con-aselect.active .aselect {
border-color: var(--color-primary);
}

View File

@ -8,13 +8,14 @@
--text-color-primary: #09205c; --text-color-primary: #09205c;
--text-color-1: #5e5873; --text-color-1: #5e5873;
--text-color-heading-1: #5d596c; --text-color-heading-1: #5d596c;
--text-disable: #a5a3ae --text-disable: #a5a3ae;
--radius:10px
} }
.pointer:hover{ .pointer:hover{
cursor: pointer cursor: pointer
} }
.mini-scrollbar{ .mini-scrollbar{
height: calc(100vh - 176px) height: calc(100vh - 196px)
} }
.ellipsis { .ellipsis {
@ -26,6 +27,17 @@ html {
font-size: 18px; font-size: 18px;
font-weight: 500; font-weight: 500;
} }
a{
text-decoration: none
}
body[page="10001"] .main-wrapper{
padding: 0 !important;
margin: 0
}
body[page="10001"] #header {
left:-500px
}
/*Control*/ /*Control*/
[data-dropdown]:not(.nav-i) .sub-item { [data-dropdown]:not(.nav-i) .sub-item {
border-radius: var(--border-radius); border-radius: var(--border-radius);
@ -208,7 +220,8 @@ nav {
} }
.menu-content { .menu-content {
height: calc(100% - 172px) padding-top:20px;
height: calc(100% - 176px)
} }
.m-footer { .m-footer {
@ -219,12 +232,12 @@ nav {
} }
.nav-overlay { .nav-overlay {
margin-top: -.7rem; margin-top: -10px;
background: -webkit-gradient(linear,left top,left bottom,color-stop(41%,#fff),color-stop(95%,hsla(0,0%,100%,.11)),to(hsla(0,0%,100%,0))); background: -webkit-gradient(linear,left top,left bottom,color-stop(41%,#fff),color-stop(95%,hsla(0,0%,100%,.11)),to(hsla(0,0%,100%,0)));
background: linear-gradient(#fff 41%,hsla(0,0%,100%,.11) 95%,hsla(0,0%,100%,0)); background: linear-gradient(#fff 41%,hsla(0,0%,100%,.11) 95%,hsla(0,0%,100%,0));
position: absolute; position: absolute;
z-index: 2; z-index: 2;
height: 50px; height: 30px;
width: 100%; width: 100%;
pointer-events: none; pointer-events: none;
-webkit-filter: blur(5px); -webkit-filter: blur(5px);
@ -574,7 +587,7 @@ nav {
} }
.btn-secondary { .btn-secondary {
color: #a8aaae; color: #5e5873;
border-color: rgba(0, 0, 0, 0); border-color: rgba(0, 0, 0, 0);
background: #f1f1f2; background: #f1f1f2;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,4 @@
window.L1000 = function () {
}
window.AScript.set("1000", true);

View File

@ -0,0 +1,26 @@
import AWizard from "/js/libs/js-AWizard.js";
window.L10001 = function () {
Waves.attach('.btn-effect', ['waves-float']);
Waves.init({ duration: 1000, delay: 200 });
var wrd1 = new AWizard(document.querySelector(".AWizard"));
const forget = document.getElementById("forgetPass");
const btnCB = document.querySelectorAll(".btnCB");
const navSignUp = document.getElementById("navSignUp")
forget.addEventListener("click", (e) => {
wrd1.nextPage(1);
});
btnCB.forEach((el) => {
el.addEventListener("click", (e) => {
ComeBack(e, wrd1);
});
});
navSignUp.addEventListener("click", (e) => {
wrd1.nextPage(2);
});
}
function ComeBack(e, wrd) {
wrd.nextPage(0);
}
window.AScript.set("10001", true);

View File

@ -38,9 +38,15 @@ const tmp = `<div class="slider-scrollbar" data-scrollbar>
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="pathServer">Controller Name</label> <label for="controllerName">Controller Name</label>
<div class="input-group"> <div class="input-group">
<input id="pathServer" type="text" placeholder="Controller Name" validation-isEmpty /> <input id="controllerName" type="text" placeholder="Controller Name" validation-isEmpty />
</div>
</div>
<div class="form-group d-f f-c">
<label for="storageName">Storage Name</label>
<div class="input-group">
<input id="storageName" type="text" placeholder="Storage Name" validation-isEmpty />
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
@ -103,9 +109,9 @@ const tmp = `<div class="slider-scrollbar" data-scrollbar>
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="expiredDate">SharePoint Path</label> <label for="sharepointPath">SharePoint Path</label>
<div class="input-group"> <div class="input-group">
<input id="expiredDate" type="password" placeholder="Client ID" validation-isEmpty /> <input id="sharepointPath" type="text" placeholder="Client ID" validation-isEmpty />
</div> </div>
</div> </div>
</div> </div>
@ -140,7 +146,7 @@ const tmp = `<div class="slider-scrollbar" data-scrollbar>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="clientID">OS Platform</label> <label for="clientID">OS Platform</label>
<div class="input-group"> <div class="input-group">
<input id="osName" data-id="osPlatform" type="text" placeholder="OS Platform" validation-isEmpty /> <input id="osPlatform" data-id="osPlatform" type="text" placeholder="OS Platform" validation-isEmpty />
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
@ -158,7 +164,7 @@ const tmp = `<div class="slider-scrollbar" data-scrollbar>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="clientID">OS Architecture</label> <label for="clientID">OS Architecture</label>
<div class="input-group"> <div class="input-group">
<input id="osKernel" data-id="osArch" type="text" placeholder="OS Architecture" validation-isEmpty /> <input id="osArch" data-id="osArch" type="text" placeholder="OS Architecture" validation-isEmpty />
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
@ -203,30 +209,51 @@ const tmp = `<div class="slider-scrollbar" data-scrollbar>
<h5 class="mb-2">Server Authenicator</h4> <h5 class="mb-2">Server Authenicator</h4>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="clientID">IP Server</label> <label for="idToken">Token ID</label>
<div class="input-group"> <div class="input-group">
<input id="clientID" type="text" placeholder="Client ID" validation-isEmpty /> <input id="idToken" type="text" placeholder="Token ID" validation-isEmpty />
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="clientID">Machine ID</label> <label for="vToken">Token Value</label>
<div class="input-group"> <div class="input-group">
<input id="clientID" type="text" placeholder="Client ID" validation-isEmpty /> <input id="vToken" type="password" placeholder="Token Value" validation-isEmpty />
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="clientID">OS Version</label> <label for="idRefreshToken">Refresh Token ID</label>
<div class="input-group"> <div class="input-group">
<input id="clientID" type="text" placeholder="Client ID" validation-isEmpty /> <input id="idRefreshToken" type="text" placeholder="Refresh Token ID" validation-isEmpty />
</div> </div>
</div> </div>
<div class="form-group d-f f-c"> <div class="form-group d-f f-c">
<label for="clientID">Proccessor ID</label> <label for="refreshTokenValue">Refresh Token Value</label>
<div class="input-group"> <div class="input-group">
<input id="clientID" type="text" placeholder="Client ID" validation-isEmpty /> <input id="refreshTokenValue" type="password" placeholder="Refresh Token Value" validation-isEmpty />
</div> </div>
</div> </div>
</div> </div>
<div class="tabcontent">
<div class="form-group">
<h5 class="mb-2">Storage Data Summary</h4>
</div>
<div class="form-group mb-0 d-f">
<label>Storage Server Type</label>
<span class="ml-2"></span>
</div>
<div class="form-group mb-0 d-f">
<label>Controller Name</label>
<span class="ml-2"></span>
</div>
<div class="form-group mb-0 d-f">
<label>Storage Name</label>
<span class="ml-2"></span>
</div>
<div class="form-group mb-0 d-f">
<label>Validation Domains</label>
<span class="ml-2"></span>
</div>
</div>
</div> </div>
</div> </div>
<div class="tabcontent"> <div class="tabcontent">
@ -262,24 +289,99 @@ window.L6101 = function () {
var ov = new AOverlay(document.body); var ov = new AOverlay(document.body);
ov.createOverlay(); ov.createOverlay();
const $v = id => (document.getElementById(id)?.value ?? '').trim();
var ele1 = new ASpinButton(document.getElementById("totalSize")); var ele1 = new ASpinButton(document.getElementById("totalSize"));
var ele2 = new AMultiTag(document.getElementById("listStorage")); var ele2 = new AMultiTag(document.getElementById("listStorage"));
var slider = new AModal(tmp); var slider = new AModal(tmp);
slider.createModal("CustomForm", "slider-right"); slider.createModal("CustomForm", "slider-right");
var Scrollbar = window.Scrollbar; var Scrollbar = window.Scrollbar;
Scrollbar.init(slider.CustomContainer.querySelector(".slider-scrollbar"), window.scroll_options); const mScroll = Scrollbar.init(slider.CustomContainer.querySelector(".slider-scrollbar"), window.scroll_options);
slider.overlay.isCloseOverlay(true); slider.overlay.isCloseOverlay(true);
var tabs = new ATab(slider.CustomContainer.querySelector(".atabs"), slider.CustomContainer.querySelector(".StorageTabs")); var tabs = new ATab(slider.CustomContainer.querySelector(".atabs"), slider.CustomContainer.querySelector(".StorageTabs"));
var wrd1 = new AWizard(slider.CustomContainer.querySelector(".AWizard")); var wrd1 = new AWizard(slider.CustomContainer.querySelector(".AWizard"));
var as = new ASelect(slider.CustomContainer.querySelectorAll(".aselect")); var as = new ASelect(slider.CustomContainer.querySelectorAll(".aselect"));
window.testV = as;
var asIdType = as.get(0); var asIdType = as.get(0);
wrd1.on("onBeforeBack", (evt) => {
mScroll.scrollTo(0, 0);
});
wrd1.on("onAfterNext", (evt) => {
if (evt.indexPage > 0) {
if (as.get(0).isLockElement() || as.get(1).isLockElement()) {
return;
}
switch (asIdType.getSelectedItem().vSearch) {
case "Microsoft Cloud":
break;
case "Physical Server":
if (evt.indexPage > 3) {
(async () => {
const fd = new FormData();
fd.append('StorageTypeID', asIdType.getSelectedItem()?.id ?? '');
fd.append('ControllerName', $v('controllerName'));
fd.append('StorageName', $v('storageName'));
fd.append('IpPublic', $v('ipPublic'));
fd.append('IpPrivatev4', $v('ipPrivatev4'));
fd.append('IpPrivatev6', $v('ipPrivatev6'));
fd.append('OsName', $v('osName'));
fd.append('OsPlatform', $v('osPlatform'));
fd.append('OsVersion', $v('osVersion'));
fd.append('OsKernel', $v('osKernel'));
fd.append('OsArch', $v('osArch'));
fd.append('BiosSN', $v('biosSerial'));
fd.append('BiosVendor', $v('biosVender'));
fd.append('BiosUUID', $v('biosUUID'));
const i = 0;
as.get(1).multiSelectedItem.forEach((item) => {
fd.append(`ValidationDomains[${i}].IdValidationDomain`, item.getAttribute("data-value"));
});
fd.append('SocketN', $v('socketN'));
fd.append('CpuName', $v('cpuName'));
fd.append('TotalRam', $v('totalRam'));
fd.append('TokenID', $v('idToken'));
fd.append('TokenValue', $v('vToken'));
fd.append('RTokenID', $v('idRefreshToken'));
fd.append('RTokenValue', $v('refreshTokenValue'))
const response = await fetch("/Storage/SetStorageData", {
method: 'POST',
body: fd
});
})();
}
break;
}
}
});
wrd1.on("onBeforeNext", (evt) => { wrd1.on("onBeforeNext", (evt) => {
mScroll.scrollTo(0, 0);
if (evt.indexPage == 0) { if (evt.indexPage == 0) {
if (asIdType.isLockElement() && asVD.isLockElement()) { if (as.get(0).isLockElement() || as.get(1).isLockElement()) {
wrd1.isStopNextPage = true;
return;
} else {
wrd1.isStopNextPage = false;
}
let flag = false;
if ($v('controllerName').length == 0) {
flag = true;
}
if ($v('storageName').length == 0) {
flag = true;
}
if (as.get(1).multiSelectedItem.length == 0) {
flag = true;
}
if (flag) {
wrd1.isStopNextPage = true; wrd1.isStopNextPage = true;
return; return;
} else { } else {
@ -289,9 +391,11 @@ window.L6101 = function () {
case "Microsoft Cloud": case "Microsoft Cloud":
wrd1.showPage(1); wrd1.showPage(1);
wrd1.hidePage(2); wrd1.hidePage(2);
wrd1.hidePage(3);
break; break;
case "Physical Server": case "Physical Server":
wrd1.showPage(2); wrd1.showPage(2);
wrd1.showPage(3);
wrd1.hidePage(1); wrd1.hidePage(1);
let promise = new Promise(async function (resolve, reject) { let promise = new Promise(async function (resolve, reject) {
const response = await fetch(`/Storage/GetSysInfo`, { const response = await fetch(`/Storage/GetSysInfo`, {
@ -308,7 +412,40 @@ window.L6101 = function () {
}); });
break; break;
} }
}e } else if (evt.indexPage == 1)
{
}
else if (evt.indexPage == 2) {
(async () => {
const response = await fetch(`/Storage/GenerationAccessToken`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
if (data) {
document.getElementById("idToken").value = data.idToken;
document.getElementById("vToken").value = data.tokenValue;
}
})();
(async () => {
const response = await fetch(`/Storage/GenerationAccessToken`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
if (data) {
document.getElementById("idRefreshToken").value = data.idToken;
document.getElementById("refreshTokenValue").value = data.tokenValue;
}
})();
}
}, true); }, true);
var asVD = as.get(1); var asVD = as.get(1);
@ -321,6 +458,7 @@ window.L6101 = function () {
let promise = new Promise(function (resolve, reject) { let promise = new Promise(function (resolve, reject) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("GET", window.GetAbsoluteURL("/Storage/GetTypeStorage")); xhr.open("GET", window.GetAbsoluteURL("/Storage/GetTypeStorage"));
xhr.setRequestHeader('Accept', 'application/json; charset=UTF-8');
var f = function (ev) { var f = function (ev) {
if (ev.currentTarget.readyState == 4) { if (ev.currentTarget.readyState == 4) {
if (ev.currentTarget.status == 200) { if (ev.currentTarget.status == 200) {
@ -344,6 +482,7 @@ window.L6101 = function () {
let promise1 = new Promise(function (resolve, reject) { let promise1 = new Promise(function (resolve, reject) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("GET", window.GetAbsoluteURL("/Storage/GetValidationDomain")); xhr.open("GET", window.GetAbsoluteURL("/Storage/GetValidationDomain"));
xhr.setRequestHeader('Accept', 'application/json; charset=UTF-8');
var f = function (ev) { var f = function (ev) {
if (ev.currentTarget.readyState == 4) { if (ev.currentTarget.readyState == 4) {
if (ev.currentTarget.status == 200) { if (ev.currentTarget.status == 200) {
@ -374,7 +513,7 @@ window.L6101 = function () {
asAddStorage.on("click_btAddStorage", (e) => { asAddStorage.on("click_btAddStorage", (e) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", "/Storage/AddStorageServer"); xhr.open("POST", "/Storage/AddStorageServer");
xhr.setRequestHeader('Content-type', 'application/json; charset=UTF-8'); xhr.setRequestHeader('Accept', 'application/json; charset=UTF-8');
var data = { var data = {
"Name": document.getElementById("inpType").value "Name": document.getElementById("inpType").value
}; };

View File

@ -14,6 +14,7 @@ class AsyncLayout extends ALayout {
super.dispose(); super.dispose();
} }
renderMenu() { renderMenu() {
console.log("ÁDASD");
this.isLoaded = true; this.isLoaded = true;
var asyncStyleSheets = [ var asyncStyleSheets = [
'https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap', 'https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap',

View File

@ -0,0 +1,39 @@
import ALayout from '/js/libs/js-ALayout.js'
class LoginLayout extends ALayout {
constructor() {
super();
this.isLoaded = false;
}
dispose() {
this.isLoaded = false;
window.app.removeSytemEventParent(window.app.lName);
window.app.removeCustomEventParent(window.app.lName);
super.dispose();
}
renderMenu() {
this.isLoaded = true;
var asyncStyleSheets = [
'https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap',
'/css/atg-font/atg-admin-font.css',
'/css/atg-lib/waves.min.css',
'/css/pages/login.css'
];
window.app.loadCSS(asyncStyleSheets);
window.app.initNavs("Login");
var sOption = {
damping: (window.getOS() == "Android") ? .06 : .04,
thumbMinSize: 25,
renderByPixel: true,
alwaysShowTracks: true,
continuousScrolling: true
};
window.Scrollbar.use(window.OverscrollPlugin);
window.app.initScrollBar();
}
}
window.ALayout.set("Login", new LoginLayout());
window.AScript.set("asyncLoginLayout", true);

View File

@ -106,12 +106,12 @@ export default class AWizard extends ATab {
btnNext_Click(e, idx) { btnNext_Click(e, idx) {
this.trigger("onBeforeNext", { "currentPage": this.ctabs[idx], "indexPage": idx, "currentButton": e.currentTarget }); this.trigger("onBeforeNext", { "currentPage": this.ctabs[idx], "indexPage": idx, "currentButton": e.currentTarget });
if (this.isStopNextPage) return; if (this.isStopNextPage) return;
console.log(idx);
this.selectedTab(this.checkSelectedNext(idx)); this.selectedTab(this.checkSelectedNext(idx));
this.trigger("onAfterNext"); this.trigger("onAfterNext", { "currentPage": this.ctabs[idx], "indexPage": idx + 1, "currentButton": e.currentTarget });
} }
btnBack_Click(e, idx) { btnBack_Click(e, idx) {
this.trigger("onBeforeBack", { "currentPage": this.ctabs[idx], "indexPage": idx, "currentButton": e.currentTarget });
this.selectedTab(this.checkSelectedBack(idx)); this.selectedTab(this.checkSelectedBack(idx));
} }
btnFinish_Click(e) { btnFinish_Click(e) {

View File

@ -541,6 +541,7 @@ class AApp extends window.AObject {
this.metaPage = document.head.querySelector("meta[name=idPage]"); this.metaPage = document.head.querySelector("meta[name=idPage]");
this.pageName = this.metaPage.getAttribute("pageName"); this.pageName = this.metaPage.getAttribute("pageName");
this.lName = this.metaPage.getAttribute("layName"); this.lName = this.metaPage.getAttribute("layName");
console.log(this.lName);
var tmp = document.querySelector(container); var tmp = document.querySelector(container);
this.mainApp = tmp.querySelector("[main-content]") ? tmp.querySelector("[main-content]") : tmp; this.mainApp = tmp.querySelector("[main-content]") ? tmp.querySelector("[main-content]") : tmp;
var f = function (ev) { var f = function (ev) {
@ -688,6 +689,7 @@ class AApp extends window.AObject {
doc: doc, doc: doc,
dynamicF: a.dynamicF dynamicF: a.dynamicF
}; };
this.setContentPage(obj, url); this.setContentPage(obj, url);
} else { } else {
this.getPage(url, result) this.getPage(url, result)
@ -721,7 +723,9 @@ class AApp extends window.AObject {
document.title = page.title + " - " + this.pageName; document.title = page.title + " - " + this.pageName;
var meta = document.head.querySelector("meta[name=idPage]"); var meta = document.head.querySelector("meta[name=idPage]");
document.body.setAttribute("page", page.idPage); document.body.setAttribute("page", page.idPage);
meta.content = page.idPage; meta.content = page.idPage;
this.lName = page.lName;
meta.setAttribute("layName", page.lName); meta.setAttribute("layName", page.lName);
this.loadContentPage(page.html); this.loadContentPage(page.html);
window.history.pushState({"url":url}, page.title, url); window.history.pushState({"url":url}, page.title, url);
@ -821,6 +825,7 @@ class AApp extends window.AObject {
}).bind(this)(); }).bind(this)();
} }
loadedLayout() { loadedLayout() {
console.log(this.lName);
if (this.lName !== "None") { if (this.lName !== "None") {
const l = window.ALayout.get(this.lName); const l = window.ALayout.get(this.lName);
if (!l.isLoaded) { if (!l.isLoaded) {
@ -831,7 +836,6 @@ class AApp extends window.AObject {
} }
} }
setLayout(o) { setLayout(o) {
var oP = new DOMParser(); var oP = new DOMParser();
var pHtml = oP.parseFromString(o.Content, 'text/html'); var pHtml = oP.parseFromString(o.Content, 'text/html');
(function () { (function () {