update TWA Management v0.0.1
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
47
AppLibs/AppLibs/Libs/Crypt/UUID7.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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--)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
33
TWA-App/Models/Service.cs
Normal 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 = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 =>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
}
|
}
|
||||||
@ -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 You’ve 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>
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2717,10 +2732,10 @@ h3 {
|
|||||||
border-radius: var(--radius)
|
border-radius: var(--radius)
|
||||||
}
|
}
|
||||||
|
|
||||||
.con-image img {
|
.con-image img {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
BIN
TWA-App/wwwroot/images/3000/3001/cargo-charter-operations.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
TWA-App/wwwroot/images/3000/3001/passenger.png
Normal file
|
After Width: | Height: | Size: 739 KiB |
BIN
TWA-App/wwwroot/images/3000/3001/stackholder-group-1.png
Normal file
|
After Width: | Height: | Size: 559 KiB |
BIN
TWA-App/wwwroot/images/3000/3001/stackholder-group.png
Normal file
|
After Width: | Height: | Size: 675 KiB |
BIN
TWA-App/wwwroot/images/3000/3001/twa-service-scope.png
Normal file
|
After Width: | Height: | Size: 5.2 MiB |
BIN
TWA-App/wwwroot/images/3000/3001/warehouse.png
Normal file
|
After Width: | Height: | Size: 4.3 MiB |
BIN
TWA-App/wwwroot/images/6000/contact-form.png
Normal file
|
After Width: | Height: | Size: 874 KiB |
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
17
TWASys-App/Controllers/LoginController.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
26
TWASys-App/DBModels/Files.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
27
TWASys-App/DBModels/Folder.cs
Normal 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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
16
TWASys-App/DBModels/Folders_has_StorageArea.cs
Normal 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!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
26
TWASys-App/DBModels/MicrosoftAccount.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
TWASys-App/DBModels/RefreshToken.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
TWASys-App/DBModels/RefreshToken_Log.cs
Normal 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!;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
TWASys-App/DBModels/ServerAuthorization.cs
Normal 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>();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
TWASys-App/DBModels/ServerAuthorization_has_Token.cs
Normal 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!;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
TWASys-App/DBModels/StorageArea.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
TWASys-App/DBModels/StorageArea_has_StorageServer.cs
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
25
TWASys-App/DBModels/StorageServer.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
TWASys-App/DBModels/StorageServer_has_MicrosoftAccount.cs
Normal 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!;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
TWASys-App/DBModels/StorageServers_ValidationDomain.cs
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
27
TWASys-App/DBModels/SyncFile_Log.cs
Normal 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!;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
TWASys-App/DBModels/Token.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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; }
|
||||||
|
|
||||||
|
|||||||
@ -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; }
|
||||||
|
|||||||
329
TWASys-App/Dapper.AExtentions/BatchInsert.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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,19 +94,42 @@ 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
|
||||||
|
return keyProperties;
|
||||||
|
}
|
||||||
|
|
||||||
if (keyProperties.Count == 0)
|
|
||||||
|
public static List<PropertyInfo> FKPropertiesCache(Type type)
|
||||||
|
{
|
||||||
|
if (FKProperties.TryGetValue(type.TypeHandle, out var cachedProps))
|
||||||
{
|
{
|
||||||
var idProp = allProperties.Find(p => string.Equals(p.Name, "id", StringComparison.CurrentCultureIgnoreCase));
|
return cachedProps.ToList();
|
||||||
if (idProp != null)
|
}
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
keyProperties.Add(idProp);
|
var val = attr.GetType().GetProperty("Name")?.GetValue(attr);
|
||||||
|
if (val == null && string.IsNullOrWhiteSpace(val?.ToString()))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
KeyProperties[type.TypeHandle] = keyProperties;
|
|
||||||
return keyProperties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<PropertyInfo> ComputedPropertiesCache(Type type)
|
public static List<PropertyInfo> ComputedPropertiesCache(Type type)
|
||||||
@ -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)
|
||||||
|
|||||||
55
TWASys-App/Models/Login/LoginCheckFilter.cs
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
TWASys-App/Models/Shared.cs
Normal 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)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
159
TWASys-App/Models/StorageModel.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
|||||||
@ -1,50 +1,56 @@
|
|||||||
<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>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="wwwroot\css\atg-font\atg-admin-font.css" />
|
|
||||||
<None Include="wwwroot\css\atg-font\atg-font.css" />
|
|
||||||
<None Include="wwwroot\css\atg-lib\atg-core-min.css" />
|
|
||||||
<None Include="wwwroot\css\atg-lib\atg-core.css" />
|
|
||||||
<None Include="wwwroot\css\atg-lib\atg-upload.css" />
|
|
||||||
<None Include="wwwroot\css\atg-lib\datepicker.css" />
|
|
||||||
<None Include="wwwroot\css\atg-lib\swiper-bundle.min.css" />
|
|
||||||
<None Include="wwwroot\css\atg-lib\waves.min.css" />
|
|
||||||
<None Include="wwwroot\css\atg-ui\atg-gui.css" />
|
|
||||||
<None Include="wwwroot\css\atg-ui\table.css" />
|
|
||||||
<None Include="wwwroot\css\site.css" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<PropertyGroup Condition="'$(TargetFramework)'=='net9.0-windows'">
|
||||||
<ProjectReference Include="..\AppLibs\AppLibs\AppLibs.csproj" />
|
<DefineConstants>$(DefineConstants);WINDOWS</DefineConstants>
|
||||||
</ItemGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
<None Include="wwwroot\css\atg-font\atg-admin-font.css" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.10" />
|
<None Include="wwwroot\css\atg-font\atg-font.css" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.10" />
|
<None Include="wwwroot\css\atg-lib\atg-core-min.css" />
|
||||||
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
<None Include="wwwroot\css\atg-lib\atg-core.css" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<None Include="wwwroot\css\atg-lib\atg-upload.css" />
|
||||||
</ItemGroup>
|
<None Include="wwwroot\css\atg-lib\datepicker.css" />
|
||||||
|
<None Include="wwwroot\css\atg-lib\swiper-bundle.min.css" />
|
||||||
|
<None Include="wwwroot\css\atg-lib\waves.min.css" />
|
||||||
|
<None Include="wwwroot\css\atg-ui\atg-gui.css" />
|
||||||
|
<None Include="wwwroot\css\atg-ui\table.css" />
|
||||||
|
<None Include="wwwroot\css\site.css" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Keys\db\ca-cert.pem">
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.10" />
|
||||||
</None>
|
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.10" />
|
||||||
<None Update="Keys\db\client-cert.pem">
|
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
</None>
|
</ItemGroup>
|
||||||
<None Update="Keys\db\client-key.pem">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<ItemGroup>
|
||||||
</None>
|
<ProjectReference Include="..\AppLibs\AppLibs\AppLibs.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Keys\db\ca-cert.pem">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Keys\db\client-cert.pem">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="Keys\db\client-key.pem">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -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>
|
||||||
|
}
|
||||||
@ -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>
|
||||||
<div class="d-n c-x-6 d-x-b">
|
<div class="c-login">
|
||||||
<img src="~/images/img1.jpg" class="w-100" />
|
<div class="frm-login">
|
||||||
</div>
|
<div class="ws-login">
|
||||||
<div class="c-12 c-x-6">
|
<div class="frm-header d-f f-c a-i-center">
|
||||||
<div class="h-100 d-f a-i-center">
|
<a app-nav href="@Url.AbsoluteContent("~/")" class="c_logo d-f j-c-center">
|
||||||
<div class="ws-login d-f f-c a-i-center">
|
<img src="@Url.AbsoluteContent("~/images/logo/slogo.png")">
|
||||||
<span class="ws-title">Đăng Nhập</span>
|
|
||||||
<div class="ws-input mb-4">
|
</a>
|
||||||
<span class="label-input">Tài khoản</span>
|
|
||||||
<div class="c-input d-f a-i-center">
|
</div>
|
||||||
<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>
|
<div class="AWizard tabcontents">
|
||||||
<input type="text" class="input" placeholder="Nhập tài khoản" />
|
<div class="tabcontent">
|
||||||
</div>
|
|
||||||
</div>
|
<div class="frm-header">
|
||||||
<div class="ws-input">
|
<h2 class="title">Sign In</h2>
|
||||||
<span class="label-input">Mật khẩu</span>
|
<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 class="c-input d-f a-i-center">
|
</div>
|
||||||
<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>
|
<div class="frm-input mt-4">
|
||||||
<input type="password" class="input" placeholder="Nhập mật khẩu" />
|
<label>Username</label>
|
||||||
</div>
|
<div class="c-input">
|
||||||
</div>
|
<span class="ico atg a-1x atg-user"></span>
|
||||||
<div class="ws-forgot ml-auto">
|
<input type="text" class="input" placeholder="Your username" />
|
||||||
<a href="#">Quên mật khẩu?</a>
|
<span class="line"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="ws-btn d-f j-c-center">
|
</div>
|
||||||
<div class="btn-mask"></div>
|
<div class="frm-input">
|
||||||
<button class="btn btn-effect">Đăng nhập</button>
|
<label>Password</label>
|
||||||
</div>
|
<div class="c-input">
|
||||||
<div class="ws-signup d-f f-c a-i-center">
|
<span class="ico atg a-1x atg-user"></span>
|
||||||
<span>Đăng ký tài khoản bằng ID</span>
|
<input type="password" class="input" placeholder="Your password" />
|
||||||
<a href="#">Đăng Ký</a>
|
<span class="line"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="frm-header d-f f-wrap j-c-between a-i-center">
|
||||||
</div>
|
<a href="javascript:void(0)" id="forgetPass">Forget the password?</a>
|
||||||
</div>
|
<button class="btn btn-primary btn-effect">Sign In</button>
|
||||||
</div>
|
</div>
|
||||||
</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" />
|
||||||
|
<span class="line"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="frm-input">
|
||||||
|
<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="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>
|
||||||
|
@section jsLib {
|
||||||
|
<script type="module" src="@Url.AbsoluteContent("~/js/js-page/10001.js")" js-lib></script>
|
||||||
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
9
TWASys-App/Views/Shared/_LayoutLogin.cshtml
Normal 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>
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
|||||||
165
TWASys-App/wwwroot/css/pages/login.css
Normal 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);
|
||||||
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
TWASys-App/wwwroot/images/login/80633.jpg
Normal file
|
After Width: | Height: | Size: 634 KiB |
BIN
TWASys-App/wwwroot/images/login/806372.jpg
Normal file
|
After Width: | Height: | Size: 852 KiB |
BIN
TWASys-App/wwwroot/images/login/806459.jpg
Normal file
|
After Width: | Height: | Size: 497 KiB |
BIN
TWASys-App/wwwroot/images/logo/icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
TWASys-App/wwwroot/images/logo/slogo.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
4
TWASys-App/wwwroot/js/js-page/1000.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
window.L1000 = function () {
|
||||||
|
|
||||||
|
}
|
||||||
|
window.AScript.set("1000", true);
|
||||||
26
TWASys-App/wwwroot/js/js-page/10001.js
Normal 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);
|
||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
39
TWASys-App/wwwroot/js/js-page/asyncLoginLayout.js
Normal 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);
|
||||||
@ -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) {
|
||||||
|
|||||||
@ -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 () {
|
||||||
|
|||||||