SQL Server password change
This commit is contained in:
parent
3446691162
commit
c079aee0ca
13 changed files with 163 additions and 53 deletions
|
|
@ -3,8 +3,8 @@
|
||||||
"CdnUrl": "https://cdn-api.sozsoft.com"
|
"CdnUrl": "https://cdn-api.sozsoft.com"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"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=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
|
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
"IsEnabled": "true",
|
"IsEnabled": "true",
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
"CdnUrl": "https://cdn.sozsoft.com"
|
"CdnUrl": "https://cdn.sozsoft.com"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"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=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
|
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
"IsEnabled": "true",
|
"IsEnabled": "true",
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
"CdnUrl": "http://localhost:4005"
|
"CdnUrl": "http://localhost:4005"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=localhost;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"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=NvQp8s@l;Host=localhost;Port=5432;Database=Sozsoft;"
|
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=localhost;Port=5432;Database=Sozsoft;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
"IsEnabled": "true",
|
"IsEnabled": "true",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Extensions.DependencyInjection;
|
using Microsoft.AspNetCore.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
@ -491,6 +492,20 @@ public class PlatformHttpApiHostModule : AbpModule
|
||||||
app.MapAbpStaticAssets();
|
app.MapAbpStaticAssets();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseCors();
|
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) =>
|
app.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
if (context.Request.Path.StartsWithSegments("/messengerhub") &&
|
if (context.Request.Path.StartsWithSegments("/messengerhub") &&
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
"BackupPath": "/var/opt/mssql/backup"
|
"BackupPath": "/var/opt/mssql/backup"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"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=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
|
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
"IsEnabled": "true",
|
"IsEnabled": "true",
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
"BackupPath": "/var/opt/mssql/backup"
|
"BackupPath": "/var/opt/mssql/backup"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=sql;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"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=NvQp8s@l;Host=postgres;Port=5432;Database=Sozsoft;"
|
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=postgres;Port=5432;Database=Sozsoft;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
"IsEnabled": "true",
|
"IsEnabled": "true",
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
"BackupPath": "/var/opt/mssql/backup"
|
"BackupPath": "/var/opt/mssql/backup"
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"SqlServer": "Server=localhost;Database=Sozsoft;User Id=sa;password=NvQp8s@l;Trusted_Connection=False;Encrypt=False;TrustServerCertificate=True;Connection Timeout=60;",
|
"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=NvQp8s@l;Host=localhost;Port=5432;Database=Sozsoft;"
|
"PostgreSql": "User ID=sa;Password=K1r1Ut7Z2o@@;Host=localhost;Port=5432;Database=Sozsoft;"
|
||||||
},
|
},
|
||||||
"Redis": {
|
"Redis": {
|
||||||
"IsEnabled": "true",
|
"IsEnabled": "true",
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Setup": {
|
"Setup": {
|
||||||
"MigrationPassword": "sozsoft"
|
"MigrationPassword": "K1r1Ut7Z2o@@"
|
||||||
},
|
},
|
||||||
"StringEncryption": {
|
"StringEncryption": {
|
||||||
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"
|
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ public class DynamicDataRepository_Tests : PlatformEntityFrameworkCoreTestBase
|
||||||
{
|
{
|
||||||
private readonly DbContext _context;
|
private readonly DbContext _context;
|
||||||
private readonly DbContext _context2;
|
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 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=NvQp8s@l;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()
|
public DynamicDataRepository_Tests()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- pg:/var/lib/postgresql/data
|
- pg:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=NvQp8s@l
|
- POSTGRES_PASSWORD=K1r1Ut7Z2o@@
|
||||||
- POSTGRES_USER=sa
|
- POSTGRES_USER=sa
|
||||||
- POSTGRES_DB=postgres
|
- POSTGRES_DB=postgres
|
||||||
networks:
|
networks:
|
||||||
|
|
@ -40,7 +40,7 @@ services:
|
||||||
profiles: ["sql"]
|
profiles: ["sql"]
|
||||||
user: root
|
user: root
|
||||||
environment:
|
environment:
|
||||||
- SA_PASSWORD=NvQp8s@l
|
- SA_PASSWORD=K1r1Ut7Z2o@@
|
||||||
- ACCEPT_EULA=Y
|
- ACCEPT_EULA=Y
|
||||||
- MSSQL_PID=Web
|
- MSSQL_PID=Web
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- N8N_BASIC_AUTH_ACTIVE=true
|
- N8N_BASIC_AUTH_ACTIVE=true
|
||||||
- N8N_BASIC_AUTH_USER=admin
|
- N8N_BASIC_AUTH_USER=admin
|
||||||
- N8N_BASIC_AUTH_PASSWORD=NvQp8s@l
|
- N8N_BASIC_AUTH_PASSWORD=K1r1Ut7Z2o@@
|
||||||
- N8N_HOST=ai.sozsoft.com
|
- N8N_HOST=ai.sozsoft.com
|
||||||
- N8N_PORT=5678
|
- N8N_PORT=5678
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- pg:/var/lib/postgresql/data
|
- pg:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=NvQp8s@l
|
- POSTGRES_PASSWORD=K1r1Ut7Z2o@@
|
||||||
- POSTGRES_USER=sa
|
- POSTGRES_USER=sa
|
||||||
- POSTGRES_DB=postgres
|
- POSTGRES_DB=postgres
|
||||||
- TZ=UTC
|
- TZ=UTC
|
||||||
|
|
@ -39,7 +39,7 @@ services:
|
||||||
profiles: ["sql"]
|
profiles: ["sql"]
|
||||||
user: root
|
user: root
|
||||||
environment:
|
environment:
|
||||||
- SA_PASSWORD=NvQp8s@l
|
- SA_PASSWORD=K1r1Ut7Z2o@@
|
||||||
- ACCEPT_EULA=Y
|
- ACCEPT_EULA=Y
|
||||||
- MSSQL_PID=Web
|
- MSSQL_PID=Web
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- N8N_BASIC_AUTH_ACTIVE=true
|
- N8N_BASIC_AUTH_ACTIVE=true
|
||||||
- N8N_BASIC_AUTH_USER=admin
|
- N8N_BASIC_AUTH_USER=admin
|
||||||
- N8N_BASIC_AUTH_PASSWORD=NvQp8s@l
|
- N8N_BASIC_AUTH_PASSWORD=K1r1Ut7Z2o@@
|
||||||
- N8N_HOST=api.sozsoft.com
|
- N8N_HOST=api.sozsoft.com
|
||||||
- N8N_PORT=5678
|
- N8N_PORT=5678
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import classNames from 'classnames'
|
||||||
import { APP_NAME } from '@/constants/app.constant'
|
import { APP_NAME } from '@/constants/app.constant'
|
||||||
import { getMigrateUrl, getSetupStatus } from '@/services/setup.service'
|
import { getMigrateUrl, getSetupStatus } from '@/services/setup.service'
|
||||||
import { applicationConfigurationUrl } from '@/services/abpConfig.service'
|
import { applicationConfigurationUrl } from '@/services/abpConfig.service'
|
||||||
import { Button, Dialog } from '@/components/ui'
|
import { Button, Dialog } from '@/components/ui'
|
||||||
|
import PasswordInput from '@/components/shared/PasswordInput'
|
||||||
|
import { useStoreState } from '@/store'
|
||||||
|
|
||||||
interface LogLine {
|
interface LogLine {
|
||||||
level: 'info' | 'warn' | 'error' | 'success' | 'restart' | 'done'
|
level: 'info' | 'warn' | 'error' | 'success' | 'restart' | 'done'
|
||||||
|
|
@ -11,7 +14,7 @@ interface LogLine {
|
||||||
|
|
||||||
type MigrationStatus = 'idle' | 'running' | 'success' | 'error' | 'restarting'
|
type MigrationStatus = 'idle' | 'running' | 'success' | 'error' | 'restarting'
|
||||||
|
|
||||||
const levelClass: Record<string, string> = {
|
const darkLevelClass: Record<string, string> = {
|
||||||
info: 'text-gray-300',
|
info: 'text-gray-300',
|
||||||
warn: 'text-yellow-400',
|
warn: 'text-yellow-400',
|
||||||
error: 'text-red-400',
|
error: 'text-red-400',
|
||||||
|
|
@ -20,6 +23,15 @@ const levelClass: Record<string, string> = {
|
||||||
done: 'text-blue-400',
|
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.
|
* 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.
|
* 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 DatabaseSetup = () => {
|
||||||
|
const mode = useStoreState((state) => state.theme.mode)
|
||||||
|
const isDark = mode === 'dark'
|
||||||
const [logs, setLogs] = useState<LogLine[]>([])
|
const [logs, setLogs] = useState<LogLine[]>([])
|
||||||
const [status, setStatus] = useState<MigrationStatus>('idle')
|
const [status, setStatus] = useState<MigrationStatus>('idle')
|
||||||
const [pollCountdown, setPollCountdown] = useState(0)
|
const [pollCountdown, setPollCountdown] = useState(0)
|
||||||
|
|
@ -82,7 +96,7 @@ const DatabaseSetup = () => {
|
||||||
const [setupPassword, setSetupPassword] = useState('')
|
const [setupPassword, setSetupPassword] = useState('')
|
||||||
const [isPasswordDialogOpen, setIsPasswordDialogOpen] = useState(false)
|
const [isPasswordDialogOpen, setIsPasswordDialogOpen] = useState(false)
|
||||||
const logEndRef = useRef<HTMLDivElement>(null)
|
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
|
// Auto-scroll to bottom when new logs arrive
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -99,9 +113,8 @@ const DatabaseSetup = () => {
|
||||||
// Cleanup on component unmount
|
// Cleanup on component unmount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
const ref = pollTimerRef.current as any
|
pollCancelRef.current?.()
|
||||||
if (ref?.unref) ref.unref()
|
pollCancelRef.current = null
|
||||||
else if (pollTimerRef.current) clearTimeout(pollTimerRef.current as any)
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
@ -109,14 +122,18 @@ const DatabaseSetup = () => {
|
||||||
setLogs((prev) => [...prev, { level, message }])
|
setLogs((prev) => [...prev, { level, message }])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const levelClass = isDark ? darkLevelClass : lightLevelClass
|
||||||
|
|
||||||
// ── Poll ABP config endpoint until server is ready ──────────────
|
// ── Poll ABP config endpoint until server is ready ──────────────
|
||||||
const pollUntilReady = () => {
|
const pollUntilReady = () => {
|
||||||
|
if (pollCancelRef.current) return
|
||||||
|
|
||||||
setStatus('restarting')
|
setStatus('restarting')
|
||||||
const cancel = pollUntilServerReady(
|
const cancel = pollUntilServerReady(
|
||||||
() => { window.location.href = '/' },
|
() => { window.location.href = '/' },
|
||||||
(attempt) => setPollCountdown(attempt),
|
(attempt) => setPollCountdown(attempt),
|
||||||
)
|
)
|
||||||
pollTimerRef.current = { unref: cancel } as any
|
pollCancelRef.current = cancel
|
||||||
}
|
}
|
||||||
|
|
||||||
const startMigration = async () => {
|
const startMigration = async () => {
|
||||||
|
|
@ -211,15 +228,25 @@ const DatabaseSetup = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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 */}
|
{/* Header */}
|
||||||
<div className="mb-10 text-center sm:text-left">
|
<div className="mb-10 text-center sm:text-left">
|
||||||
<div className="flex flex-col items-center gap-2 sm:flex-row">
|
<div className="flex flex-col items-center gap-2 sm:flex-row">
|
||||||
<div className="relative">
|
<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">
|
<div className="relative flex h-20 w-20 items-center justify-center">
|
||||||
<img
|
<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`}
|
alt={`${APP_NAME} logo`}
|
||||||
className="h-18 w-18 object-contain drop-shadow-2xl"
|
className="h-18 w-18 object-contain drop-shadow-2xl"
|
||||||
/>
|
/>
|
||||||
|
|
@ -227,11 +254,25 @@ const DatabaseSetup = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h1 className="tracking-tight text-white">
|
<h1 className={classNames('tracking-tight', isDark ? 'text-white' : 'text-gray-900')}>
|
||||||
{APP_NAME}
|
{APP_NAME}
|
||||||
</h1>
|
</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">
|
<div
|
||||||
<span className="h-1.5 w-1.5 rounded-full bg-emerald-300 shadow-[0_0_12px_rgba(110,231,183,0.85)]" />
|
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
|
Database creation and migration workspace
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -239,9 +280,19 @@ const DatabaseSetup = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status Card */}
|
{/* 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 */}
|
{/* 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">
|
<div className="flex items-center gap-3">
|
||||||
<StatusIndicator status={status} />
|
<StatusIndicator status={status} />
|
||||||
<span className="font-semibold text-sm tracking-wide">
|
<span className="font-semibold text-sm tracking-wide">
|
||||||
|
|
@ -252,17 +303,31 @@ const DatabaseSetup = () => {
|
||||||
{status === 'error' && 'Migration Failed ✗'}
|
{status === 'error' && 'Migration Failed ✗'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
{/* Log Terminal */}
|
{/* 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 ? (
|
{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) => (
|
logs.map((line, i) => (
|
||||||
<div key={i} className={`${levelClass[line.level] ?? 'text-gray-300'} mb-0.5`}>
|
<div
|
||||||
<span className="text-gray-600 mr-2 select-none">{String(i + 1).padStart(4, '0')}</span>
|
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}
|
{line.message}
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
@ -271,8 +336,13 @@ const DatabaseSetup = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Area */}
|
{/* Action Area */}
|
||||||
<div className="flex items-center justify-between gap-4 px-5 py-4 bg-gray-700 border-t border-gray-600">
|
<div
|
||||||
<div className="text-xs text-gray-400">
|
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 === true && 'Database already exists. Migration is not required.'}
|
||||||
{status === 'idle' && dbExists === false && 'Database not found. Press the button to start migration.'}
|
{status === 'idle' && dbExists === false && 'Database not found. Press the button to start migration.'}
|
||||||
{status === 'idle' && dbExists === null && 'Checking database status...'}
|
{status === 'idle' && dbExists === null && 'Checking database status...'}
|
||||||
|
|
@ -288,7 +358,7 @@ const DatabaseSetup = () => {
|
||||||
{(status === 'idle' || status === 'error') && !dbExists && (
|
{(status === 'idle' || status === 'error') && !dbExists && (
|
||||||
<Button
|
<Button
|
||||||
variant='solid'
|
variant='solid'
|
||||||
className="min-w-40"
|
className={status === 'error' ? 'min-w-40' : 'min-w-25'}
|
||||||
onClick={() => setIsPasswordDialogOpen(true)}
|
onClick={() => setIsPasswordDialogOpen(true)}
|
||||||
>
|
>
|
||||||
{status === 'error' ? 'Retry Migration' : 'Migrate'}
|
{status === 'error' ? 'Retry Migration' : 'Migrate'}
|
||||||
|
|
@ -309,23 +379,33 @@ const DatabaseSetup = () => {
|
||||||
isOpen={isPasswordDialogOpen}
|
isOpen={isPasswordDialogOpen}
|
||||||
onClose={() => setIsPasswordDialogOpen(false)}
|
onClose={() => setIsPasswordDialogOpen(false)}
|
||||||
onRequestClose={() => 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}
|
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>
|
<div>
|
||||||
<h5 className="text-gray-900 dark:text-gray-100">Setup authorization</h5>
|
<h5 className={isDark ? 'text-gray-100' : 'text-gray-900'}>Setup authorization</h5>
|
||||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
<p className={classNames('mt-1 text-sm', isDark ? 'text-gray-400' : 'text-gray-500')}>
|
||||||
Enter the setup password to start database migration.
|
Enter the setup password to start database migration.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Header>
|
</Dialog.Header>
|
||||||
<Dialog.Body className="pt-5">
|
<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
|
Setup password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<PasswordInput
|
||||||
autoFocus
|
autoFocus
|
||||||
type="password"
|
|
||||||
value={setupPassword}
|
value={setupPassword}
|
||||||
onChange={(event) => setSetupPassword(event.target.value)}
|
onChange={(event) => setSetupPassword(event.target.value)}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
|
|
@ -335,15 +415,30 @@ const DatabaseSetup = () => {
|
||||||
}}
|
}}
|
||||||
placeholder="Enter setup password"
|
placeholder="Enter setup password"
|
||||||
autoComplete="off"
|
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
|
This protects the migration action when users are redirected to setup after a database
|
||||||
problem.
|
problem.
|
||||||
</p>
|
</p>
|
||||||
</Dialog.Body>
|
</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
|
<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)}
|
onClick={() => setIsPasswordDialogOpen(false)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|
@ -358,7 +453,7 @@ const DatabaseSetup = () => {
|
||||||
</Dialog.Footer>
|
</Dialog.Footer>
|
||||||
</Dialog>
|
</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.
|
This page is only visible when the database does not exist.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue