SQL Server password change

This commit is contained in:
Sedat ÖZTÜRK 2026-06-29 14:10:27 +03:00
parent 3446691162
commit c079aee0ca
13 changed files with 163 additions and 53 deletions

View file

@ -3,8 +3,8 @@
"CdnUrl": "https://cdn-api.sozsoft.com"
},
"ConnectionStrings": {
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
},
"Redis": {
"IsEnabled": "true",

View file

@ -3,8 +3,8 @@
"CdnUrl": "https://cdn.sozsoft.com"
},
"ConnectionStrings": {
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
},
"Redis": {
"IsEnabled": "true",

View file

@ -4,8 +4,8 @@
"CdnUrl": "http://localhost:4005"
},
"ConnectionStrings": {
"SqlServer": "Server=localhost;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=localhost;Port=5432;Database=Sozsoft;"
"SqlServer": "Server=localhost;Database=Sozsoft;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=localhost;Port=5432;Database=Sozsoft;"
},
"Redis": {
"IsEnabled": "true",

View file

@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
@ -491,6 +492,20 @@ public class PlatformHttpApiHostModule : AbpModule
app.MapAbpStaticAssets();
app.UseRouting();
app.UseCors();
app.Use(async (httpContext, next) =>
{
if (httpContext.Request.Path.StartsWithSegments("/api/setup/application-status"))
{
var configuration = httpContext.RequestServices.GetRequiredService<IConfiguration>();
var dbExists = SetupAppRunner.DatabaseIsReady(configuration);
httpContext.Response.ContentType = "application/json; charset=utf-8";
await httpContext.Response.WriteAsJsonAsync(new { dbExists });
return;
}
await next();
});
app.Use(async (context, next) =>
{
if (context.Request.Path.StartsWithSegments("/messengerhub") &&

View file

@ -11,8 +11,8 @@
"BackupPath": "/var/opt/mssql/backup"
},
"ConnectionStrings": {
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
},
"Redis": {
"IsEnabled": "true",

View file

@ -11,8 +11,8 @@
"BackupPath": "/var/opt/mssql/backup"
},
"ConnectionStrings": {
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
},
"Redis": {
"IsEnabled": "true",

View file

@ -11,8 +11,8 @@
"BackupPath": "/var/opt/mssql/backup"
},
"ConnectionStrings": {
"SqlServer": "Server=localhost;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=NvQp8s@l;Host=localhost;Port=5432;Database=Sozsoft;"
"SqlServer": "Server=localhost;Database=Sozsoft;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=localhost;Port=5432;Database=Sozsoft;"
},
"Redis": {
"IsEnabled": "true",
@ -30,7 +30,7 @@
}
},
"Setup": {
"MigrationPassword": "sozsoft"
"MigrationPassword": "K1r1Ut7Z2o@@"
},
"StringEncryption": {
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"

View file

@ -16,8 +16,8 @@ public class DynamicDataRepository_Tests : PlatformEntityFrameworkCoreTestBase
{
private readonly DbContext _context;
private readonly DbContext _context2;
private readonly string ConnectionString = "Server=localhost;Database=DemoTests;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;TrustServerCertificate=True;";
private readonly string ConnectionString2 = "Server=localhost;Database=DemoTests2;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;TrustServerCertificate=True;";
private readonly string ConnectionString = "Server=localhost;Database=DemoTests;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;TrustServerCertificate=True;";
private readonly string ConnectionString2 = "Server=localhost;Database=DemoTests2;User Id=sa;password=K1r1Ut7Z2o@@;Trusted_Connection=False;TrustServerCertificate=True;";
public DynamicDataRepository_Tests()
{

View file

@ -30,7 +30,7 @@ services:
volumes:
- pg:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=NvQp8s@l
- POSTGRES_PASSWORD=K1r1Ut7Z2o@@
- POSTGRES_USER=sa
- POSTGRES_DB=postgres
networks:
@ -40,7 +40,7 @@ services:
profiles: ["sql"]
user: root
environment:
- SA_PASSWORD=NvQp8s@l
- SA_PASSWORD=K1r1Ut7Z2o@@
- ACCEPT_EULA=Y
- MSSQL_PID=Web
ports:

View file

@ -90,7 +90,7 @@ services:
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=NvQp8s@l
- N8N_BASIC_AUTH_PASSWORD=K1r1Ut7Z2o@@
- N8N_HOST=ai.sozsoft.com
- N8N_PORT=5678
- NODE_ENV=production

View file

@ -29,7 +29,7 @@ services:
volumes:
- pg:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=NvQp8s@l
- POSTGRES_PASSWORD=K1r1Ut7Z2o@@
- POSTGRES_USER=sa
- POSTGRES_DB=postgres
- TZ=UTC
@ -39,7 +39,7 @@ services:
profiles: ["sql"]
user: root
environment:
- SA_PASSWORD=NvQp8s@l
- SA_PASSWORD=K1r1Ut7Z2o@@
- ACCEPT_EULA=Y
- MSSQL_PID=Web
ports:

View file

@ -10,7 +10,7 @@ services:
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=NvQp8s@l
- N8N_BASIC_AUTH_PASSWORD=K1r1Ut7Z2o@@
- N8N_HOST=api.sozsoft.com
- N8N_PORT=5678
- NODE_ENV=production

View file

@ -1,8 +1,11 @@
import { useEffect, useRef, useState } from 'react'
import classNames from 'classnames'
import { APP_NAME } from '@/constants/app.constant'
import { getMigrateUrl, getSetupStatus } from '@/services/setup.service'
import { applicationConfigurationUrl } from '@/services/abpConfig.service'
import { Button, Dialog } from '@/components/ui'
import PasswordInput from '@/components/shared/PasswordInput'
import { useStoreState } from '@/store'
interface LogLine {
level: 'info' | 'warn' | 'error' | 'success' | 'restart' | 'done'
@ -11,7 +14,7 @@ interface LogLine {
type MigrationStatus = 'idle' | 'running' | 'success' | 'error' | 'restarting'
const levelClass: Record<string, string> = {
const darkLevelClass: Record<string, string> = {
info: 'text-gray-300',
warn: 'text-yellow-400',
error: 'text-red-400',
@ -20,6 +23,15 @@ const levelClass: Record<string, string> = {
done: 'text-blue-400',
}
const lightLevelClass: Record<string, string> = {
info: 'text-gray-700',
warn: 'text-amber-600',
error: 'text-red-600',
success: 'text-emerald-600',
restart: 'text-blue-600',
done: 'text-blue-600',
}
/**
* Sunucu yeniden başlayana kadar ABP config endpoint'ini poll eder.
* Arka arkaya 2 başarılı yanıt alındığında onReady çağrılır.
@ -75,6 +87,8 @@ const pollUntilServerReady = (onReady: () => void, onAttempt?: (attempt: number)
}
const DatabaseSetup = () => {
const mode = useStoreState((state) => state.theme.mode)
const isDark = mode === 'dark'
const [logs, setLogs] = useState<LogLine[]>([])
const [status, setStatus] = useState<MigrationStatus>('idle')
const [pollCountdown, setPollCountdown] = useState(0)
@ -82,7 +96,7 @@ const DatabaseSetup = () => {
const [setupPassword, setSetupPassword] = useState('')
const [isPasswordDialogOpen, setIsPasswordDialogOpen] = useState(false)
const logEndRef = useRef<HTMLDivElement>(null)
const pollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const pollCancelRef = useRef<(() => void) | null>(null)
// Auto-scroll to bottom when new logs arrive
useEffect(() => {
@ -99,9 +113,8 @@ const DatabaseSetup = () => {
// Cleanup on component unmount
useEffect(() => {
return () => {
const ref = pollTimerRef.current as any
if (ref?.unref) ref.unref()
else if (pollTimerRef.current) clearTimeout(pollTimerRef.current as any)
pollCancelRef.current?.()
pollCancelRef.current = null
}
}, [])
@ -109,14 +122,18 @@ const DatabaseSetup = () => {
setLogs((prev) => [...prev, { level, message }])
}
const levelClass = isDark ? darkLevelClass : lightLevelClass
// ── Poll ABP config endpoint until server is ready ──────────────
const pollUntilReady = () => {
if (pollCancelRef.current) return
setStatus('restarting')
const cancel = pollUntilServerReady(
() => { window.location.href = '/' },
(attempt) => setPollCountdown(attempt),
)
pollTimerRef.current = { unref: cancel } as any
pollCancelRef.current = cancel
}
const startMigration = async () => {
@ -211,15 +228,25 @@ const DatabaseSetup = () => {
}
return (
<div className="min-h-screen bg-gray-900 flex flex-col items-center justify-center p-6 text-white">
<div
className={classNames(
'min-h-screen flex flex-col items-center justify-center p-6 transition-colors',
isDark ? 'bg-gray-900 text-white' : 'bg-gray-100 text-gray-900',
)}
>
{/* Header */}
<div className="mb-10 text-center sm:text-left">
<div className="flex flex-col items-center gap-2 sm:flex-row">
<div className="relative">
<div className="absolute inset-0 rounded-full bg-sky-400/30 blur-2xl" />
<div
className={classNames(
'absolute inset-0 rounded-full blur-2xl',
isDark ? 'bg-sky-400/30' : 'bg-sky-300/40',
)}
/>
<div className="relative flex h-20 w-20 items-center justify-center">
<img
src="/img/logo/logo-dark-streamline.png"
src={isDark ? '/img/logo/logo-dark-streamline.png' : '/img/logo/logo-light-streamline.png'}
alt={`${APP_NAME} logo`}
className="h-18 w-18 object-contain drop-shadow-2xl"
/>
@ -227,11 +254,25 @@ const DatabaseSetup = () => {
</div>
<div>
<h1 className="tracking-tight text-white">
<h1 className={classNames('tracking-tight', isDark ? 'text-white' : 'text-gray-900')}>
{APP_NAME}
</h1>
<div className="mt-1 inline-flex items-center gap-2 rounded-full border border-emerald-400/20 bg-emerald-400/10 px-1 py-1 text-xs font-medium uppercase tracking-[0.22em] text-emerald-200">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-300 shadow-[0_0_12px_rgba(110,231,183,0.85)]" />
<div
className={classNames(
'mt-1 inline-flex items-center gap-2 rounded-full border px-1 py-1 text-xs font-medium uppercase tracking-[0.22em]',
isDark
? 'border-emerald-400/20 bg-emerald-400/10 text-emerald-200'
: 'border-emerald-500/25 bg-emerald-50 text-emerald-700',
)}
>
<span
className={classNames(
'h-1.5 w-1.5 rounded-full',
isDark
? 'bg-emerald-300 shadow-[0_0_12px_rgba(110,231,183,0.85)]'
: 'bg-emerald-500 shadow-[0_0_10px_rgba(16,185,129,0.45)]',
)}
/>
Database creation and migration workspace
</div>
</div>
@ -239,9 +280,19 @@ const DatabaseSetup = () => {
</div>
{/* Status Card */}
<div className="w-full max-w-4xl bg-gray-800 rounded-xl shadow-2xl overflow-hidden">
<div
className={classNames(
'w-full max-w-4xl rounded-xl shadow-2xl overflow-hidden border',
isDark ? 'border-gray-700 bg-gray-800' : 'border-gray-200 bg-white',
)}
>
{/* Card Header */}
<div className="flex items-center justify-between px-5 py-4 bg-gray-700 border-b border-gray-600">
<div
className={classNames(
'flex items-center justify-between px-5 py-4 border-b',
isDark ? 'border-gray-600 bg-gray-700' : 'border-gray-200 bg-gray-50',
)}
>
<div className="flex items-center gap-3">
<StatusIndicator status={status} />
<span className="font-semibold text-sm tracking-wide">
@ -252,17 +303,31 @@ const DatabaseSetup = () => {
{status === 'error' && 'Migration Failed ✗'}
</span>
</div>
<span className="text-xs text-gray-400">{logs.length} log lines</span>
<span className={classNames('text-xs', isDark ? 'text-gray-400' : 'text-gray-500')}>
{logs.length} log lines
</span>
</div>
{/* Log Terminal */}
<div className="h-96 overflow-y-auto bg-gray-950 px-4 py-3 font-mono text-xs leading-relaxed">
<div
className={classNames(
'h-96 overflow-y-auto px-4 py-3 font-mono text-xs leading-relaxed',
isDark ? 'bg-gray-950' : 'bg-white',
)}
>
{logs.length === 0 ? (
<p className="text-gray-600 mt-2">Logs will appear here when migration starts...</p>
<p className={classNames('mt-2', isDark ? 'text-gray-600' : 'text-gray-400')}>
Logs will appear here when migration starts...
</p>
) : (
logs.map((line, i) => (
<div key={i} className={`${levelClass[line.level] ?? 'text-gray-300'} mb-0.5`}>
<span className="text-gray-600 mr-2 select-none">{String(i + 1).padStart(4, '0')}</span>
<div
key={i}
className={`${levelClass[line.level] ?? (isDark ? 'text-gray-300' : 'text-gray-700')} mb-0.5`}
>
<span className={classNames('mr-2 select-none', isDark ? 'text-gray-600' : 'text-gray-400')}>
{String(i + 1).padStart(4, '0')}
</span>
{line.message}
</div>
))
@ -271,8 +336,13 @@ const DatabaseSetup = () => {
</div>
{/* Action Area */}
<div className="flex items-center justify-between gap-4 px-5 py-4 bg-gray-700 border-t border-gray-600">
<div className="text-xs text-gray-400">
<div
className={classNames(
'flex items-center justify-between gap-4 px-5 py-4 border-t',
isDark ? 'border-gray-600 bg-gray-700' : 'border-gray-200 bg-gray-50',
)}
>
<div className={classNames('text-xs', isDark ? 'text-gray-400' : 'text-gray-500')}>
{status === 'idle' && dbExists === true && 'Database already exists. Migration is not required.'}
{status === 'idle' && dbExists === false && 'Database not found. Press the button to start migration.'}
{status === 'idle' && dbExists === null && 'Checking database status...'}
@ -288,7 +358,7 @@ const DatabaseSetup = () => {
{(status === 'idle' || status === 'error') && !dbExists && (
<Button
variant='solid'
className="min-w-40"
className={status === 'error' ? 'min-w-40' : 'min-w-25'}
onClick={() => setIsPasswordDialogOpen(true)}
>
{status === 'error' ? 'Retry Migration' : 'Migrate'}
@ -309,23 +379,33 @@ const DatabaseSetup = () => {
isOpen={isPasswordDialogOpen}
onClose={() => setIsPasswordDialogOpen(false)}
onRequestClose={() => setIsPasswordDialogOpen(false)}
contentClassName={classNames(
'setup-password-dialog border',
isDark
? 'border-gray-700 bg-gray-900 text-gray-100'
: 'border-gray-200 bg-white text-gray-900',
)}
width={440}
>
<Dialog.Header className="-mx-6 -mt-6 border-b border-gray-200 px-6 py-4 dark:border-gray-700">
<Dialog.Header
className={classNames(
'-mx-6 -mt-6 border-b px-6 py-4',
isDark ? 'border-gray-700 bg-gray-800' : 'border-gray-200 bg-gray-50',
)}
>
<div>
<h5 className="text-gray-900 dark:text-gray-100">Setup authorization</h5>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
<h5 className={isDark ? 'text-gray-100' : 'text-gray-900'}>Setup authorization</h5>
<p className={classNames('mt-1 text-sm', isDark ? 'text-gray-400' : 'text-gray-500')}>
Enter the setup password to start database migration.
</p>
</div>
</Dialog.Header>
<Dialog.Body className="pt-5">
<label className="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-200">
<label className={classNames('mb-2 block text-sm font-medium', isDark ? 'text-gray-200' : 'text-gray-700')}>
Setup password
</label>
<input
<PasswordInput
autoFocus
type="password"
value={setupPassword}
onChange={(event) => setSetupPassword(event.target.value)}
onKeyDown={(event) => {
@ -335,15 +415,30 @@ const DatabaseSetup = () => {
}}
placeholder="Enter setup password"
autoComplete="off"
className="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder:text-gray-500"
className={classNames(
'w-full [&_.input:focus]:border-blue-500 [&_.input:focus]:ring-blue-500',
isDark
? '[&_.input-suffix-end]:text-gray-300 [&_.input]:border-gray-600 [&_.input]:bg-gray-800 [&_.input]:text-gray-100 [&_.input]:placeholder:text-gray-500'
: '[&_.input-suffix-end]:text-gray-500 [&_.input]:border-gray-300 [&_.input]:bg-white [&_.input]:text-gray-900 [&_.input]:placeholder:text-gray-400',
)}
/>
<p className="mt-3 text-xs text-gray-500 dark:text-gray-400">
<p className={classNames('mt-3 text-xs', isDark ? 'text-gray-400' : 'text-gray-500')}>
This protects the migration action when users are redirected to setup after a database
problem.
</p>
</Dialog.Body>
<Dialog.Footer className="mt-2 flex justify-end gap-2 border-t border-gray-200 pt-4 dark:border-gray-700">
<Dialog.Footer
className={classNames(
'mt-2 flex justify-end gap-2 border-t pt-4',
isDark ? 'border-gray-700' : 'border-gray-200',
)}
>
<Button
className={classNames(
isDark
? 'border-gray-600 bg-gray-800 text-gray-100 hover:bg-gray-700'
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
)}
onClick={() => setIsPasswordDialogOpen(false)}
>
Cancel
@ -358,7 +453,7 @@ const DatabaseSetup = () => {
</Dialog.Footer>
</Dialog>
<p className="mt-6 text-xs text-gray-600">
<p className={classNames('mt-6 text-xs', isDark ? 'text-gray-600' : 'text-gray-400')}>
This page is only visible when the database does not exist.
</p>
</div>