Compare commits

..
4 Commits
Author SHA1 Message Date
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
14 changed files with 1577 additions and 1394 deletions
+31 -3
View File
@@ -1,6 +1,8 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "C#: FamilyTreeAPI", "name": "C#: FamilyTreeAPI",
"type": "coreclr", "type": "coreclr",
@@ -11,16 +13,42 @@
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"stopAtEntry": false, "stopAtEntry": false,
"console": "internalConsole", "console": "internalConsole",
"env": { "env": {
"ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5300" "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\\FamilyTreeAPI.dll",
"serverReadyAction": { "serverReadyAction": {
"action": "openExternally", "action": "openExternally",
"pattern": "Now listening on:\\s+(https?://\\S+)", "pattern": "listening on:\\s+(https?://\\S+)",
"uriFormat": "%s/swagger/index.html" "uriFormat": "%s/swagger/index.html"
} },
"env": {
"ASPNETCORE_ENVIRONMENT": "Production",
"ASPNETCORE_URLS": "https://localhost:5019"
},
"cwd": "${workspaceFolder}"
} }
] ]
} }
+19 -8
View File
@@ -1,20 +1,31 @@
{ {
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "build", "label": "build",
"command": "dotnet", "command": "dotnet",
"type": "process", "type": "shell",
"args": [ "args": [
"build", "build",
"${workspaceFolder}/FamilyTreeAPI.csproj", "${workspaceFolder}/FamilyTreeAPI/FamilyTreeAPI.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": "$msCompile" "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
@@ -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);
} }
+1 -1
View File
@@ -20,7 +20,7 @@ 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
@@ -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; }
+9 -9
View File
@@ -1,5 +1,4 @@
<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>
@@ -11,23 +10,24 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="16.0.0" /> <PackageReference Include="HotChocolate.AspNetCore" Version="16.6.1" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="16.0.0" /> <PackageReference Include="HotChocolate.Data.EntityFramework" Version="16.6.1" />
<PackageReference Include="HotChocolate.Pagination.EntityFramework" Version="14.3.1" /> <PackageReference Include="HotChocolate.Pagination.EntityFramework" Version="14.3.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.7" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.11">
<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.7"> <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> <ItemGroup>
+32
View File
@@ -13,6 +13,9 @@ 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 // Explicitly register the base paths for Linux container execution
builder.Configuration builder.Configuration
.SetBasePath(AppContext.BaseDirectory) // <-- Pulls from wherever the binary sits .SetBasePath(AppContext.BaseDirectory) // <-- Pulls from wherever the binary sits
@@ -220,4 +223,33 @@ query {
} }
} }
} }
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
*/ */
@@ -11,6 +11,12 @@
"applicationUrl": "http://localhost:5016" "applicationUrl": "http://localhost:5016"
}, },
"http": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5000"
},
"$schema": "https://json.schemastore.org/launchsettings.json", "$schema": "https://json.schemastore.org/launchsettings.json",
@@ -44,8 +44,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.HasValue)
photoType = criteria.PhotoType.Value.ToString();
HttpContext? httpContext = _httpContext.HttpContext; HttpContext? httpContext = _httpContext.HttpContext;
string loginName = ""; string loginName = "";
if (httpContext != null) if (httpContext != null)
@@ -62,7 +64,7 @@ namespace FamilyTreeAPI.Repository
PersonPhoto model; PersonPhoto model;
model = new(); model = new();
model.PhotoType = photoType;
model = FillModel(model,criteria.PersonId, formfile.FileName); model = FillModel(model,criteria.PersonId, formfile.FileName);
using (var stream = new MemoryStream()) using (var stream = new MemoryStream())
@@ -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,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,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,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;
} }
@@ -538,7 +561,7 @@ public partial class PersonRepository : IPerson
string loginName = ""; string loginName = "";
if (httpContext != null) if (httpContext != null)
{ {
UserDto? user = (UserDto?)httpContext.Items["User"]; UserDto? user = (UserDto?)httpContext.Items["User"];
if (user != null) if (user != null)
loginName = user.FirstName + " " + user.LastName; loginName = user.FirstName + " " + user.LastName;
} }
@@ -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,34 @@ 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,string filename)
{
string result = "";
// DownloadFileCriteria criteria = new() { FileName = filename };
var resultModel = await _personPhoto.DownloadPersonPhoto(personId, filename);
//if (resultModel.Data.Content)
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;
+5 -6
View File
@@ -3,7 +3,7 @@
"AppSettings": { "AppSettings": {
"Secret": "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.",
"iSQLConnectionString_i": "host=localhost;port=5432;database=FamilyTreeDB_prod;username=postgres;password=Positive~1;", "iSQLConnectionString_i": "host=localhost;port=5432;database=FamilyTreeDB_prod;username=postgres;password=Positive~1;",
"SQLConnectionString": "host=192.168.1.240;port=5432;database=FamilyTreeDB_prod;username=postgres;password=Positive~1;", "SQLConnectionString": "host=127.0.0.1;port=5432;database=familytreedb;username=postgres;password=Positive~1;",
"ImageFolder": "/temp/Family", "ImageFolder": "/temp/Family",
"ImportFolder": "/temp" "ImportFolder": "/temp"
@@ -17,13 +17,12 @@
} }
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"Kestrel": {
"Kestrel": {
"Endpoints": { "Endpoints": {
"Http": { "Http": {
"Url": "http://*:5015" "Url": "http://localhost:5016"
} }
} }
} }
} }
+1376 -1341
View File
File diff suppressed because it is too large Load Diff