Files
SkyArtShop/Services/PostgreSQLService.cs
Local Server 703ab57984 Fix admin route access and backend configuration
- Added /admin redirect to login page in nginx config
- Fixed backend server.js route ordering for proper admin handling
- Updated authentication middleware and routes
- Added user management routes
- Configured PostgreSQL integration
- Updated environment configuration
2025-12-13 22:34:11 -06:00

467 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Npgsql;
using NpgsqlTypes;
using SkyArtShop.Models;
namespace SkyArtShop.Services;
public class PostgreSQLService
{
private readonly string _connectionString;
public PostgreSQLService(string connectionString)
{
_connectionString = connectionString;
}
private async Task<NpgsqlConnection> GetConnectionAsync()
{
NpgsqlConnection conn = new NpgsqlConnection(_connectionString);
await conn.OpenAsync();
return conn;
}
public async Task<List<T>> GetAllAsync<T>(string tableName) where T : class, new()
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM " + tableName.ToLower();
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
List<T> results = new List<T>();
while (await reader.ReadAsync())
{
results.Add(MapToObject<T>(reader));
}
return results;
}
public async Task<T?> GetByIdAsync<T>(string tableName, string id) where T : class, new()
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM " + tableName.ToLower() + " WHERE id = @id";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("id", id);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
return MapToObject<T>(reader);
}
return null;
}
public async Task<AdminUser?> GetUserByEmailAsync(string email)
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM adminusers WHERE LOWER(email) = LOWER(@email) AND isactive = true";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("email", email);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
return MapToAdminUser(reader);
}
return null;
}
public async Task UpdateUserLastLoginAsync(string userId, DateTime lastLogin)
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "UPDATE adminusers SET lastlogin = @lastlogin WHERE id = @id";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("id", userId);
cmd.Parameters.AddWithValue("lastlogin", lastLogin);
await cmd.ExecuteNonQueryAsync();
}
public async Task CreateAdminUserAsync(AdminUser user)
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "INSERT INTO adminusers (id, email, passwordhash, name, role, permissions, isactive, createdby, createdat)\n VALUES (@id, @email, @passwordhash, @name, @role, @permissions::jsonb, @isactive, @createdby, @createdat)";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("id", Guid.NewGuid().ToString());
cmd.Parameters.AddWithValue("email", user.Email);
cmd.Parameters.AddWithValue("passwordhash", user.PasswordHash);
cmd.Parameters.AddWithValue("name", user.Name);
cmd.Parameters.AddWithValue("role", user.Role);
cmd.Parameters.AddWithValue("permissions", JsonSerializer.Serialize(user.Permissions));
cmd.Parameters.AddWithValue("isactive", user.IsActive);
cmd.Parameters.AddWithValue("createdby", user.CreatedBy);
cmd.Parameters.AddWithValue("createdat", user.CreatedAt);
await cmd.ExecuteNonQueryAsync();
}
public async Task<List<Product>> GetProductsAsync()
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM products WHERE isactive = true ORDER BY createdat DESC";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
List<Product> results = new List<Product>();
while (await reader.ReadAsync())
{
results.Add(MapToProduct(reader));
}
return results;
}
public async Task<Product?> GetProductBySlugAsync(string slug)
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM products WHERE slug = @slug AND isactive = true";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("slug", slug);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
return MapToProduct(reader);
}
return null;
}
public async Task<Page?> GetPageBySlugAsync(string slug)
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM pages WHERE pageslug = @slug AND isactive = true";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("slug", slug);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
return MapToPage(reader);
}
return null;
}
public async Task<SiteSettings?> GetSiteSettingsAsync()
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM sitesettings LIMIT 1";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
return MapToSiteSettings(reader);
}
return null;
}
public async Task<List<MenuItem>> GetMenuItemsAsync()
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "SELECT * FROM menuitems WHERE isactive = true ORDER BY displayorder";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
using NpgsqlDataReader reader = await cmd.ExecuteReaderAsync();
List<MenuItem> results = new List<MenuItem>();
while (await reader.ReadAsync())
{
results.Add(MapToMenuItem(reader));
}
return results;
}
public async Task InsertAsync<T>(string tableName, T entity) where T : class
{
using NpgsqlConnection conn = await GetConnectionAsync();
Type typeFromHandle = typeof(T);
List<PropertyInfo> list = (from p in typeFromHandle.GetProperties()
where p.CanRead && p.Name != "Id"
select p).ToList();
PropertyInfo property = typeFromHandle.GetProperty("Id");
if (property != null && string.IsNullOrEmpty(property.GetValue(entity)?.ToString()))
{
property.SetValue(entity, Guid.NewGuid().ToString());
}
string value = string.Join(", ", list.Select((PropertyInfo p) => p.Name.ToLower()).Prepend("id"));
string value2 = string.Join(", ", list.Select((PropertyInfo p) => "@" + p.Name.ToLower()).Prepend("@id"));
string cmdText = $"INSERT INTO {tableName.ToLower()} ({value}) VALUES ({value2})";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("id", property?.GetValue(entity)?.ToString() ?? Guid.NewGuid().ToString());
foreach (PropertyInfo item in list)
{
object value3 = item.GetValue(entity);
string parameterName = item.Name.ToLower();
if (value3 == null)
{
cmd.Parameters.AddWithValue(parameterName, DBNull.Value);
}
else if (item.PropertyType == typeof(List<string>) || item.PropertyType == typeof(List<ProductVariant>) || item.PropertyType == typeof(List<TeamMember>) || item.PropertyType == typeof(Dictionary<string, string>))
{
cmd.Parameters.AddWithValue(parameterName, NpgsqlDbType.Jsonb, JsonSerializer.Serialize(value3));
}
else if (item.PropertyType == typeof(DateTime) || item.PropertyType == typeof(DateTime?))
{
cmd.Parameters.AddWithValue(parameterName, value3);
}
else if (item.PropertyType == typeof(bool) || item.PropertyType == typeof(bool?))
{
cmd.Parameters.AddWithValue(parameterName, value3);
}
else
{
cmd.Parameters.AddWithValue(parameterName, value3);
}
}
await cmd.ExecuteNonQueryAsync();
}
public async Task UpdateAsync<T>(string tableName, string id, T entity) where T : class
{
using NpgsqlConnection conn = await GetConnectionAsync();
Type typeFromHandle = typeof(T);
List<PropertyInfo> list = (from p in typeFromHandle.GetProperties()
where p.CanRead && p.Name != "Id"
select p).ToList();
string value = string.Join(", ", list.Select((PropertyInfo p) => p.Name.ToLower() + " = @" + p.Name.ToLower()));
string cmdText = $"UPDATE {tableName.ToLower()} SET {value} WHERE id = @id";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("id", id);
foreach (PropertyInfo item in list)
{
object value2 = item.GetValue(entity);
string parameterName = item.Name.ToLower();
if (value2 == null)
{
cmd.Parameters.AddWithValue(parameterName, DBNull.Value);
}
else if (item.PropertyType == typeof(List<string>) || item.PropertyType == typeof(List<ProductVariant>) || item.PropertyType == typeof(List<TeamMember>) || item.PropertyType == typeof(Dictionary<string, string>))
{
cmd.Parameters.AddWithValue(parameterName, NpgsqlDbType.Jsonb, JsonSerializer.Serialize(value2));
}
else if (item.PropertyType == typeof(DateTime) || item.PropertyType == typeof(DateTime?))
{
cmd.Parameters.AddWithValue(parameterName, value2);
}
else if (item.PropertyType == typeof(bool) || item.PropertyType == typeof(bool?))
{
cmd.Parameters.AddWithValue(parameterName, value2);
}
else
{
cmd.Parameters.AddWithValue(parameterName, value2);
}
}
await cmd.ExecuteNonQueryAsync();
}
public async Task DeleteAsync<T>(string tableName, string id) where T : class
{
using NpgsqlConnection conn = await GetConnectionAsync();
string cmdText = "DELETE FROM " + tableName.ToLower() + " WHERE id = @id";
using NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("id", id);
await cmd.ExecuteNonQueryAsync();
}
private T MapToObject<T>(NpgsqlDataReader reader) where T : class, new()
{
Type typeFromHandle = typeof(T);
if (typeFromHandle == typeof(AdminUser))
{
return (MapToAdminUser(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(Product))
{
return (MapToProduct(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(Page))
{
return (MapToPage(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(MenuItem))
{
return (MapToMenuItem(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(SiteSettings))
{
return (MapToSiteSettings(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(PortfolioCategory))
{
return (MapToPortfolioCategory(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(PortfolioProject))
{
return (MapToPortfolioProject(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(BlogPost))
{
return (MapToBlogPost(reader) as T) ?? new T();
}
if (typeFromHandle == typeof(HomepageSection))
{
return (MapToHomepageSection(reader) as T) ?? new T();
}
return new T();
}
private AdminUser MapToAdminUser(NpgsqlDataReader reader)
{
AdminUser adminUser = new AdminUser();
adminUser.Id = reader["id"].ToString();
adminUser.Email = reader["email"].ToString() ?? "";
adminUser.PasswordHash = reader["passwordhash"].ToString() ?? "";
adminUser.Name = reader["name"].ToString() ?? "";
adminUser.Role = reader["role"].ToString() ?? "Admin";
adminUser.Permissions = JsonSerializer.Deserialize<List<string>>(reader["permissions"].ToString() ?? "[]") ?? new List<string>();
adminUser.IsActive = (reader["isactive"] as bool?) ?? true;
adminUser.CreatedBy = reader["createdby"]?.ToString() ?? "";
adminUser.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
adminUser.LastLogin = (reader.IsDBNull(reader.GetOrdinal("lastlogin")) ? ((DateTime?)null) : new DateTime?(reader.GetDateTime(reader.GetOrdinal("lastlogin"))));
adminUser.Phone = reader["phone"]?.ToString() ?? "";
adminUser.Notes = reader["notes"]?.ToString() ?? "";
return adminUser;
}
private Product MapToProduct(NpgsqlDataReader reader)
{
Product product = new Product();
product.Id = reader["id"].ToString();
product.Name = reader["name"].ToString() ?? "";
product.Slug = reader["slug"].ToString() ?? "";
product.SKU = reader["sku"]?.ToString() ?? "";
product.ShortDescription = reader["shortdescription"]?.ToString() ?? "";
product.Description = reader["description"]?.ToString() ?? "";
product.Price = (reader["price"] as decimal?).GetValueOrDefault();
product.Category = reader["category"]?.ToString() ?? "";
product.Color = reader["color"]?.ToString() ?? "";
product.Colors = JsonSerializer.Deserialize<List<string>>(reader["colors"].ToString() ?? "[]") ?? new List<string>();
product.ImageUrl = reader["imageurl"]?.ToString() ?? "";
product.Images = JsonSerializer.Deserialize<List<string>>(reader["images"].ToString() ?? "[]") ?? new List<string>();
product.IsFeatured = reader["isfeatured"] as bool? == true;
product.IsTopSeller = reader["istopseller"] as bool? == true;
product.StockQuantity = (reader["stockquantity"] as int?).GetValueOrDefault();
product.IsActive = (reader["isactive"] as bool?) ?? true;
product.UnitsSold = (reader["unitssold"] as int?).GetValueOrDefault();
product.TotalRevenue = (reader["totalrevenue"] as decimal?).GetValueOrDefault();
product.AverageRating = (reader["averagerating"] as double?).GetValueOrDefault();
product.TotalReviews = (reader["totalreviews"] as int?).GetValueOrDefault();
product.CostPrice = (reader["costprice"] as decimal?).GetValueOrDefault();
product.Tags = JsonSerializer.Deserialize<List<string>>(reader["tags"].ToString() ?? "[]") ?? new List<string>();
product.MetaDescription = reader["metadescription"]?.ToString() ?? "";
product.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
product.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
return product;
}
private Page MapToPage(NpgsqlDataReader reader)
{
Page page = new Page();
page.Id = reader["id"].ToString();
page.PageName = reader["pagename"].ToString() ?? "";
page.PageSlug = reader["pageslug"].ToString() ?? "";
page.Title = reader["title"]?.ToString() ?? "";
page.Subtitle = reader["subtitle"]?.ToString() ?? "";
page.HeroImage = reader["heroimage"]?.ToString() ?? "";
page.Content = reader["content"]?.ToString() ?? "";
page.MetaDescription = reader["metadescription"]?.ToString() ?? "";
page.ImageGallery = JsonSerializer.Deserialize<List<string>>(reader["imagegallery"].ToString() ?? "[]") ?? new List<string>();
page.IsActive = (reader["isactive"] as bool?) ?? true;
page.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
page.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
return page;
}
private MenuItem MapToMenuItem(NpgsqlDataReader reader)
{
MenuItem menuItem = new MenuItem();
menuItem.Id = reader["id"].ToString();
menuItem.Label = reader["label"].ToString() ?? "";
menuItem.Url = reader["url"].ToString() ?? "";
menuItem.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
menuItem.IsActive = (reader["isactive"] as bool?) ?? true;
menuItem.ShowInNavbar = (reader["showinnavbar"] as bool?) ?? true;
menuItem.ShowInDropdown = (reader["showindropdown"] as bool?) ?? true;
menuItem.OpenInNewTab = reader["openinnewtab"] as bool? == true;
menuItem.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
return menuItem;
}
private SiteSettings MapToSiteSettings(NpgsqlDataReader reader)
{
SiteSettings siteSettings = new SiteSettings();
siteSettings.Id = reader["id"].ToString();
siteSettings.SiteName = reader["sitename"]?.ToString() ?? "Sky Art Shop";
siteSettings.SiteTagline = reader["sitetagline"]?.ToString() ?? "";
siteSettings.ContactEmail = reader["contactemail"]?.ToString() ?? "";
siteSettings.ContactPhone = reader["contactphone"]?.ToString() ?? "";
siteSettings.InstagramUrl = reader["instagramurl"]?.ToString() ?? "#";
siteSettings.FooterText = reader["footertext"]?.ToString() ?? "";
siteSettings.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
return siteSettings;
}
private PortfolioCategory MapToPortfolioCategory(NpgsqlDataReader reader)
{
PortfolioCategory portfolioCategory = new PortfolioCategory();
portfolioCategory.Id = reader["id"].ToString();
portfolioCategory.Name = reader["name"].ToString() ?? "";
portfolioCategory.Slug = reader["slug"].ToString() ?? "";
portfolioCategory.Description = reader["description"]?.ToString() ?? "";
portfolioCategory.ThumbnailImage = reader["thumbnailimage"]?.ToString() ?? "";
portfolioCategory.FeaturedImage = reader["featuredimage"]?.ToString() ?? "";
portfolioCategory.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
portfolioCategory.IsActive = (reader["isactive"] as bool?) ?? true;
portfolioCategory.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
portfolioCategory.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
return portfolioCategory;
}
private BlogPost MapToBlogPost(NpgsqlDataReader reader)
{
BlogPost blogPost = new BlogPost();
blogPost.Id = reader["id"].ToString();
blogPost.Title = reader["title"].ToString() ?? "";
blogPost.Slug = reader["slug"].ToString() ?? "";
blogPost.Content = reader["content"]?.ToString() ?? "";
blogPost.Excerpt = reader["excerpt"]?.ToString() ?? "";
blogPost.FeaturedImage = reader["featuredimage"]?.ToString() ?? "";
blogPost.Author = reader["author"]?.ToString() ?? "";
blogPost.Tags = JsonSerializer.Deserialize<List<string>>(reader["tags"].ToString() ?? "[]") ?? new List<string>();
blogPost.IsPublished = (reader["ispublished"] as bool?) ?? true;
blogPost.PublishedDate = (reader["publisheddate"] as DateTime?) ?? DateTime.UtcNow;
blogPost.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
blogPost.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
return blogPost;
}
private HomepageSection MapToHomepageSection(NpgsqlDataReader reader)
{
HomepageSection homepageSection = new HomepageSection();
homepageSection.Id = reader["id"].ToString();
homepageSection.Title = reader["title"]?.ToString() ?? "";
homepageSection.Subtitle = reader["subtitle"]?.ToString() ?? "";
homepageSection.Content = reader["content"]?.ToString() ?? "";
homepageSection.SectionType = reader["sectiontype"]?.ToString() ?? "";
homepageSection.ImageUrl = reader["imageurl"]?.ToString() ?? "";
homepageSection.ButtonText = reader["buttontext"]?.ToString() ?? "";
homepageSection.ButtonUrl = reader["buttonurl"]?.ToString() ?? "";
homepageSection.IsActive = (reader["isactive"] as bool?) ?? true;
homepageSection.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
homepageSection.AdditionalData = JsonSerializer.Deserialize<Dictionary<string, string>>(reader["additionaldata"]?.ToString() ?? "{}") ?? new Dictionary<string, string>();
homepageSection.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
homepageSection.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
return homepageSection;
}
private PortfolioProject MapToPortfolioProject(NpgsqlDataReader reader)
{
PortfolioProject portfolioProject = new PortfolioProject();
portfolioProject.Id = reader["id"].ToString();
portfolioProject.CategoryId = reader["categoryid"]?.ToString() ?? "";
portfolioProject.Title = reader["title"]?.ToString() ?? "";
portfolioProject.Description = reader["description"]?.ToString() ?? "";
portfolioProject.FeaturedImage = reader["featuredimage"]?.ToString() ?? "";
portfolioProject.Images = JsonSerializer.Deserialize<List<string>>(reader["images"].ToString() ?? "[]") ?? new List<string>();
portfolioProject.DisplayOrder = (reader["displayorder"] as int?).GetValueOrDefault();
portfolioProject.IsActive = (reader["isactive"] as bool?) ?? true;
portfolioProject.CreatedAt = (reader["createdat"] as DateTime?) ?? DateTime.UtcNow;
portfolioProject.UpdatedAt = (reader["updatedat"] as DateTime?) ?? DateTime.UtcNow;
return portfolioProject;
}
}