Compare commits

...
24 Commits
Author SHA1 Message Date
kham 1a9316ed6f put in new function call 2026-09-26 09:23:54 +10:00
kham f3b1d901a8 put in vieww 2026-09-24 22:28:34 +10:00
kham df7e8827be change to v22 and ui optimus ng 2026-09-20 23:07:32 +10:00
kham 2e477ca023 change to person id to photo id 2026-09-20 17:38:15 +10:00
kham 5292b99af6 fixed download if no image return empty 2026-09-20 15:30:22 +10:00
kham 3d83e95613 put in new ui and backend store image to table 2026-09-20 14:32:59 +10:00
kham 75b49be476 change all save to database no folder anymore 2026-09-19 22:37:43 +10:00
kham ba4976fce4 add clear log provide 2026-08-23 18:15:40 +10:00
khamandand logout too 4eafa2fa8d add in cookie in now
Co-authored-by: and logout too
2026-08-14 17:59:47 +10:00
kham 70083f1230 commit first 2026-08-14 17:52:42 +10:00
kham 9070bebb54 put in two launch.json and tasks.json 2026-08-05 10:49:18 +10:00
kham 5d2e989ee9 fixed the program.cs and run in VC code 2026-07-30 17:23:48 +10:00
kham ce2b3ba8c9 add toolbar layout on the left when mobile on below image and text 2026-05-17 13:07:11 +10:00
kham c9228950d5 add check db is not there create db not there same as table 2026-05-16 23:22:42 +10:00
kham 2e7f9e7a27 add menu on left 2026-05-16 19:16:47 +10:00
kham d656229ab3 remove hard code password and username 2026-05-15 22:07:13 +10:00
kham fa3b289a77 update config file and sample graphql 2026-05-15 21:34:45 +10:00
kham 09ac6c6bb1 add nginx in arch linux style 2026-05-14 21:59:52 +10:00
kham 8228ff27b8 adding config for arch linux and nginx 2026-05-14 21:59:19 +10:00
kham c0bc89083d put in fixed error in appsetting.cs does not have other key 2026-05-13 18:22:17 +10:00
kham fb751ecbc3 add file deploy in linux with nginx configure file 2026-05-12 21:39:26 +10:00
kham d4543b5169 add new folder wsl 2026-05-12 21:02:00 +10:00
kham adf62c8dcc change namme and project 2026-05-12 20:59:47 +10:00
kham 936094a4bf remove ensure mirgration
and scope.serviceProvider.getService
2026-05-10 21:49:38 +10:00
131 changed files with 11574 additions and 11413 deletions
+54
View File
@@ -0,0 +1,54 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C#: FamilyTreeAPI",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/FamilyTreeAPI/bin/Debug/net10.0/FamilyTreeAPI.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5019"
},
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger/index.html"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
},
},
{
"name": ".NET Core Launch (web) - Release",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "publish",
"program": "${workspaceFolder}/FamilyTreeAPI/bin/Release/net10.0/linux-x64/FamilyTreeAPI.dll",
"serverReadyAction": {
"action": "openExternally",
"pattern": "listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger/index.html"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Production",
"ASPNETCORE_URLS": "https://localhost:5019"
},
"cwd": "${workspaceFolder}"
}
]
}
+31
View File
@@ -0,0 +1,31 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"${workspaceFolder}/FamilyTreeAPI/FamilyTreeAPI.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/FamilyTreeAPI/FamilyTreeAPI.csproj",
"-c",
"Release"
],
"problemMatcher": "$msCompile"
},
]
}
+1 -2
View File
@@ -1,5 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.7.34202.233 VisualStudioVersion = 17.7.34202.233
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
@@ -3,6 +3,7 @@ using FamilyTreeAPI.Repository;
namespace FamilyTreeAPI.Controllers; namespace FamilyTreeAPI.Controllers;
using DocumentFormat.OpenXml.Drawing.Charts;
using FamilyTreeAPI.Interface; using FamilyTreeAPI.Interface;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
@@ -110,14 +111,44 @@ public class FileUploadController : ControllerBase
return Ok(ret); return Ok(ret);
} }
[HttpPost("[action]")]
public async Task<IActionResult> DownloadPersonPhotoById(DeleteCriteria<int> criteria)
{
var result = await _personPhoto.DownloadPersonPhotoById(criteria.Id);
if (result.StatusCode > 0)
return File(result.Data.Content, result.Data.ContentType, result.Data.FileName);
else
return NotFound("not found image");
}
[HttpPost("[action]")] [HttpPost("[action]")]
public async Task<IActionResult> DownloadPersonPhoto(DownloadFileCriteria criteria) public async Task<IActionResult> DownloadPersonPhoto(DownloadFileCriteria criteria)
{ {
var result = await _personPhoto.DownloadPersonPhoto(criteria.Id, criteria.FileName); var result = await _personPhoto.DownloadPersonPhotoPerson(criteria.Id,criteria.PhotoType ,criteria.FileName);
if (result.StatusCode > 0)
return File(result.Data.Content, result.Data.ContentType, result.Data.FileName); return File(result.Data.Content, result.Data.ContentType, result.Data.FileName);
else
return NotFound("not found image");
}
[HttpPost("[action]")]
public async Task<IActionResult> DownloadPersonPhoto64(DownloadFileCriteria criteria)
{
var result = await _personPhoto.DownloadPersonPhotoPerson(criteria.Id,criteria.PhotoType ,criteria.FileName);
if (result.StatusCode > 0)
{
byte[] file64 = result.Data.Content;
string rval = Convert.ToBase64String(file64);
ResultModel<string> retval = new() {
Data = rval,
StatusCode = 1,
Message = ""
};
return Ok(retval);
}
else
return NotFound("not found image");
} }
[HttpPost("[action]")] [HttpPost("[action]")]
public async Task<ActionResult> DeletePersonPhoto(DeleteFileCriteria criteria) public async Task<ActionResult> DeletePersonPhoto(DeleteFileCriteria criteria)
@@ -127,8 +158,18 @@ public class FileUploadController : ControllerBase
if ( criteria.Id > 0) if ( criteria.Id > 0)
{ {
ret = await _personPhoto.DeletePersonPhoto(criteria.Id); ret = await _personPhoto.DeletePersonPhoto(criteria.Id, criteria.PhotoType);
} }
return Ok(ret); return Ok(ret);
} }
[HttpPost("[action]")]
public async Task<ActionResult> DeletePersonPhotoById(DeleteCriteria<int> criteria)
{
ResultModel<int> ret = new();
ret = await _personPhoto.DeletePersonPhotoById(criteria.Id);
return Ok(ret);
}
} }
@@ -14,6 +14,7 @@ namespace FamilyTreeAPI.Controllers
{ {
_repo = repo; _repo = repo;
} }
[HttpPost("[action]")] [HttpPost("[action]")]
public async Task<IActionResult> UploadImage(IFormFile file) public async Task<IActionResult> UploadImage(IFormFile file)
{ {
@@ -37,8 +37,25 @@ public class UsersController : ControllerBase
public async Task<IActionResult> LoginApiAD([FromBody] AuthenticateRequest model) public async Task<IActionResult> LoginApiAD([FromBody] AuthenticateRequest model)
{ {
string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
var response = await _userService.LoginApiAD(model, remoteIpAddress); var rval = await _userService.LoginApiAD(model, remoteIpAddress);
return Ok(response); if (rval.Data != null && rval.Data.Token != null)
{
// Configure cookie options
var cookieOptions = new CookieOptions
{
HttpOnly = true, // Blocks client-side JavaScript reading/theft
Secure = false, // Requires HTTPS connections
SameSite = SameSiteMode.Unspecified, // Prevents CSRF attacks
Expires = DateTimeOffset.UtcNow.AddDays(1), // Match your token lifespan
Path = "/" // Accessible across entire domain structure
};
// Write cookie into response headers
Response.Cookies.Append("token", rval.Data.Token, cookieOptions);
}
return Ok(rval);
} }
[HttpPost("[action]")] [HttpPost("[action]")]
@@ -51,6 +68,14 @@ public class UsersController : ControllerBase
string token = Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); string token = Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
string remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(); string remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();
var response = await _userService.Logout(token, currentUser, remoteIpAddress); var response = await _userService.Logout(token, currentUser, remoteIpAddress);
Response.Cookies.Delete("token", new CookieOptions
{
HttpOnly = true,
Secure = false,
SameSite = SameSiteMode.Unspecified,
Path = "/"
});
return Ok(response); return Ok(response);
} }
+2 -2
View File
@@ -3,7 +3,7 @@
# This stage is used when running from VS in fast mode (Default for Debug configuration) # This stage is used when running from VS in fast mode (Default for Debug configuration)
#FROM mcr.microsoft.com/dotnet/aspnet:9::.0 AS base #FROM mcr.microsoft.com/dotnet/aspnet:9::.0 AS base
ARG BUILD_CONFIGURATION=Release ARG BUILD_CONFIGURATION=Release
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src WORKDIR /src
COPY ["FamilyTreeAPI/FamilyTreeAPI.csproj", "FamilyTreeAPI/"] COPY ["FamilyTreeAPI/FamilyTreeAPI.csproj", "FamilyTreeAPI/"]
RUN dotnet restore "./FamilyTreeAPI/FamilyTreeAPI.csproj" RUN dotnet restore "./FamilyTreeAPI/FamilyTreeAPI.csproj"
@@ -31,7 +31,7 @@ FROM build AS publish
RUN dotnet publish "./FamilyTreeAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false RUN dotnet publish "./FamilyTreeAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) # This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app WORKDIR /app
ENV ASPNETCORE_HTTP_PORTS=8080 ENV ASPNETCORE_HTTP_PORTS=8080
EXPOSE 8080 EXPOSE 8080
+2 -2
View File
@@ -4,9 +4,9 @@ public class AppSettings
{ {
public string Secret { get; set; } public string Secret { get; set; }
public string SQLConnectionString { get; set; } public string SQLConnectionString { get; set; }
public string LoginWebAPI { get; set; } public string ImageFolder { get; set; }
public string ClientURL { get; set; } public string ImportFolder { get; set; }
} }
/* /*
+4 -1
View File
@@ -15,16 +15,19 @@ public class DownloadFileCriteria
{ {
public string FileName { get; set; } public string FileName { get; set; }
public int Id { get; set; } public int Id { get; set; }
public int PhotoType {get; set;}
} }
public class UploadCriteria public class UploadCriteria
{ {
public string FileName { get; set; } public string FileName { get; set; }
public int PersonId { get; set; } public int PersonId { get; set; }
public int PhotoType {get; set;}
public IFormFile File { get; set; } public IFormFile File { get; set; }
} }
public class DeleteFileCriteria public class DeleteFileCriteria
{ {
public int Id { get; set; } public int Id { get; set; }
public string Filename { get; set; } public string Filename { get; set; }
public int PhotoType {get; set;}
} }
@@ -1,5 +1,12 @@
namespace FamilyTreeAPI.Entities; namespace FamilyTreeAPI.Entities;
//photo Type string number
public enum enumPhotoType
{
Profile = 1,
Other = 2
}
public class PersonPhotoDto public class PersonPhotoDto
{ {
public int Id { get; set; } public int Id { get; set; }
+20 -17
View File
@@ -1,38 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<PublishSingleFile>true</PublishSingleFile>
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
<AllowMissingPrunePackageData>true</AllowMissingPrunePackageData>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Models_old\**" /> <PackageReference Include="HotChocolate.AspNetCore" Version="16.6.1" />
<Content Remove="Models_old\**" /> <PackageReference Include="HotChocolate.Data.EntityFramework" Version="16.6.1" />
<EmbeddedResource Remove="Models_old\**" /> <PackageReference Include="HotChocolate.Pagination.EntityFramework" Version="14.3.1" />
<None Remove="Models_old\**" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
</ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.11">
<ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="15.1.13" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.13" />
<PackageReference Include="HotChocolate.Pagination.EntityFramework" Version="14.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.5"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.11">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
<PackageReference Include="SpreadsheetLight" Version="3.5.0" /> <PackageReference Include="SpreadsheetLight" Version="3.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
<PackageReference Include="System.Drawing.Common" Version="10.0.11" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project> </Project>
+6
View File
@@ -2,6 +2,12 @@
{ {
public class Helpers public class Helpers
{ {
public static string GetFileExtension(string fileName)
{
string result = System.IO.Path.GetExtension(fileName);
return result;
}
public static string GetFileContent(string extension) public static string GetFileContent(string extension)
{ {
string contentType = ""; string contentType = "";
+7
View File
@@ -71,6 +71,10 @@ $ sudo nano /etc/nginx/sites-avaible/default
After adding the content, save it. Then let’s enable and start the service: After adding the content, save it. Then let’s enable and start the service:
sudo systemctl enable kestrel-app.service sudo systemctl enable kestrel-app.service
sudo systemctl start kestrel-app.service sudo systemctl start kestrel-app.service
put familytreeapp.conf
put in /etc/nginx/sites-availalble/familytreeapp.conf
$) sudo ln -s /etc/nginx/sites-available/familytreeapp.conf /etc/nginx/sites-enabled/
postgresql.conf: postgresql.conf:
@@ -105,3 +109,6 @@ $ sudo nano /etc/nginx/sites-avaible/default
docker run -p 5432:5432 -e POSTGRES_PASSWORD=123456789 \ docker run -p 5432:5432 -e POSTGRES_PASSWORD=123456789 \
-d postgres:9.3.6 \ -d postgres:9.3.6 \
-c config_file=/path/to/postgresql.conf -c config_file=/path/to/postgresql.conf
+4 -3
View File
@@ -5,8 +5,9 @@ namespace FamilyTreeAPI.Interface;
public interface IPersonPhoto public interface IPersonPhoto
{ {
Task<ResultModel<int>> SaveAsync(UploadCriteria criteria); Task<ResultModel<int>> SaveAsync(UploadCriteria criteria);
Task<ResultModel<FileContent>> DownloadPersonPhoto(int id, string? filename); Task<ResultModel<FileContent>> DownloadPersonPhotoPerson(int personId, int photoType, string filename);
Task<ResultModel<FileContent>> DownloadPersonPhotoById(int id);
Task<ResultModel<int>> DeletePersonPhoto(int id); Task<ResultModel<int>> DeletePersonPhoto(int id, int photoType);
Task<ResultModel<int>> DeletePersonPhotoById(int id);
Task<ResultModel<List<PersonPhotoDto>>> LoadPersonPhoto(int personId); Task<ResultModel<List<PersonPhotoDto>>> LoadPersonPhoto(int personId);
} }
+80 -26
View File
@@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.Text; using System.Text;
using DocumentFormat.OpenXml.Office2010.Drawing.Charts;
using FamilyTreeAPI.Authorization; using FamilyTreeAPI.Authorization;
using FamilyTreeAPI.Entities; using FamilyTreeAPI.Entities;
using FamilyTreeAPI.GraphQL.Query; using FamilyTreeAPI.GraphQL.Query;
@@ -14,6 +13,15 @@ using FamilyTreeAPI.Repository;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
// Explicitly register the base paths for Linux container execution
builder.Configuration
.SetBasePath(AppContext.BaseDirectory) // <-- Pulls from wherever the binary sits
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
// Add services to the container. // Add services to the container.
#region Services #region Services
var services = builder.Services; var services = builder.Services;
@@ -22,11 +30,26 @@ services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
services.AddEndpointsApiExplorer(); services.AddEndpointsApiExplorer();
services.AddSwaggerGen(); services.AddSwaggerGen();
string skey = "Super Secret SIGN AND VERIFY JWT TOKENS";
var appSettingsSection = builder.Configuration.GetSection("AppSettings"); var appSettingsSection = builder.Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection); if (appSettingsSection.Exists())
var appSettings = appSettingsSection.Get<AppSettings>(); {
var key = Encoding.ASCII.GetBytes(appSettings.Secret); services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
if ( appSettings != null)
skey = appSettings.Secret;
else
{
string tkey = builder.Configuration.GetValue<string>("AppSettings:Secret") ?? string.Empty;
if (!string.IsNullOrEmpty(tkey))
skey = tkey;
}
}
var key = Encoding.ASCII.GetBytes(skey);
services.AddAuthentication(x => services.AddAuthentication(x =>
{ {
@@ -103,19 +126,7 @@ using (var scope = app.Services.CreateScope())
var db = context.Database; var db = context.Database;
if (db != null) if (db != null)
{ {
try var staff = scope.ServiceProvider.GetService<Seed>();
{
using var cts = new CancellationTokenSource();
CancellationToken cancellationToken = cts.Token;
await db.MigrateAsync(cancellationToken);
db.EnsureCreated();
}
catch
{
//continue
}
var staff = context.GetService<Seed>();
int id = staff.InsertOneUser(); int id = staff.InsertOneUser();
if (id < 0) if (id < 0)
{ {
@@ -124,30 +135,33 @@ using (var scope = app.Services.CreateScope())
{ {
//if (!databaseCreator.Exists()) //if (!databaseCreator.Exists())
try try
{
// Check if database exists
if (!databaseCreator.Exists())
{
databaseCreator.Create();
}
if (!databaseCreator.HasTables())
{ {
databaseCreator.CreateTables(); databaseCreator.CreateTables();
} }
}
catch catch
{ {
//continue //continue
} }
} }
id = staff.InsertOneUser(); id = staff.InsertOneUser();
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.ToString()); Console.WriteLine(e.ToString());
} }
} }
app.UseMiddleware<JwtMiddleware>(); app.UseMiddleware<JwtMiddleware>();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())// //if (app.Environment.IsDevelopment())//
@@ -173,6 +187,17 @@ app.Run();
#endregion #endregion
/* /*
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
https://www.youtube.com/watch?v=WQFx2m5Ub9M https://www.youtube.com/watch?v=WQFx2m5Ub9M
@@ -188,14 +213,43 @@ PM> Scaffold-DbContext "Host=postgresdb;Port=5432;database=FamilyTreeDB;user id=
https://www.youtube.com/watch?v=HnXA8RI7Tvc https://www.youtube.com/watch?v=HnXA8RI7Tvc
query { query {
family { persons {
nodes{ nodes{
id id
firstname firstName
lastname sex
email email
} }
} }
} }
dotnet --list-sdks
In the VS Code Explorer, click and open your <ProjectName>.csproj file.
Locate the <TargetFramework> tag inside the <PropertyGroup> block.
Change the value to your desired version (e.g., change net11.0 or net9.0 to net10.0
now run to see out of date
dotnet list package --outdated
dotnet tool uninstall --global dotnet-outdated
dotnet tool install --global dotnet-outdated-tool
//update the outdate
dotnet outdated -u
update the package
dotnet add package <Package_Name>
Open the .vscode folder in your workspace and select launch.json.
Look for lines containing your old framework directory string (e.g., /bin/Debug/net8.0/).
Update those paths to reflect your new version (e.g., /bin/Debug/net9.0/).
clear and rebuild
dotnet clean
dotnet restore
dotnet build
*/ */
@@ -8,35 +8,17 @@
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"dotnetRunMessages": true, "dotnetRunMessages": true,
"applicationUrl": "http://localhost:5016", "applicationUrl": "http://localhost:5016"
"applicationUrl1": "http://192.168.8.188:5015"
}, },
"IIS Express": { "http": {
"commandName": "IISExpress", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"launchUrl": "swagger", "launchUrl": "swagger",
"environmentVariables": { "applicationUrl": "http://localhost:5000"
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": false
}
}, },
"$schema": "https://json.schemastore.org/launchsettings.json", "$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:4559",
"sslPort": 0
}
} }
} }
@@ -35,7 +35,7 @@ namespace FamilyTreeAPI.Repository
string error = ""; string error = "";
try try
{ {
Lookup model = null!; FamilyTreeAPI.Models.Lookup model = null!;
bool ok = !checkDescription(desc, lookup.Type, lookup.Id); bool ok = !checkDescription(desc, lookup.Type, lookup.Id);
if (ok) if (ok)
{ {
@@ -52,7 +52,7 @@ namespace FamilyTreeAPI.Repository
} }
else else
{ {
Lookup? model1 = await _context.Lookups.FindAsync(lookup.Id); FamilyTreeAPI.Models.Lookup? model1 = await _context.Lookups.FindAsync(lookup.Id);
if (model1 != null) if (model1 != null)
{ {
model1.Description = desc; model1.Description = desc;
@@ -3,6 +3,7 @@ using FamilyTreeAPI.Entities;
using FamilyTreeAPI.Helper; using FamilyTreeAPI.Helper;
using FamilyTreeAPI.Interface; using FamilyTreeAPI.Interface;
using FamilyTreeAPI.Models; using FamilyTreeAPI.Models;
using HotChocolate.CostAnalysis.Types;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace FamilyTreeAPI.Repository namespace FamilyTreeAPI.Repository
@@ -31,12 +32,12 @@ namespace FamilyTreeAPI.Repository
dto.PhotoType = model.PhotoType; dto.PhotoType = model.PhotoType;
return dto; return dto;
} }
private PersonPhoto FillModel(PersonPhoto model, int personId, string filename) private PersonPhoto FillModel(PersonPhoto model, int personId, int photoType, string filename)
{ {
model.PersonId = personId; model.PersonId = personId;
model.Photo = filename; model.Photo = filename;
model.PhotoType = System.IO.Path.GetExtension(filename); model.PhotoType = photoType.ToString(); //System.IO.Path.GetExtension(filename);
return model; return model;
} }
@@ -44,8 +45,10 @@ namespace FamilyTreeAPI.Repository
{ {
int result = default(int); int result = default(int);
int statuscode = 0; int statuscode = 0;
string error = ""; string error = "";//Other
string photoType = "2"; //
if (criteria.PhotoType > 0)
photoType = criteria.PhotoType.ToString();
HttpContext? httpContext = _httpContext.HttpContext; HttpContext? httpContext = _httpContext.HttpContext;
string loginName = ""; string loginName = "";
if (httpContext != null) if (httpContext != null)
@@ -63,7 +66,7 @@ namespace FamilyTreeAPI.Repository
model = new(); model = new();
model = FillModel(model,criteria.PersonId, formfile.FileName); model = FillModel(model,criteria.PersonId, criteria.PhotoType,formfile.FileName);
using (var stream = new MemoryStream()) using (var stream = new MemoryStream())
{ {
@@ -90,15 +93,50 @@ namespace FamilyTreeAPI.Repository
Message = error Message = error
}; };
} }
public async Task<ResultModel<FileContent>> DownloadPersonPhotoById(int Id)
public async Task<ResultModel<FileContent>> DownloadPersonPhoto(int id, string? filename)
{ {
int statusCode = -1; int statusCode = -1;
string error = ""; string error = "";
string extension = "";
PersonPhoto? model = null; PersonPhoto? model = null;
FileContent fileContent = new(); FileContent fileContent = new();
if (id < 1) try {
model = await _context.PersonPhotos.FindAsync(Id);
if (model != null)
{
extension = Helpers.GetFileExtension(model.Photo);
fileContent.ContentType = Helpers.GetFileContent(extension);
fileContent.Content = (byte[])model.ImagePhoto;
fileContent.FileName = model.Photo;
statusCode = 1;
}
}
catch (Exception e)
{
error = e.ToString();
statusCode = -1;
}
return new ResultModel<FileContent>()
{
Data = fileContent,
StatusCode = statusCode,
Message = error
};
}
public async Task<ResultModel<FileContent>> DownloadPersonPhotoPerson(int personId, int photoType, string filename)
{
int statusCode = -1;
string error = "";
string extension = "";
PersonPhoto? model = null;
FileContent fileContent = new();
if (personId < 1)
{ {
var rlist = await _context.PersonPhotos.Where(x => x.Photo == filename).ToListAsync(); var rlist = await _context.PersonPhotos.Where(x => x.Photo == filename).ToListAsync();
if (rlist != null) if (rlist != null)
@@ -109,19 +147,26 @@ namespace FamilyTreeAPI.Repository
} }
} }
} }
else else
{ {
model = await _context.PersonPhotos.FindAsync(id); var rlist = await _context.PersonPhotos.Where (x => x.PersonId == personId && photoType.ToString() == x.PhotoType).ToListAsync();
for ( int i = 0; i < rlist.Count; i++)
{
model = rlist[i];
break;
}
} }
if (model != null) if (model != null)
{ {
fileContent.ContentType = Helpers.GetFileContent(model.PhotoType); extension = Helpers.GetFileExtension(model.Photo);
fileContent.ContentType = Helpers.GetFileContent(extension);
fileContent.Content = (byte[])model.ImagePhoto; fileContent.Content = (byte[])model.ImagePhoto;
fileContent.FileName = model.Photo; fileContent.FileName = model.Photo;
statusCode = 1; statusCode = 1;
} }
return new ResultModel<FileContent>() return new ResultModel<FileContent>()
{ {
Data = fileContent, Data = fileContent,
@@ -130,21 +175,70 @@ namespace FamilyTreeAPI.Repository
}; };
} }
public async Task<ResultModel<int>> DeletePersonPhoto(int id) public async Task<ResultModel<int>> DeletePersonPhoto(int personId, int photoType)
{ {
int result = default(int); int result = default(int);
int statuscode = 0; int statuscode = 0;
string error = ""; string error = "";
PersonPhoto? model;
try try
{ {
PersonPhoto? model = await _context.PersonPhotos.FindAsync(id); if (photoType < 1)
if (model != null)
{ {
List<PersonPhoto> list = await _context.PersonPhotos.Where ( x => x.PersonId == personId).ToListAsync();
for (int i = 0; i< list.Count; i++)
{
model = list[i];
_context.PersonPhotos.Remove(model); _context.PersonPhotos.Remove(model);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
statuscode = 1; statuscode = 1;
} }
} }
else
{
List<PersonPhoto> list = await _context.PersonPhotos.Where ( x => x.PersonId == personId && x.PhotoType == photoType.ToString()).ToListAsync();
for (int i = 0; i< list.Count; i++)
{
model = list[i];
_context.PersonPhotos.Remove(model);
await _context.SaveChangesAsync();
statuscode = 1;
}
}
}
catch (Exception ex)
{
error = ex.ToString();
statuscode = -1;
}
//var dto = await Task.Run(() => result);
return new ResultModel<int>()
{
Data = result,
StatusCode = statuscode,
Message = error
};
}
public async Task<ResultModel<int>> DeletePersonPhotoById(int id)
{
int result = default(int);
int statuscode = 0;
string error = "";
try
{
PersonPhoto? model = await _context.PersonPhotos.FindAsync(id);
if (model != null)
{
_context.PersonPhotos.Remove(model);
await _context.SaveChangesAsync();
statuscode = 1;
}
}
catch (Exception ex) catch (Exception ex)
{ {
error = ex.ToString(); error = ex.ToString();
@@ -283,7 +283,7 @@ public partial class PersonRepository : IPerson
node.Key = item.Id.ToString(); node.Key = item.Id.ToString();
node.Type = type; node.Type = type;
if (item.Image != null) if (item.Image != null)
node.Data.Image = await LoadPersonImage(item.Image); node.Data.Image = await LoadPersonImage(item.Id, (int) enumPhotoType.Profile, item.Image);
node.Icon = "T"; node.Icon = "T";
node.StyleClass = getStyleClass(node.Icon); node.StyleClass = getStyleClass(node.Icon);
@@ -319,8 +319,8 @@ public partial class PersonRepository : IPerson
TreeNode<TreeData> child; TreeNode<TreeData> child;
PersonDto dto; PersonDto dto;
Person pe; Person pe;
int fatherId, motherId; int fatherId, motherId, personId;
fatherId = motherId = 0; personId = fatherId = motherId = 0;
string data = person.Sex; string data = person.Sex;
TreeNode<TreeData> citem; TreeNode<TreeData> citem;
string pName = ""; string pName = "";
@@ -371,6 +371,7 @@ public partial class PersonRepository : IPerson
pName = pe.FirstName; pName = pe.FirstName;
image = pe.Image; image = pe.Image;
key = motherId.ToString(); key = motherId.ToString();
personId = motherId;
data = pe.Sex; data = pe.Sex;
} }
} }
@@ -382,6 +383,7 @@ public partial class PersonRepository : IPerson
pName = pe.FirstName; pName = pe.FirstName;
image = pe.Image; image = pe.Image;
key = fatherId.ToString(); key = fatherId.ToString();
personId = fatherId;
data = pe.Sex; data = pe.Sex;
} }
} }
@@ -390,7 +392,7 @@ public partial class PersonRepository : IPerson
citem.Data = new(); citem.Data = new();
citem.Label = pName; citem.Label = pName;
if (image != null) if (image != null)
citem.Data.Image = await LoadPersonImage(image); citem.Data.Image = await LoadPersonImage(personId, (int) enumPhotoType.Profile, image);
citem.Icon = "P"; citem.Icon = "P";
@@ -411,7 +413,7 @@ public partial class PersonRepository : IPerson
child.Type = type; child.Type = type;
image = dto.Image; image = dto.Image;
if (image != null) if (image != null)
child.Data.Image = await LoadPersonImage(image); child.Data.Image = await LoadPersonImage(dto.Id,(int) enumPhotoType.Profile, image);
child.Icon = "C"; child.Icon = "C";
child.StyleClass = getStyleClass(child.Icon); child.StyleClass = getStyleClass(child.Icon);
child.Label = dto.FirstName; child.Label = dto.FirstName;
@@ -480,12 +482,12 @@ public partial class PersonRepository : IPerson
/// <param name="fileName"></param> /// <param name="fileName"></param>
/// <param name="familyId"></param> /// <param name="familyId"></param>
/// <param name="base64"></param> /// <param name="base64"></param>
private string SaveFile(string fileName, int familyId, string base64) private async Task<string> SaveFile(string fileName, int personId, string base64)
{ {
string path = ""; string path = "";
int statusCode = 0; int statusCode = 0;
string error = ""; // string error = "";
string sdate = GetCurrentDateTime(); string sdate = GetCurrentDateTime();
string filename, extention; string filename, extention;
filename = extention = ""; filename = extention = "";
@@ -494,14 +496,33 @@ public partial class PersonRepository : IPerson
string rbase = base64.Substring(first, last - first); string rbase = base64.Substring(first, last - first);
byte[] newBytes = Convert.FromBase64String(rbase); byte[] newBytes = Convert.FromBase64String(rbase);
extention = System.IO.Path.GetExtension(fileName);
filename = personId + "_" + sdate + extention;
// Wrap the byte array in a MemoryStream
var stream = new MemoryStream(newBytes);
// Create the IFormFile instance
IFormFile file = new FormFile(stream, 0, newBytes.Length, "Profile", filename)
{
Headers = new HeaderDictionary(),
ContentType = "application/octet-stream" // Change to your specific MIME type if known (e.g., "image/jpeg", "application/pdf")
};
//save to database.
UploadCriteria criteria = new();
criteria.FileName = filename;
criteria.PersonId = personId;
criteria.PhotoType = (int) enumPhotoType.Profile;
criteria.File = file;
var rval = await _personPhoto.SaveAsync(criteria);
/*
path = _config["AppSettings:ImageFolder"]; path = _config["AppSettings:ImageFolder"];
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
} }
extention = System.IO.Path.GetExtension(fileName);
filename = familyId + "_" + sdate + extention;
string fullpath = System.IO.Path.Combine(path, filename); string fullpath = System.IO.Path.Combine(path, filename);
/* /*
using (var fileStream = new FileStream(fullpath, FileMode.Create)) using (var fileStream = new FileStream(fullpath, FileMode.Create))
@@ -520,10 +541,12 @@ public partial class PersonRepository : IPerson
} }
*/ */
/*
using (FileStream fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.None)) using (FileStream fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.None))
{ {
fileStream.Write(newBytes, 0, newBytes.Length); fileStream.Write(newBytes, 0, newBytes.Length);
} }
*/
return filename; return filename;
} }
@@ -571,7 +594,7 @@ public partial class PersonRepository : IPerson
if (!string.IsNullOrEmpty(container.FileName)) if (!string.IsNullOrEmpty(container.FileName))
{ {
string image = SaveFile(container.FileName, result, container.FormData); string image = await SaveFile(container.FileName, result, container.FormData);
// update the image in table to new // update the image in table to new
model.Image = image; model.Image = image;
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
@@ -587,7 +610,7 @@ public partial class PersonRepository : IPerson
result = item.Id; result = item.Id;
if (!string.IsNullOrEmpty(container.FileName)) if (!string.IsNullOrEmpty(container.FileName))
{ {
string image = SaveFile(container.FileName, result, container.FormData); string image = await SaveFile(container.FileName, result, container.FormData);
// update the image in table to new // update the image in table to new
model1.Image = image; model1.Image = image;
} }
@@ -714,17 +737,37 @@ public partial class PersonRepository : IPerson
}; };
} }
private async Task<string> LoadPersonImage(string filename) private async Task<string> LoadPersonImage_old(string filename)
{ {
string result = ""; string result = "";
DownloadFileCriteria criteria = new() { FileName = filename }; DownloadFileCriteria criteria = new() { FileName = filename };
var resultModel = await _importPersonRepository.DownloadFile(criteria); var resultModel = await _importPersonRepository.DownloadFile(criteria);
if (!string.IsNullOrEmpty((resultModel.Data)))
if (!string.IsNullOrEmpty(resultModel.Data))
result = "data:image/png;base64," + resultModel.Data; result = "data:image/png;base64," + resultModel.Data;
return result; return result;
} }
private async Task<string> LoadPersonImage(int personId, int photoType, string filename)
{
string result = "";
// DownloadFileCriteria criteria = new() { FileName = filename };
var resultModel = await _personPhoto.DownloadPersonPhotoPerson(personId, photoType , filename);
if (resultModel.StatusCode > 0)
{
string base64String = Convert.ToBase64String(resultModel.Data.Content);
if (!string.IsNullOrEmpty(base64String))
result = "data:image/png;base64," + base64String;
}
return result;
}
public ResultModel<int> DeleteUploadFile(DeleteFileCriteria criteria) public ResultModel<int> DeleteUploadFile(DeleteFileCriteria criteria)
{ {
int result = -1; int result = -1;
-1
View File
@@ -66,7 +66,6 @@ public class Seed
var conn = _context.Database.GetDbConnection(); var conn = _context.Database.GetDbConnection();
try try
{ {
conn.Open(); conn.Open();
var command = conn.CreateCommand(); var command = conn.CreateCommand();
command.CommandText = sptext; command.CommandText = sptext;
@@ -146,7 +146,7 @@ public class UserServiceRepository : IUserService
AuthenticateResponse retval = null; AuthenticateResponse retval = null;
string error = "user name is not in DB or user Name profile is not generic"; string error = "user name is not in DB or user Name profile is not generic";
int statuscode = 0; int statuscode = 0;
string webAPIUrl = _appSettings.LoginWebAPI; string webAPIUrl = "_appSettings.LoginWebAPI";
//KCO, D204KCO //KCO, D204KCO
//now check the adminuser table //now check the adminuser table
//ward clerk //ward clerk
+9 -8
View File
@@ -1,12 +1,12 @@
{ {
"AppSettings": { "AppSettings": {
"Secret": "Nepean Blue Mountain Super Secret SIGN AND VERIFY JWT TOKENS, BEARER TOKEN USE WHEN CALLING THIS API.", "Secret": "Super Secret SIGN AND VERIFY JWT TOKENS, BEARER TOKEN USE WHEN CALLING THIS API.",
"SQLConnectionString_o": "host=localhost;port=5432;database=FamilyTreeDB;username=postgres;password=Positive~1;", "iSQLConnectionString_i": "host=localhost;port=5432;database=familytreedb;username=postgres;password=Positive~1;",
"SQLConnectionString": "host=192.168.1.102;port=5432;database=FamilyTreeDB;username=postgres;password=Positive~1;", "SQLConnectionString": "host=127.0.0.1;port=5432;database=familytreedb;username=postgres;password=Positive~1;",
"ImageFolder": "c:\\temp\\Family", "ImageFolder": "/temp/Family",
"ImportFolder": "c:\\temp" "ImportFolder": "/temp"
}, },
"Logging": { "Logging": {
@@ -17,11 +17,12 @@
} }
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel_not_use": {
"Kestrel": {
"Endpoints": { "Endpoints": {
"Http": { "Http": {
"Url": "http://*:5015" "Url": "http://localhost:5016"
}
} }
} }
} }
}
+7 -5
View File
@@ -1,15 +1,17 @@
[Unit] [Unit]
Description=ASP.NET Core Web App running on Ubuntu Description=ASP.NET Core Web App running on arch linux
[Service] [Service]
WorkingDirectory=/var/www/familytree/api WorkingDirectory=/var/www/familytree/api
ExecStart=/usr/bin/dotnet /var/www/fadmilytree/api/FamilyTreeAPI.dll ExecStart=/usr/bin/dotnet /var/www/familytree/api/FamilyTreeAPI.dll
Restart=always Restart=always
# Restart service after 10 seconds if the dotnet service crashes: # Restart service after 20 seconds if the dotnet service crashes:
RestartSec=10 RestartSec=20
KillSignal=SIGINT KillSignal=SIGINT
SyslogIdentifier=dotnet-web-familytree-api SyslogIdentifier=dotnet-web-familytree-api
# This user should exist on the server and have ownership of the deployment directory # This user should exist on the server and have ownership of the deployment directory
User=www-data #User=www-data this is other ubuntu
#arch linux has user http
User=http
Environment=ASPNETCORE_ENVIRONMENT=Production Environment=ASPNETCORE_ENVIRONMENT=Production
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+44
View File
@@ -0,0 +1,44 @@
#make sure in nginx.conf has include module.d/*.conf inside http block one.
server {
listen 80; #make nginx out side listen 82;
location /familytreeapi/ {
#proxy_pass http://127.0.0.1:5015;
proxy_pass http://192.168.1.219:5015/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
return 301 /familytreeui/; #if same one redirect to /familytreeui/
}
location /familytreeui {
return 301 /familytreeui/; #if same one redirect to /familytreeui/
}
location /familytreeui/ {
#root /var/www/familytree/; #path is real must exist like /var/www/familytree/familytreeui and /var/www/familytree/api folder.
alias /var/www/familytree/ui/; #location does not to match. but angular index baseurl must same as location.
#index index.html;
try_files $uri $uri/ /familytreeui/index.html /index.html;
}
# Optional: Cache static assets for better performance
#location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
# expires 30d;
# add_header Cache-Control "public";
#}
# error_page 404 /index.html;
# put in /etc/nginx/sites-availalble/familytreeapp.conf
# link to enable
# $) sudo ln -s /etc/nginx/sites-availalble/familytreeapp.conf /etc/nginx/sites-enabled/
}
+123
View File
@@ -0,0 +1,123 @@
user http;
worker_processes auto;
worker_cpu_affinity auto;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
# Load all installed modules
#include modules.d/*.conf;
events {
worker_connections 1024;
}
http {
charset utf-8;
include mime.types;
default_type application/octet-stream;
types_hash_max_size 4096;
client_max_body_size 16M;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
include modules.d/*.conf;
server {
listen 81;
server_name localhost;
#access_log logs/host.access.log main;
#kham comment out first
#location / {
# root /usr/share/nginx/html;
# index index.html index.htm;
# }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
+3193 -3673
View File
File diff suppressed because it is too large Load Diff
+18 -18
View File
@@ -23,35 +23,35 @@
"private": true, "private": true,
"packageManager": "npm@11.6.4", "packageManager": "npm@11.6.4",
"dependencies": { "dependencies": {
"@angular/common": "^21.0.0", "@angular/common": "^22.0.0",
"@angular/compiler": "^21.0.0", "@angular/compiler": "^22.0.0",
"@angular/core": "^21.0.0", "@angular/core": "^22.0.0",
"@angular/forms": "^21.0.0", "@angular/forms": "^22.0.0",
"@angular/platform-browser": "^21.0.0", "@angular/platform-browser": "^22.0.0",
"@angular/router": "^21.0.0", "@angular/router": "^22.0.0",
"@ngrx/effects": "^21.0.1", "@ngrx/effects": "^22.0.0",
"@ngrx/store": "^21.0.1", "@ngrx/store": "^22.0.0",
"@ngrx/store-devtools": "^21.0.1", "@ngrx/store-devtools": "^22.0.0",
"@primeuix/themes": "^2.0.2", "@openng/optimus-ui": "^2.0.2",
"@tailwindcss/postcss": "^4.1.17", "@tailwindcss/postcss": "^4.1.17",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"moment": "^2.30.1", "moment": "^2.30.1",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"primeicons": "^7.0.0",
"primeng": "^21.0.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"tailwindcss-primeui": "^0.6.1",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"xlsx": "^0.18.5" "xlsx": "^0.18.5",
"@openng/icons": "^1.0.0",
"@openng/optimus-ui-themes": "^2.0.2",
"@openng/optimus-ui-tailwindcss": "^2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "^21.2.7", "@angular/build": "^22.0.0",
"@angular/cli": "^21.2.7", "@angular/cli": "^22.0.0",
"@angular/compiler-cli": "^21.0.0", "@angular/compiler-cli": "^22.0.0",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"jsdom": "^27.1.0", "jsdom": "^27.1.0",
"typescript": "~5.9.2", "typescript": "~6.0.0",
"vitest": "^4.0.8" "vitest": "^4.0.8"
} }
} }
+3 -1
View File
@@ -2,5 +2,7 @@
"baseUrl1": "http://192.168.8.188:5015", "baseUrl1": "http://192.168.8.188:5015",
"baseUrl": "http://localhost:5016", "baseUrl": "http://localhost:5016",
"baseUrl_docker": "http://localhost:8080", "baseUrl_docker": "http://localhost:8080",
"attachment": "http://localhost/document/family" "attachment": "http://localhost/document/family",
"username": "kham.vilaythong@gmail.com",
"prefill_ps": "passwor"
} }
+5 -5
View File
@@ -1,9 +1,9 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners , import { ApplicationConfig, provideBrowserGlobalErrorListeners ,
provideAppInitializer} from '@angular/core'; provideAppInitializer} from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router'; import { provideRouter, withComponentInputBinding } from '@angular/router';
import { providePrimeNG } from 'primeng/config'; import { provideOptimus } from '@openng/optimus-ui/config';
import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { ConfirmationService, MessageService } from 'primeng/api'; import { ConfirmationService, MessageService } from '@openng/optimus-ui/api';
import { routes } from './app.routes'; import { routes } from './app.routes';
import MyPreset from './mythem'; import MyPreset from './mythem';
import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares'; import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares';
@@ -29,13 +29,13 @@ export const appConfig: ApplicationConfig = {
provideAppInitializer(initializeApp()), provideAppInitializer(initializeApp()),
provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])), provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])),
provideRouter(routes, withComponentInputBinding()), provideRouter(routes, withComponentInputBinding()),
providePrimeNG({ provideOptimus({
theme: { theme: {
preset: MyPreset, preset: MyPreset,
options: { options: {
cssLayer: { cssLayer: {
name: 'primeng', name: 'optimus',
order: 'theme, base, primeng' order: 'theme, base, optimus'
} }
} }
} }
+2 -2
View File
@@ -2,8 +2,8 @@
import { Component, signal } from '@angular/core'; import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import { ToolbarComponent } from './toolbar/toolbar.component'; import { ToolbarComponent } from './toolbar/toolbar.component';
import { ToastModule } from 'primeng/toast'; import { ToastModule } from '@openng/optimus-ui/toast';
import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { ConfirmDialogModule } from '@openng/optimus-ui/confirmdialog';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule], imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule],
+5 -5
View File
@@ -1,15 +1,15 @@
import { ChangeDetectorRef, Component, inject, Inject, OnDestroy, OnInit, signal } from '@angular/core'; import { ChangeDetectorRef, Component, inject, Inject, OnDestroy, OnInit, signal } from '@angular/core';
import { DynamicDialogModule, DynamicDialogRef ,DynamicDialogConfig} from 'primeng/dynamicdialog'; import { DynamicDialogModule, DynamicDialogRef ,DynamicDialogConfig} from '@openng/optimus-ui/dynamicdialog';
import { LookupEdit } from '../models'; import { LookupEdit } from '../models';
import { UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; import { UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { ConfirmationService, MessageService } from 'primeng/api'; import { ConfirmationService, MessageService } from '@openng/optimus-ui/api';
import { LookupService } from '../shares'; import { LookupService } from '../shares';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CheckboxModule } from 'primeng/checkbox'; import { CheckboxModule } from '@openng/optimus-ui/checkbox';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
@Component({ @Component({
selector: 'add-photo', selector: 'add-photo',
imports: [CommonModule, ReactiveFormsModule, imports: [CommonModule, ReactiveFormsModule,
+2 -1
View File
@@ -16,7 +16,7 @@
<th>Id</th> <th>Id</th>
<th pSortableColumn="description">Description<p-sortIcon field="description"></p-sortIcon> <th pSortableColumn="description">Description<p-sortIcon field="description"></p-sortIcon>
</th> </th>
<th>Photo Type</th>
<th>Action</th> <th>Action</th>
</tr> </tr>
@@ -25,6 +25,7 @@
<tr> <tr>
<td>{{item.id}}</td> <td>{{item.id}}</td>
<td >{{item.photo}}</td> <td >{{item.photo}}</td>
<td>{{item.photoType}}</td>
<td> <td>
<button pButton type="button" icon="pi pi-times" <button pButton type="button" icon="pi pi-times"
+9 -9
View File
@@ -2,16 +2,16 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core'; import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, output } from '@angular/core';
import { catchError, EMPTY, finalize, Subscription } from 'rxjs'; import { catchError, EMPTY, finalize, Subscription } from 'rxjs';
import { CommonUtilities, HttpUtility, LookupService } from '../shares'; import { CommonUtilities, HttpUtility, LookupService } from '../shares';
import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from 'primeng/dynamicdialog'; import { DialogService ,DynamicDialogConfig,DynamicDialogModule, DynamicDialogRef} from '@openng/optimus-ui/dynamicdialog';
import { ConfirmationService, MessageService } from 'primeng/api'; import { ConfirmationService, MessageService } from '@openng/optimus-ui/api';
import { AddPhoto } from './add.photo'; import { AddPhoto } from './add.photo';
import { PersonPhotoDto } from '../models'; import { PersonPhotoDto } from '../models';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { TooltipModule } from 'primeng/tooltip'; import { TooltipModule } from '@openng/optimus-ui/tooltip';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { HttpResponse } from '@angular/common/http'; import { HttpResponse } from '@angular/common/http';
import { PersonService } from '../person'; import { PersonService } from '../person';
@@ -69,11 +69,11 @@ export class PhotoList implements OnInit, OnDestroy {
downloadAttachment(id: number): void { downloadAttachment(id: number): void {
//GetReportFile //GetReportFile
let typeofCall = "personId_" + id.toString(); let typeofCall = "personPhotoId_" + id.toString();
let criteria:any ={id, fileName:''}; let criteria: any = { id };
this.downloadFile.set(false); this.downloadFile.set(false);
this.http.getFileResponse("api/FileUpload/downloadPersonPhoto", criteria).pipe( this.http.getFileResponse("api/FileUpload/DownloadPersonPhotoById", criteria).pipe(
catchError(err => { catchError(err => {
console.error(err.message); console.error(err.message);
return EMPTY; return EMPTY;
@@ -128,7 +128,7 @@ doRemove(id: number): void {
} }
else else
{ {
const deletepersonPhoto$ = this.personService.deletePersonPhotoFile(id); const deletepersonPhoto$ = this.personService.deletePersonPhotoFileById(id);
this.subscription.add(deletepersonPhoto$.subscribe( this.subscription.add(deletepersonPhoto$.subscribe(
{ {
next: x => { next: x => {
+5 -5
View File
@@ -1,9 +1,9 @@
import { Component, inject, OnInit } from '@angular/core'; import { Component, inject, OnInit } from '@angular/core';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { FileUploadEvent, FileUploadModule } from 'primeng/fileupload'; import { FileUploadEvent, FileUploadModule } from '@openng/optimus-ui/fileupload';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import { FileUpload } from 'primeng/fileupload'; import { FileUpload } from '@openng/optimus-ui/fileupload';
import { ToastModule } from 'primeng/toast'; import { ToastModule } from '@openng/optimus-ui/toast';
import { AppSettingService } from '../shares'; import { AppSettingService } from '../shares';
/* /*
interface UploadEvent { interface UploadEvent {
+10 -8
View File
@@ -5,15 +5,15 @@ import { first } from 'rxjs/operators';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { ConfirmationService } from 'primeng/api'; import { ConfirmationService } from '@openng/optimus-ui/api';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CardModule } from 'primeng/card'; import { CardModule } from '@openng/optimus-ui/card';
import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { Utils } from '../shares'; import { AppSettingService, Utils } from '../shares';
@Component({ @Component({
templateUrl: './login.html', templateUrl: './login.html',
styleUrl: './login.css', styleUrl: './login.css',
@@ -29,6 +29,7 @@ export class Login implements OnInit, OnDestroy {
router = inject(Router); router = inject(Router);
authenticationService = inject(AuthenticationService); authenticationService = inject(AuthenticationService);
formBuilder = inject(FormBuilder); formBuilder = inject(FormBuilder);
private appSetting =inject(AppSettingService);
loading = false; loading = false;
homeUrl = "/person"; homeUrl = "/person";
submitted = false; submitted = false;
@@ -40,10 +41,11 @@ export class Login implements OnInit, OnDestroy {
private subscription: Subscription = new Subscription(); private subscription: Subscription = new Subscription();
constructor() { constructor() {
const default_us = this.appSetting.appSetting.username;
const pass = this.appSetting.appSetting.prefill_ps;
this.loginForm = this.formBuilder.group({ this.loginForm = this.formBuilder.group({
username: ['kham.vilaythong@gmail.com', Validators.required], username: [default_us, Validators.required],
password: ['password', Validators.required], password: [pass, Validators.required],
}); });
} }
+4 -1
View File
@@ -15,7 +15,10 @@ export enum mState {
Modified = 1, Modified = 1,
New =2, New =2,
}; };
export enum enumPhoto {
Profile = 1,
Other = 2
}
export enum userRole { export enum userRole {
Normal = 1, Normal = 1,
Admin = 2, Admin = 2,
+28 -37
View File
@@ -1,51 +1,41 @@
//mypreset.ts //mypreset.ts
import { definePreset } from '@primeuix/themes'; import { definePreset } from '@openng/optimus-ui-themes';
import Aura from '@primeuix/themes/aura'; import Aura from '@openng/optimus-ui-themes/aura';
import { primitive } from '@primeuix/themes/aura/base';
const MyPreset = definePreset(Aura, { const MyPreset = definePreset(Aura, {
semantic: { semantic: {
colorScheme: {
primitive: {
cyan: {
50: '{cyan.50}',
100: '{cyan.100}',
200: '{cyan.200}',
300: '{cyan.300}',
400: '{cyan.400}',
500: '{cyan.500}',
}
},
primary: { primary: {
50: '{zinc.50}', 50: '{blue.50}',
100: '{zinc.100}', 100: '{blue.100}',
200: '{zinc.200}', 200: '{blue.200}',
300: '{zinc.300}', 300: '{blue.300}',
400: '{zinc.400}', 400: '{blue.400}',
500: '{zinc.500}', 500: '{blue.500}',
600: '{zinc.600}', 600: '{blue.600}',
700: '{zinc.700}', 700: '{blue.700}',
800: '{zinc.800}', 800: '{blue.800}',
900: '{zinc.900}', 900: '{blue.900}',
950: '{zinc.950}' 950: '{blue.950}'
}, },
colorScheme: {
light: { light: {
surface: { surface: {
0: '#ffffff', 0: '#ffffff',
50: '{zinc.50}', 50: '{blue.50}',
100: '{zinc.100}', 100: '{blue.100}',
200: '{zinc.200}', 200: '{blue.200}',
300: '{zinc.300}', 300: '{blue.300}',
400: '{zinc.400}', 400: '{blue.400}',
500: '{zinc.500}', 500: '{blue.500}',
600: '{zinc.600}', 600: '{blue.600}',
700: '{zinc.700}', 700: '{blue.700}',
800: '{zinc.800}', 800: '{blue.800}',
900: '{zinc.900}', 900: '{blue.900}',
950: '{zinc.950}' 950: '{blue.950}'
} }
}, },
dark: { dark: {
@@ -65,6 +55,7 @@ const MyPreset = definePreset(Aura, {
} }
} }
} }
} }
}); });
+10 -10
View File
@@ -1,23 +1,23 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core'; import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person, RelationShip } from '../models'; import { StaffView ,StaffSearch, Person, RelationShip } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart'; import { OrganizationChartModule } from '@openng/optimus-ui/organizationchart';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { PersonService } from './person.service'; import { PersonService } from './person.service';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { TreeNode } from 'primeng/api'; import { TreeNode } from '@openng/optimus-ui/api';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DialogService, DynamicDialogConfig, DynamicDialogRef } from '@openng/optimus-ui/dynamicdialog';
import { PersonEdit } from './person.edit'; import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree'; import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from '@openng/optimus-ui/tree';
import { Utils } from '../shares'; import { Utils } from '../shares';
import { PopoverModule } from 'primeng/popover'; import { PopoverModule } from '@openng/optimus-ui/popover';
@Component({ @Component({
selector: 'family-orga', selector: 'family-orga',
@@ -64,7 +64,7 @@ export class FamilyOrga implements OnInit, OnDestroy{
{ {
expanded: true, expanded: true,
type: 'person', type: 'person',
styleClass: 'bg-indigo-500 text-white', styleClass: 'bg-blue-500 text-white',
data: { data: {
image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png', image: 'https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png',
name: 'Amy Elsner', name: 'Amy Elsner',
+9 -9
View File
@@ -1,24 +1,24 @@
import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core'; import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core';
import { StaffView ,StaffSearch, Person } from '../models'; import { StaffView ,StaffSearch, Person } from '../models';
import { OrganizationChartModule } from 'primeng/organizationchart'; import { OrganizationChartModule } from '@openng/optimus-ui/organizationchart';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { PersonService } from './person.service'; import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { TreeNode } from 'primeng/api'; import { TreeNode } from '@openng/optimus-ui/api';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { DialogService } from 'primeng/dynamicdialog'; import { DialogService } from '@openng/optimus-ui/dynamicdialog';
import { PersonEdit } from './person.edit'; import { PersonEdit } from './person.edit';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import { CheckboxModule } from 'primeng/checkbox'; import { CheckboxModule } from '@openng/optimus-ui/checkbox';
import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree'; import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from '@openng/optimus-ui/tree';
import { Utils } from '../shares'; import { Utils } from '../shares';
@Component({ @Component({
+8 -8
View File
@@ -8,17 +8,17 @@ import { Subscription } from 'rxjs';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { PersonService } from './person.service'; import { PersonService } from './person.service';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { Table, TableModule } from 'primeng/table'; import { Table, TableModule } from '@openng/optimus-ui/table';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { DialogService } from 'primeng/dynamicdialog'; import { DialogService } from '@openng/optimus-ui/dynamicdialog';
import { PersonEdit } from './person.edit'; import { PersonEdit } from './person.edit';
import { ConfirmationService, MenuItem, MessageService } from 'primeng/api'; import { ConfirmationService, MenuItem, MessageService } from '@openng/optimus-ui/api';
import { IconFieldModule } from 'primeng/iconfield'; import { IconFieldModule } from '@openng/optimus-ui/iconfield';
import { InputIconModule } from 'primeng/inputicon'; import { InputIconModule } from '@openng/optimus-ui/inputicon';
import { Menu, MenuModule } from 'primeng/menu'; import { Menu, MenuModule } from '@openng/optimus-ui/menu';
import { FamilyOrga } from './family.orga'; import { FamilyOrga } from './family.orga';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
+25 -18
View File
@@ -1,27 +1,28 @@
import { ChangeDetectorRef, Component, inject, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, inject, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; import {DialogService, DynamicDialogRef} from '@openng/optimus-ui/dynamicdialog';
import {DynamicDialogConfig} from 'primeng/dynamicdialog'; import {DynamicDialogConfig} from '@openng/optimus-ui/dynamicdialog';
import { FormControl, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms'; import { FormControl, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subject, Subscription} from 'rxjs'; import { Subject, Subscription} from 'rxjs';
import { ConfirmationService, MessageService } from 'primeng/api'; import { ConfirmationService, MessageService } from '@openng/optimus-ui/api';
import { DatePickerModule } from 'primeng/datepicker'; import { DatePickerModule } from '@openng/optimus-ui/datepicker';
import { Code, RelationShipView, Person, mState, RelationShip, PersonPhotoDto} from '../models'; import { Code, RelationShipView, Person, mState, RelationShip, PersonPhotoDto, enumPhoto } from '../models';
import { AppSettingService, LookupService, Utils } from '../shares'; import { AppSettingService, LookupService, Utils } from '../shares';
import { PersonService } from './person.service'; import { PersonService } from './person.service';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { SelectModule } from 'primeng/select'; import { SelectModule } from '@openng/optimus-ui/select';
import { CheckboxModule } from 'primeng/checkbox'; import { CheckboxModule } from '@openng/optimus-ui/checkbox';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import moment from 'moment'; import moment from 'moment';
import { HttpEvent } from '@angular/common/http'; import { HttpEvent } from '@angular/common/http';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { Pickperson } from '../pickperson/pickperson'; import { Pickperson } from '../pickperson/pickperson';
import { ImageDisplayComponent } from '../pickperson/image.display'; import { ImageDisplayComponent } from '../pickperson/image.display';
import { TooltipModule } from 'primeng/tooltip'; import { TooltipModule } from '@openng/optimus-ui/tooltip';
import { AutoFocusModule } from 'primeng/autofocus'; import { AutoFocusModule } from '@openng/optimus-ui/autofocus';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
import { PhotoList } from '../attachphoto/photolist'; import { PhotoList } from '../attachphoto/photolist';
import { InputNumberDesignTokens } from '@openng/optimus-ui-themes/types/inputnumber';
export interface FileUploadEvent { export interface FileUploadEvent {
originalEvent: HttpEvent<any>; originalEvent: HttpEvent<any>;
@@ -190,9 +191,9 @@ doDeleteFile(): void {
this.profileFile = null; this.profileFile = null;
} }
doDeleteImage(fileName:string) : void { doDeleteImage(fileName:string) : void {
const familyId = this.adminuserForm.value.id; const personId = this.adminuserForm.value.id;
const data = { fileName, familyId }; const photoType = enumPhoto.Profile;
const delete$ = this.personService.deleteUploadFile(data); const delete$ = this.personService.deletePersonPhotoFile(personId, photoType);
this.subscription.add(delete$.subscribe( this.subscription.add(delete$.subscribe(
{ {
next: x => { next: x => {
@@ -307,6 +308,8 @@ doViewImage(imageName:string): void {
const ref = this.dialogService.open(ImageDisplayComponent, { const ref = this.dialogService.open(ImageDisplayComponent, {
data: { data: {
imageName, imageName,
personId: this._id,
photyType: enumPhoto.Profile
}, },
header: 'View Image', header: 'View Image',
draggable: true, draggable: true,
@@ -358,7 +361,7 @@ assignValue(item:Person): void {
} }
if (item.image && item.image.length > 0) if (item.image && item.image.length > 0)
{ {
this.loadImage(item.image); this.loadImage(item.image, item.id, enumPhoto.Profile);
} }
// disable use false//true for not disable. // disable use false//true for not disable.
this.subChanged$.next(true); this.subChanged$.next(true);
@@ -367,8 +370,10 @@ assignValue(item:Person): void {
// convenience getter for easy access in form fields // convenience getter for easy access in form fields
get f() { return this.adminuserForm.controls;} get f() { return this.adminuserForm.controls;}
loadImage(fileName: string| null): void { loadImage(fileName: string | null,
const download = this.personService.downloadFile(fileName!); personId: number, photoType: number): void {
if (fileName) {
const download = this.personService.downloadPersonPhoto64(fileName, personId, photoType);
this.subscription.add(download.subscribe({ this.subscription.add(download.subscribe({
next: x => { next: x => {
if (x.statusCode == 1) if (x.statusCode == 1)
@@ -385,6 +390,8 @@ assignValue(item:Person): void {
error: e => console.error("error in download image", e) error: e => console.error("error in download image", e)
})); }));
} }
}
displayIamge64(baseImage: string): void displayIamge64(baseImage: string): void
{ {
const changeUrl = (this.sanitizer.bypassSecurityTrustResourceUrl(baseImage) as any).changingThisBreaksApplicationSecurity; const changeUrl = (this.sanitizer.bypassSecurityTrustResourceUrl(baseImage) as any).changingThisBreaksApplicationSecurity;
+37 -10
View File
@@ -4,7 +4,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models'; import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models';
import { AppSettingService } from '../shares'; import { AppSettingService } from '../shares';
import { TreeNode } from 'primeng/api'; import { TreeNode } from '@openng/optimus-ui/api';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class PersonService { export class PersonService {
@@ -77,33 +77,60 @@ export class PersonService {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage"; const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage";
return this.http.post<ResultModel<string>>(baseUrl, data); return this.http.post<ResultModel<string>>(baseUrl, data);
} }
downloadFile(imageName: string): Observable<ResultModel<string>> { downloadFile_dontusethis(imageName: string, personId: number,
photoType: number): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile"; const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadFile";
const data = { const data = {
fileName: imageName fileName: imageName,
id: personId,
photoType
}; };
return this.http.post<ResultModel<string>>(baseUrl, data); return this.http.post<ResultModel<string>>(baseUrl, data);
} }
deleteUploadFile(data: any): Observable<ResultModel<number>> { deleteUploadFile_dotnotuse(data: any): Observable<ResultModel<number>> {
//data ={filename}; //data ={filename};
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile"; const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile";
return this.http.post<ResultModel<number>>(baseUrl, data); return this.http.post<ResultModel<number>>(baseUrl, data);
} }
deletePersonPhotoFile(id: number): Observable<ResultModel<number>> { deletePersonPhotoFile(personId: number, photoType: number): Observable<ResultModel<number>> {
const data ={id, fileName:''}; const data = {
id: personId,
fileName: '',
photoType
};
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto"; const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhoto";
return this.http.post<ResultModel<number>>(baseUrl, data); return this.http.post<ResultModel<number>>(baseUrl, data);
} }
deletePersonPhotoFileById(id: number): Observable<ResultModel<number>> {
const data = {
id
};
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DeletePersonPhotoById";
return this.http.post<ResultModel<number>>(baseUrl, data);
}
savePersonPhotoList(data:FormData): Observable<ResultModel<number>> { savePersonPhotoList(data:FormData): Observable<ResultModel<number>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/SavePersonPhoto"; const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/SavePersonPhoto";
return this.http.post<ResultModel<number>>(baseUrl, data); return this.http.post<ResultModel<number>>(baseUrl, data);
} }
downloadPersonPhoto(id: number): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto"; downloadPersonPhoto64(imageName: string, personId: number,
photoType: number): Observable<ResultModel<string>> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto64";
const data = { const data = {
id, id: personId,
fileName: '' photoType,
fileName: imageName
}; };
return this.http.post<ResultModel<string>>(baseUrl, data); return this.http.post<ResultModel<string>>(baseUrl, data);
} }
downloadPersonPhoto(imageName: string, personId: number,
photoType: number): Observable<any> {
const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.FileUploadUrl + "/DownloadPersonPhoto";
const data = {
id: personId,
photoType,
fileName: imageName
};
return this.http.post<any>(baseUrl, data);
}
} }
+7 -5
View File
@@ -5,8 +5,8 @@ import { PersonService } from '../person';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DynamicDialogConfig, DynamicDialogRef } from '@openng/optimus-ui/dynamicdialog';
@Component({ @Component({
selector: 'app-image-display', selector: 'app-image-display',
@@ -28,11 +28,13 @@ export class ImageDisplayComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
const imageName = this.config.data.imageName; const imageName = this.config.data.imageName;
this.loadImage(imageName); const personId = this.config.data.personId;
const photoType = this.config.data.photyType;
this.loadImage(imageName, personId, photoType);
} }
loadImage(fileName: string| null): void { loadImage(fileName: string | null, personId: number, photoType: number): void {
const download = this.personService.downloadFile(fileName!); const download = this.personService.downloadPersonPhoto64(fileName!, personId, photoType);
this.subscription.add(download.subscribe({ this.subscription.add(download.subscribe({
next: x => { next: x => {
if (x.statusCode == 1) if (x.statusCode == 1)
+6 -6
View File
@@ -1,13 +1,13 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Table, TableModule } from 'primeng/table'; import { Table, TableModule } from '@openng/optimus-ui/table';
import { Person } from '../models'; import { Person } from '../models';
import { IconFieldModule } from 'primeng/iconfield'; import { IconFieldModule } from '@openng/optimus-ui/iconfield';
import { InputIconModule } from 'primeng/inputicon'; import { InputIconModule } from '@openng/optimus-ui/inputicon';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DynamicDialogConfig, DynamicDialogRef } from '@openng/optimus-ui/dynamicdialog';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
@Component({ @Component({
selector: 'app-pickperson', selector: 'app-pickperson',
+1 -1
View File
@@ -1,6 +1,6 @@
import moment from "moment"; import moment from "moment";
import { Person, User, userRole } from "../models" import { Person, User, userRole } from "../models"
import { TreeNode } from "primeng/api"; import { TreeNode } from "@openng/optimus-ui/api";
type MyProName = "fatherId" | "motherId"; type MyProName = "fatherId" | "motherId";
+3 -3
View File
@@ -9,11 +9,11 @@ import { Subscription } from 'rxjs';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { StaffService } from './staff.service'; import { StaffService } from './staff.service';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { TableModule } from 'primeng/table'; import { TableModule } from '@openng/optimus-ui/table';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
import { selectStaffLoaded,selectStaff } from '../state/staff.selectors'; import { selectStaffLoaded,selectStaff } from '../state/staff.selectors';
@Component({ @Component({
+5 -5
View File
@@ -2,15 +2,15 @@ import { Component, inject, input, numberAttribute, OnDestroy, OnInit } from '@a
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms'; import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subject, Subscription} from 'rxjs'; import { Subject, Subscription} from 'rxjs';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import {Staff, Code, userRole} from '../models'; import {Staff, Code, userRole} from '../models';
import { LookupService, Utils } from '../shares'; import { LookupService, Utils } from '../shares';
import { StaffService } from './staff.service'; import { StaffService } from './staff.service';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
import { SelectModule } from 'primeng/select'; import { SelectModule } from '@openng/optimus-ui/select';
import { CheckboxModule } from 'primeng/checkbox'; import { CheckboxModule } from '@openng/optimus-ui/checkbox';
import { InputTextModule } from 'primeng/inputtext'; import { InputTextModule } from '@openng/optimus-ui/inputtext';
@Component({ @Component({
templateUrl: 'staff.edit.component.html', templateUrl: 'staff.edit.component.html',
+2 -2
View File
@@ -2,13 +2,13 @@ import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms'; import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { Subject, Subscription} from 'rxjs'; import { Subject, Subscription} from 'rxjs';
import { MessageService } from 'primeng/api'; import { MessageService } from '@openng/optimus-ui/api';
import {Staff, Code, userRole, ResetPassword} from '../models'; import {Staff, Code, userRole, ResetPassword} from '../models';
import { LookupService, Utils } from '../shares'; import { LookupService, Utils } from '../shares';
import { StaffService } from './staff.service'; import { StaffService } from './staff.service';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ButtonModule } from 'primeng/button'; import { ButtonModule } from '@openng/optimus-ui/button';
@Component({ @Component({
templateUrl: 'staff.pass.component.html', templateUrl: 'staff.pass.component.html',
+8 -5
View File
@@ -1,17 +1,15 @@
@if (currentUserS() && isAuth) @if (currentUserS() && isAuth == true)
{ {
<div class="flex flex-column justify-between md:flex-row rounded" style="background-color:transparent"> <div class="flex flex-column justify-between md:flex-row rounded" style="background-color:transparent">
<div style="margin-top:5px;padding-right:35px; cursor: pointer;width:400px;"> <div style="margin-top:5px;padding-right:35px; cursor: pointer;width:400px;">
<img (click)="onHome()" src="images/application_image.png" alt="Banner" style="height:80px;width:100%" /> <img (click)="onHome()" src="images/application_image.png" alt="Banner" style="height:80px;width:100%" />
</div> </div>
<div class="flex justify-end items-end flex-row"> <div class="flex justify-end items-end flex-row">
<div class="flex-column"> <div class="flex-column">
<label class=" flex justify-end items-end mr-1 mb-2 myname">{{loginUser}}</label> <label class=" flex justify-end items-end mr-1 mb-2 myname">{{loginUser}}</label>
<div> <div>
<span class="md:hidden flex justify-end"> <!--hidden on md screen show on small-->
<button type="button" pButton icon="pi pi-align-justify" (click)="onemenu.toggle($event);"></button>
<p-menu #onemenu [popup]="true" [model]="oneMenu"></p-menu>
</span>
<span class="hidden md:inline-flex"> <!--hidden when small screen--> <span class="hidden md:inline-flex"> <!--hidden when small screen-->
<p-buttonGroup > <p-buttonGroup >
<button type="button" pButton icon="pi pi-slack" label="Home" (click)="onHome()" <button type="button" pButton icon="pi pi-slack" label="Home" (click)="onHome()"
@@ -38,4 +36,9 @@
</div> </div>
</div> </div>
</div> </div>
<!--put in the left new row if in mobile not much room-->
<span class="md:hidden flex justify-start"> <!--hidden on md screen show on small-->
<button type="button" pButton icon="pi pi-align-justify" (click)="onemenu.toggle($event);"></button>
<p-menu #onemenu [popup]="true" [model]="oneMenu"></p-menu>
</span>
} }
+4 -4
View File
@@ -1,10 +1,10 @@
import { Component, OnInit, OnDestroy, effect, computed, Signal, } from '@angular/core'; import { Component, OnInit, OnDestroy, effect, computed, Signal, } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MenuItem } from 'primeng/api'; import { MenuItem } from '@openng/optimus-ui/api';
import { MenuModule } from 'primeng/menu'; import { MenuModule } from '@openng/optimus-ui/menu';
import { ButtonModule} from 'primeng/button'; import { ButtonModule} from '@openng/optimus-ui/button';
import { ButtonGroupModule } from 'primeng/buttongroup'; import { ButtonGroupModule } from '@openng/optimus-ui/buttongroup';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { AuthenticationService } from '../user-services'; import { AuthenticationService } from '../user-services';
import { Utils } from '../shares'; import { Utils } from '../shares';
+2 -2
View File
@@ -1,7 +1,7 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
@import "tailwindcss"; @import "tailwindcss";
@plugin "tailwindcss-primeui"; @plugin "@openng/optimus-ui-tailwindcss";
@import "primeicons/primeicons.css"; @import "@openng/icons/openng-icons.css";
html, html,
body { body {
height: 100%; height: 100%;
-1
View File
@@ -10,7 +10,6 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"skipLibCheck": true, "skipLibCheck": true,
"isolatedModules": true, "isolatedModules": true,
"experimentalDecorators": true,
"importHelpers": true, "importHelpers": true,
"target": "ES2022", "target": "ES2022",
"module": "preserve" "module": "preserve"
+38
View File
@@ -0,0 +1,38 @@
import { HttpInterceptorFn } from '@angular/common/http';
export const credentialsInterceptor: HttpInterceptorFn = (req, next) => {
// Clone the request to add the withCredentials property
const clonedRequest = req.clone({
withCredentials: true
});
return next(clonedRequest);
};
typescriptimport { provideHttpClient, withInterceptors } from '@angular/common/http';
import { credentialsInterceptor } from './credentials.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([credentialsInterceptor]) // Register it here
)
]
};
2. Individual Request ConfigurationIf you only want to send cookies to specific endpoints,
pass the withCredentials option directly inside your service.
typescriptimport { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class ApiService {
private http = inject(HttpClient);
getData() {
return this.http.get('https://your-api-domain.com', {
withCredentials: true // Enables cookies for this request only
});
}
}
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh -e
docker-compose up
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh -e
docker-compose down
+40
View File
@@ -0,0 +1,40 @@
networks:
dev:
driver: bridge
services:
postgresdb:
image: postgres
container_name: ${DOCKER_REGISTRY-}postgresql-db
ports:
- "5432:5432"
environment:
- POSTGRES_DB=FamilyTreeDB
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=Positive~1
- PGDATA=/var/lib/postgresql/data/pgdata
restart: always
volumes:
- postgresdata:/var/lib/postgresql/data
networks:
- dev
pgadmin:
image: dpage/pgadmin4
container_name: pgadmin4_container
restart: always
depends_on:
- "postgresdb"
volumes:
- pgadmin-data:/var/lib/pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: postgres@domain.com
PGADMIN_DEFAULT_PASSWORD: Positive~1
ports:
- "5050:5050"
networks:
- dev
volumes:
pgadmin-data:
external: true
postgresdata:
external: true