http
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
This commit is contained in:
parent
bfc13ecfd5
commit
339318b874
2 changed files with 13 additions and 119 deletions
|
|
@ -50,8 +50,6 @@ ENV PORT=3000 \
|
||||||
BACKUP_DIR=/app/backups \
|
BACKUP_DIR=/app/backups \
|
||||||
WORK_DIR=/app \
|
WORK_DIR=/app \
|
||||||
DEBUG=false \
|
DEBUG=false \
|
||||||
CERTS_PATH= \
|
|
||||||
DUAL_MODE=false \
|
|
||||||
ADMIN_IDS=
|
ADMIN_IDS=
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
@ -60,11 +58,11 @@ EXPOSE 3000
|
||||||
# Increased start-period to 90s to allow server initialization (Isar DB, cron jobs, etc.)
|
# Increased start-period to 90s to allow server initialization (Isar DB, cron jobs, etc.)
|
||||||
# Isar может долго инициализироваться при первом запуске или при большом объеме данных
|
# Isar может долго инициализироваться при первом запуске или при большом объеме данных
|
||||||
# Используем 127.0.0.1 для healthcheck (внутри контейнера работает даже если сервер слушает на 0.0.0.0)
|
# Используем 127.0.0.1 для healthcheck (внутри контейнера работает даже если сервер слушает на 0.0.0.0)
|
||||||
# Сервер работает по HTTPS, поэтому проверяем https:// с флагом -k для игнорирования проверки сертификата
|
# Сервер работает только по HTTP (HTTPS обрабатывается на уровне reverse proxy в Coolify)
|
||||||
# Пробуем curl, если не работает - используем wget как fallback
|
# Пробуем curl, если не работает - используем wget как fallback
|
||||||
HEALTHCHECK --interval=15s --timeout=10s --start-period=90s --retries=3 \
|
HEALTHCHECK --interval=15s --timeout=10s --start-period=90s --retries=3 \
|
||||||
CMD curl -f -sS -k --max-time 8 --connect-timeout 3 https://127.0.0.1:${PORT:-3000}/health > /dev/null 2>&1 || \
|
CMD curl -f -sS --max-time 8 --connect-timeout 3 http://127.0.0.1:${PORT:-3000}/health > /dev/null 2>&1 || \
|
||||||
wget --quiet --no-check-certificate --tries=1 --timeout=8 --spider https://127.0.0.1:${PORT:-3000}/health || exit 1
|
wget --quiet --tries=1 --timeout=8 --spider http://127.0.0.1:${PORT:-3000}/health || exit 1
|
||||||
|
|
||||||
# Start server - все настройки читаются из environment variables
|
# Start server - все настройки читаются из environment variables
|
||||||
CMD ["/app/server"]
|
CMD ["/app/server"]
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,6 @@ class MnemoShelf {
|
||||||
? InternetAddress(addressArg)
|
? InternetAddress(addressArg)
|
||||||
: InternetAddress.anyIPv4;
|
: InternetAddress.anyIPv4;
|
||||||
final port = int.tryParse(portArg ?? '') ?? 3000;
|
final port = int.tryParse(portArg ?? '') ?? 3000;
|
||||||
final certs = Platform.environment['CERTS_PATH']; // null = HTTP without SSL
|
|
||||||
final dualMode = Platform.environment['DUAL_MODE'] == 'true' ||
|
|
||||||
Platform.environment['DUAL_MODE'] ==
|
|
||||||
'1'; // Запуск HTTP и HTTPS одновременно
|
|
||||||
|
|
||||||
// V2 APIs (new RESTful API with OAuth2/JWT)
|
// V2 APIs (new RESTful API with OAuth2/JWT)
|
||||||
final v2Routers = [
|
final v2Routers = [
|
||||||
|
|
@ -89,20 +85,6 @@ class MnemoShelf {
|
||||||
// ..mount('/', createStaticHandler('public'));
|
// ..mount('/', createStaticHandler('public'));
|
||||||
|
|
||||||
// Configure a pipeline with proper CORS configuration
|
// Configure a pipeline with proper CORS configuration
|
||||||
// Поддерживаем как HTTP (разработка), так и HTTPS (продакшен)
|
|
||||||
// Note: allowedOrigins is kept for reference but not used since we allow all origins
|
|
||||||
// final allowedOrigins = [
|
|
||||||
// 'https://1592725-cf88967.twc1.net',
|
|
||||||
// 'https://1592725-cf88967.twc1.net:443',
|
|
||||||
// '5492281-cf88967.twc1.net',
|
|
||||||
// '5492281-cf88967.twc1.net:8443'
|
|
||||||
// 'http://localhost:*',
|
|
||||||
// 'http://localhost:51717', // для разработки
|
|
||||||
// 'http://localhost:3000', // для разработки
|
|
||||||
// 'http://localhost:8000', // для разработки
|
|
||||||
// 'http://127.0.0.1:8000', // для разработки
|
|
||||||
// ];
|
|
||||||
|
|
||||||
final corsConfig = {
|
final corsConfig = {
|
||||||
'Access-Control-Allow-Origin': '*', // Разрешаем все домены для гибкости
|
'Access-Control-Allow-Origin': '*', // Разрешаем все домены для гибкости
|
||||||
'Access-Control-Allow-Methods':
|
'Access-Control-Allow-Methods':
|
||||||
|
|
@ -113,12 +95,6 @@ class MnemoShelf {
|
||||||
'Access-Control-Max-Age': '86400',
|
'Access-Control-Max-Age': '86400',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Добавляем HSTS только для HTTPS соединений
|
|
||||||
if (certs != null) {
|
|
||||||
corsConfig['Strict-Transport-Security'] =
|
|
||||||
'max-age=31536000; includeSubDomains';
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORS middleware that handles preflight requests
|
// CORS middleware that handles preflight requests
|
||||||
Middleware corsMiddleware() {
|
Middleware corsMiddleware() {
|
||||||
return (Handler innerHandler) {
|
return (Handler innerHandler) {
|
||||||
|
|
@ -172,92 +148,17 @@ class MnemoShelf {
|
||||||
|
|
||||||
final handler = rootRouter.call;
|
final handler = rootRouter.call;
|
||||||
|
|
||||||
if (dualMode) {
|
// Запуск HTTP сервера (HTTPS обрабатывается на уровне reverse proxy в Coolify)
|
||||||
// Запуск HTTP и HTTPS серверов одновременно
|
final server = await _createServer(
|
||||||
await _startDualServers(handler, address, port, certs);
|
|
||||||
} else {
|
|
||||||
// Обычный запуск одного сервера
|
|
||||||
final server = await _createServer(
|
|
||||||
handler,
|
|
||||||
address,
|
|
||||||
port,
|
|
||||||
securityContext: certs != null ? getSecurityContext(certs) : null,
|
|
||||||
poweredByHeader: null,
|
|
||||||
shared: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
final protocol = certs != null ? 'https' : 'http';
|
|
||||||
print(
|
|
||||||
'Serving app at $protocol://${server.address.address}:${server.port}');
|
|
||||||
final url = '$protocol://${server.address.address}:${server.port}';
|
|
||||||
print('Server listening on $url');
|
|
||||||
|
|
||||||
if (certs != null) {
|
|
||||||
print('HTTPS enabled with SSL certificates from: $certs');
|
|
||||||
print('Domain: 1592725-cf88967.twc1.net');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _startDualServers(
|
|
||||||
Handler handler, InternetAddress address, int port, String? certs) async {
|
|
||||||
print('🚀 Starting DUAL mode: HTTP + HTTPS servers');
|
|
||||||
|
|
||||||
// HTTP сервер на указанном порту
|
|
||||||
final httpServer = await _createServer(
|
|
||||||
handler,
|
handler,
|
||||||
address,
|
address,
|
||||||
port,
|
port,
|
||||||
securityContext: null, // HTTP без SSL
|
|
||||||
poweredByHeader: null,
|
poweredByHeader: null,
|
||||||
shared: true,
|
shared: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// HTTPS сервер на порту + 1 (например, 8443 -> 8081)
|
print('Serving app at http://${server.address.address}:${server.port}');
|
||||||
final httpsPort = port + 1;
|
print('Server listening on http://${server.address.address}:${server.port}');
|
||||||
final httpsServer = await _createServer(
|
|
||||||
handler,
|
|
||||||
address,
|
|
||||||
httpsPort,
|
|
||||||
securityContext: certs != null ? getSecurityContext(certs) : null,
|
|
||||||
poweredByHeader: null,
|
|
||||||
shared: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
print('✅ Both servers started successfully!');
|
|
||||||
print(
|
|
||||||
'📡 HTTP server: http://${httpServer.address.address}:${httpServer.port}');
|
|
||||||
print(
|
|
||||||
'🔒 HTTPS server: https://${httpsServer.address.address}:${httpsServer.port}');
|
|
||||||
|
|
||||||
if (certs != null) {
|
|
||||||
print('🔐 HTTPS enabled with SSL certificates from: $certs');
|
|
||||||
print('🌐 Domain: 1592725-cf88967.twc1.net');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SecurityContext getSecurityContext(String path) {
|
|
||||||
// Для Let's Encrypt используем fullchain.pem и privkey.pem
|
|
||||||
// Для самоподписанных сертификатов используем server.crt и server.key
|
|
||||||
String serverCert, key;
|
|
||||||
|
|
||||||
// if (path.contains('letsencrypt') || path.contains('/etc/letsencrypt/')) {
|
|
||||||
// // Let's Encrypt сертификаты
|
|
||||||
// // serverCert = path + '/fullchain.pem';
|
|
||||||
// // key = path + '/privkey.pem';
|
|
||||||
// } else {
|
|
||||||
// // // Самоподписанные сертификаты
|
|
||||||
// // serverCert = path.startsWith('/')
|
|
||||||
// // ? path + '/server.crt'
|
|
||||||
// // : Platform.script.resolve('$path/server.crt').toFilePath();
|
|
||||||
// // key = path.startsWith('/')
|
|
||||||
// // ? path + '/server.key'
|
|
||||||
// // : Platform.script.resolve('$path/server.key').toFilePath();
|
|
||||||
// }
|
|
||||||
|
|
||||||
return SecurityContext(withTrustedRoots: false);
|
|
||||||
// ..useCertificateChain(serverCert)
|
|
||||||
// ..usePrivateKey(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger(String tag) => (String msg, bool isError) {
|
logger(String tag) => (String msg, bool isError) {
|
||||||
|
|
@ -272,22 +173,17 @@ class MnemoShelf {
|
||||||
Handler handler,
|
Handler handler,
|
||||||
InternetAddress address,
|
InternetAddress address,
|
||||||
int port, {
|
int port, {
|
||||||
SecurityContext? securityContext,
|
|
||||||
int? backlog,
|
int? backlog,
|
||||||
bool shared = false,
|
bool shared = false,
|
||||||
String? poweredByHeader = 'Dart with package:shelf',
|
String? poweredByHeader = 'Dart with package:shelf',
|
||||||
}) async {
|
}) async {
|
||||||
backlog ??= 0;
|
backlog ??= 0;
|
||||||
var server = await (securityContext == null
|
final server = await HttpServer.bind(
|
||||||
? HttpServer.bind(address, port, backlog: backlog, shared: shared)
|
address,
|
||||||
: HttpServer.bindSecure(
|
port,
|
||||||
address,
|
backlog: backlog,
|
||||||
port,
|
shared: shared,
|
||||||
securityContext,
|
);
|
||||||
backlog: backlog,
|
|
||||||
shared: shared,
|
|
||||||
requestClientCertificate: true,
|
|
||||||
));
|
|
||||||
// Запускаем обработку запросов (неблокирующая операция)
|
// Запускаем обработку запросов (неблокирующая операция)
|
||||||
// serveRequests запускает обработку запросов в фоне и возвращает управление сразу
|
// serveRequests запускает обработку запросов в фоне и возвращает управление сразу
|
||||||
shelf_io.serveRequests(server, handler, poweredByHeader: poweredByHeader);
|
shelf_io.serveRequests(server, handler, poweredByHeader: poweredByHeader);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue