Compare commits

...

11 Commits

9 changed files with 156 additions and 27 deletions

View File

@@ -2,11 +2,15 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src WORKDIR /src
COPY . . COPY . .
RUN dotnet restore # Publish ONLY the API project: the build context contains the whole
RUN dotnet publish -c Release -o /app # solution, and publishing the sln would dump every project's
# appsettings.json into /app (last one wins) — e.g. DataCopy's
# overwrote the API's and broke IppanelSms startup.
RUN dotnet restore GreenHome.Api/GreenHome.Api.csproj
RUN dotnet publish GreenHome.Api/GreenHome.Api.csproj -c Release -o /app
# مرحله runtime # مرحله runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
WORKDIR /app WORKDIR /app
COPY --from=build /app . COPY --from=build /app .
EXPOSE 8080 EXPOSE 8080

View File

@@ -8,6 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@@ -7,7 +7,6 @@ using GreenHome.Infrastructure.Data;
using GreenHome.Sms.Ippanel; using GreenHome.Sms.Ippanel;
using GreenHome.VoiceCall.Avanak; using GreenHome.VoiceCall.Avanak;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;

View File

@@ -6,7 +6,7 @@
} }
}, },
"Database": { "Database": {
"Provider": "SqlServer" "Provider": "Postgres"
}, },
"ConnectionStrings": { "ConnectionStrings": {
"Default": "Server=.;Integrated Security=False;Trusted_Connection=True;Encrypt=false;MultipleActiveResultSets=true;Database=GreenHomeDb;", "Default": "Server=.;Integrated Security=False;Trusted_Connection=True;Encrypt=false;MultipleActiveResultSets=true;Database=GreenHomeDb;",

View File

@@ -33,24 +33,7 @@ services:
networks: networks:
- greenNetwork - greenNetwork
postgres:
image: postgres:18-alpine
restart: unless-stopped
environment:
POSTGRES_DB: greenhome
POSTGRES_USER: greenhome
POSTGRES_PASSWORD: greenhome
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- greenNetwork
networks: networks:
serviceNetwork: serviceNetwork:
name: greenNetwork name: greenNetwork
external: true external: true
volumes:
pgdata:

View File

@@ -38,6 +38,7 @@ internal static class Program
var config = new ConfigurationBuilder() var config = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory) .SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json") .AddJsonFile("appsettings.json")
.AddEnvironmentVariables() // lets the server pass ConnectionStrings__* env vars
.Build(); .Build();
var sqlServerCs = config.GetConnectionString("SqlServer") var sqlServerCs = config.GetConnectionString("SqlServer")
@@ -117,8 +118,9 @@ internal static class Program
foreach (var (_, table) in order) foreach (var (_, table) in order)
{ {
long src = (long)(await ScalarAsync(srcConn, $"SELECT COUNT(*) FROM {table}"))!; // SQL Server COUNT(*) is Int32, Postgres is Int64 — Convert handles both
long dst = (long)(await ScalarAsync(pgConn, $"SELECT COUNT(*) FROM \"{table}\""))!; long src = Convert.ToInt64(await ScalarAsync(srcConn, $"SELECT COUNT(*) FROM {table}"))!;
long dst = Convert.ToInt64(await ScalarAsync(pgConn, $"SELECT COUNT(*) FROM \"{table}\""))!;
if (src != dst) problems++; if (src != dst) problems++;
Console.WriteLine($"{(src == dst ? "OK " : "FAIL ")} {table,-28} source={src,-10} target={dst}"); Console.WriteLine($"{(src == dst ? "OK " : "FAIL ")} {table,-28} source={src,-10} target={dst}");
} }
@@ -168,9 +170,11 @@ internal static class Program
var max = (int)(await ScalarAsync(pg, $"SELECT COALESCE(MAX(\"{pkName}\"), 0) FROM \"{table}\""))!; var max = (int)(await ScalarAsync(pg, $"SELECT COALESCE(MAX(\"{pkName}\"), 0) FROM \"{table}\""))!;
if (max > 0) if (max > 0)
{ {
// Keep the identity sequence above the copied max so new rows don't collide // Keep the identity sequence above the copied max so new rows don't collide.
// pg_get_serial_sequence(text, text) — verified signature. format('%I', ...)
// preserves the exact table case EF used; the column is passed as plain text.
await target.Database.ExecuteSqlRawAsync( await target.Database.ExecuteSqlRawAsync(
$"SELECT setval(pg_get_serial_sequence('{table}', '{pkName}'), {max})"); $"SELECT setval(pg_get_serial_sequence(format('%I', '{table}'), '{pkName}'), {max})");
} }
Console.WriteLine($"\n[done ] {table}: copied={copied}, maxId={max}"); Console.WriteLine($"\n[done ] {table}: copied={copied}, maxId={max}");
} }

View File

@@ -497,5 +497,25 @@ public sealed class GreenHomeDbContext : DbContext
b.HasIndex(x => x.At); b.HasIndex(x => x.At);
b.HasIndex(x => x.AdminUserId); b.HasIndex(x => x.AdminUserId);
}); });
// Postgres-specific mappings (SQL Server path is untouched).
// MUST run last: fluent HasMaxLength/HasColumnType calls above set the
// values we adjust here, so this block has to see the final model state.
// 1) DateTime -> "timestamp" (no tz): mirrors naive datetime2, accepts any Kind.
// 2) varchar: Postgres counts BYTES, SQL Server's nvarchar counts CHARACTERS.
// x4 keeps char-count semantics for any UTF-8 charset (Persian = 2 bytes/char).
if (Database.ProviderName == "Npgsql.EntityFrameworkCore.PostgreSQL")
{
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
foreach (var p in et.GetProperties())
{
if (p.ClrType == typeof(DateTime) || p.ClrType == typeof(DateTime?))
p.SetColumnType("timestamp");
else if (p.ClrType == typeof(string) && p.GetMaxLength() is int len)
p.SetColumnType($"character varying({len * 4})");
}
}
}
} }
} }

View File

@@ -17,40 +17,114 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreenHome.VoiceCall.Avanak"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreenHome.AI.DeepSeek", "GreenHome.AI.DeepSeek\GreenHome.AI.DeepSeek.csproj", "{63D676E7-B882-4E70-8090-E56CFD516B0A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreenHome.AI.DeepSeek", "GreenHome.AI.DeepSeek\GreenHome.AI.DeepSeek.csproj", "{63D676E7-B882-4E70-8090-E56CFD516B0A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreenHome.DataCopy", "GreenHome.DataCopy\GreenHome.DataCopy.csproj", "{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|Any CPU.Build.0 = Debug|Any CPU {31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|x64.ActiveCfg = Debug|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|x64.Build.0 = Debug|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|x86.ActiveCfg = Debug|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Debug|x86.Build.0 = Debug|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|Any CPU.ActiveCfg = Release|Any CPU {31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|Any CPU.Build.0 = Release|Any CPU {31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|Any CPU.Build.0 = Release|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|x64.ActiveCfg = Release|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|x64.Build.0 = Release|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|x86.ActiveCfg = Release|Any CPU
{31D70B40-BD35-4DA8-8E2B-B51120C85F19}.Release|x86.Build.0 = Release|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|Any CPU.Build.0 = Debug|Any CPU {4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|x64.ActiveCfg = Debug|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|x64.Build.0 = Debug|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|x86.ActiveCfg = Debug|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Debug|x86.Build.0 = Debug|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|Any CPU.Build.0 = Release|Any CPU {4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|Any CPU.Build.0 = Release|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|x64.ActiveCfg = Release|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|x64.Build.0 = Release|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|x86.ActiveCfg = Release|Any CPU
{4E330D53-37E7-42D3-B38C-E0DDF52CEA35}.Release|x86.Build.0 = Release|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|Any CPU.Build.0 = Debug|Any CPU {D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|x64.ActiveCfg = Debug|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|x64.Build.0 = Debug|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|x86.ActiveCfg = Debug|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Debug|x86.Build.0 = Debug|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|Any CPU.Build.0 = Release|Any CPU {D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|Any CPU.Build.0 = Release|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|x64.ActiveCfg = Release|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|x64.Build.0 = Release|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|x86.ActiveCfg = Release|Any CPU
{D41BC2ED-CB72-4A98-82D6-8B6B7EC9423C}.Release|x86.Build.0 = Release|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|Any CPU.Build.0 = Debug|Any CPU {75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|x64.ActiveCfg = Debug|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|x64.Build.0 = Debug|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|x86.ActiveCfg = Debug|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Debug|x86.Build.0 = Debug|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|Any CPU.ActiveCfg = Release|Any CPU {75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|Any CPU.Build.0 = Release|Any CPU {75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|Any CPU.Build.0 = Release|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|x64.ActiveCfg = Release|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|x64.Build.0 = Release|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|x86.ActiveCfg = Release|Any CPU
{75E498D4-5D04-4F63-A1A5-5D851FA40C74}.Release|x86.Build.0 = Release|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|x64.ActiveCfg = Debug|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|x64.Build.0 = Debug|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|x86.ActiveCfg = Debug|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Debug|x86.Build.0 = Debug|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|Any CPU.Build.0 = Release|Any CPU {B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|Any CPU.Build.0 = Release|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|x64.ActiveCfg = Release|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|x64.Build.0 = Release|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|x86.ActiveCfg = Release|Any CPU
{B1FF8C56-E758-48C9-A6EB-E2C938D1BE6B}.Release|x86.Build.0 = Release|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|x64.ActiveCfg = Debug|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|x64.Build.0 = Debug|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|x86.ActiveCfg = Debug|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Debug|x86.Build.0 = Debug|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|Any CPU.Build.0 = Release|Any CPU {A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|Any CPU.Build.0 = Release|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|x64.ActiveCfg = Release|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|x64.Build.0 = Release|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|x86.ActiveCfg = Release|Any CPU
{A2C9D8E7-F459-4B1A-9C3D-1E4F5A6B7C8D}.Release|x86.Build.0 = Release|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|Any CPU.Build.0 = Debug|Any CPU {63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|x64.ActiveCfg = Debug|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|x64.Build.0 = Debug|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|x86.ActiveCfg = Debug|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Debug|x86.Build.0 = Debug|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|Any CPU.ActiveCfg = Release|Any CPU {63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|Any CPU.Build.0 = Release|Any CPU {63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|Any CPU.Build.0 = Release|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|x64.ActiveCfg = Release|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|x64.Build.0 = Release|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|x86.ActiveCfg = Release|Any CPU
{63D676E7-B882-4E70-8090-E56CFD516B0A}.Release|x86.Build.0 = Release|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Debug|x64.ActiveCfg = Debug|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Debug|x64.Build.0 = Debug|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Debug|x86.ActiveCfg = Debug|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Debug|x86.Build.0 = Debug|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Release|Any CPU.Build.0 = Release|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Release|x64.ActiveCfg = Release|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Release|x64.Build.0 = Release|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Release|x86.ActiveCfg = Release|Any CPU
{0F5C7A31-54F9-4C29-8E05-66CECAF520C7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -0,0 +1,44 @@
# Shared database infrastructure — standalone from the app.
# Reused by GreenHome and any future service on the same Docker network.
#
# Server usage (one-time):
# 1. mkdir -p /opt/infra && copy this file there
# 2. docker network create greenNetwork (skip if it already exists)
# 3. cd /opt/infra && docker compose up -d
# 4. docker compose exec postgres pg_isready -U greenhome
#
# App connection string: Host=postgres;Port=5432;Database=greenhome;Username=greenhome;Password=<same as below>
#
# Adding a DB for another service later (no restart needed):
# docker compose exec -it postgres psql -U greenhome -c "CREATE DATABASE otherapp OWNER otherapp;"
services:
postgres:
image: postgres:18-alpine
container_name: postgres
restart: unless-stopped
environment:
POSTGRES_DB: greenhome
POSTGRES_USER: greenhome
# ⚠️ set a strong password here and use the same value in the app's
# ConnectionStrings__Postgres env var
POSTGRES_PASSWORD: CHANGE_ME_STRONG_PASSWORD
# localhost only: containers on greenNetwork reach it by name "postgres";
# host psql can still connect via 127.0.0.1:5432. Do NOT bind 0.0.0.0.
ports:
- "127.0.0.1:5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U greenhome -d greenhome"]
interval: 10s
timeout: 5s
retries: 5
networks:
- greenNetwork
networks:
greenNetwork:
external: true
volumes:
pgdata: