bugfixes
This commit is contained in:
parent
af60ee5319
commit
ab9f1f79b5
@ -3,6 +3,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -246,6 +247,7 @@ func localAppHostCandidates() []string {
|
|||||||
host = normalizeOriginHost(host)
|
host = normalizeOriginHost(host)
|
||||||
if host != "" && !strings.Contains(host, ".") {
|
if host != "" && !strings.Contains(host, ".") {
|
||||||
add(host + ".local")
|
add(host + ".local")
|
||||||
|
addHostWithDNSSuffixes(add, host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +280,44 @@ func addInterfaceIPv4Hosts(add func(string)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(ip4.String())
|
add(ip4.String())
|
||||||
|
addReverseLookupHosts(add, ip4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addHostWithDNSSuffixes(add func(string), host string) {
|
||||||
|
host = normalizeOriginHost(host)
|
||||||
|
if host == "" || strings.Contains(host, ".") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range []string{
|
||||||
|
"USERDNSDOMAIN",
|
||||||
|
"DNSDOMAIN",
|
||||||
|
"LOCALDOMAIN",
|
||||||
|
} {
|
||||||
|
suffix := normalizeOriginHost(os.Getenv(key))
|
||||||
|
if suffix == "" || !strings.Contains(suffix, ".") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
add(host + "." + suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addReverseLookupHosts(add func(string), ip net.IP) {
|
||||||
|
if ip == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
names, err := net.DefaultResolver.LookupAddr(ctx, ip.String())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
add(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
backend/dist/nsfwapp-linux-amd64
vendored
BIN
backend/dist/nsfwapp-linux-amd64
vendored
Binary file not shown.
BIN
backend/dist/nsfwapp.exe
vendored
BIN
backend/dist/nsfwapp.exe
vendored
Binary file not shown.
BIN
backend/dist/nsfwapp_amd64.deb
vendored
BIN
backend/dist/nsfwapp_amd64.deb
vendored
Binary file not shown.
@ -8,7 +8,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed ml/*.py ml/detection_labels.json ai_server.py recorder-cert.pem recorder-key.pem yolo26n-pose.pt
|
//go:embed ml/*.py ml/detection_labels.json ai_server.py yolo26n-pose.pt
|
||||||
var embeddedMLFiles embed.FS
|
var embeddedMLFiles embed.FS
|
||||||
|
|
||||||
func embeddedWriteFileIfNeeded(dstPath string, b []byte, perm os.FileMode) error {
|
func embeddedWriteFileIfNeeded(dstPath string, b []byte, perm os.FileMode) error {
|
||||||
@ -126,42 +126,3 @@ func embeddedPoseModelPath() (string, error) {
|
|||||||
|
|
||||||
return dstPath, nil
|
return dstPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func embeddedTLSCertBytes() ([]byte, error) {
|
|
||||||
return embeddedMLFiles.ReadFile("recorder-cert.pem")
|
|
||||||
}
|
|
||||||
|
|
||||||
func embeddedTLSKeyBytes() ([]byte, error) {
|
|
||||||
return embeddedMLFiles.ReadFile("recorder-key.pem")
|
|
||||||
}
|
|
||||||
|
|
||||||
func embeddedTLSFilesDir() (string, string, error) {
|
|
||||||
dir := filepath.Join(os.TempDir(), "nsfwapp-tls")
|
|
||||||
|
|
||||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
certBytes, err := embeddedTLSCertBytes()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
keyBytes, err := embeddedTLSKeyBytes()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
certPath := filepath.Join(dir, "recorder-cert.pem")
|
|
||||||
keyPath := filepath.Join(dir, "recorder-key.pem")
|
|
||||||
|
|
||||||
if err := os.WriteFile(certPath, certBytes, 0600); err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.WriteFile(keyPath, keyBytes, 0600); err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return certPath, keyPath, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -5,9 +5,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -16,6 +18,9 @@ import (
|
|||||||
var (
|
var (
|
||||||
appLogMu sync.Mutex
|
appLogMu sync.Mutex
|
||||||
appLogFile *os.File
|
appLogFile *os.File
|
||||||
|
appConsoleOut *os.File
|
||||||
|
appStdStreamsRedirected bool
|
||||||
|
appDiagnosticsConfigured bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func appLogPath() string {
|
func appLogPath() string {
|
||||||
@ -59,6 +64,7 @@ func initAppLog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appLogFile = f
|
appLogFile = f
|
||||||
|
configureAppDiagnosticsLocked()
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(
|
_, _ = fmt.Fprintf(
|
||||||
appLogFile,
|
appLogFile,
|
||||||
@ -69,6 +75,26 @@ func initAppLog() {
|
|||||||
_ = appLogFile.Sync()
|
_ = appLogFile.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureAppDiagnosticsLocked() {
|
||||||
|
if appLogFile == nil || appDiagnosticsConfigured {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetOutput(appLogFile)
|
||||||
|
log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.Lshortfile)
|
||||||
|
debug.SetTraceback("all")
|
||||||
|
|
||||||
|
appConsoleOut = os.Stdout
|
||||||
|
|
||||||
|
if err := redirectProcessStdStreamsToFile(appLogFile); err != nil {
|
||||||
|
_, _ = fmt.Fprintf(appLogFile, "⚠️ stdout/stderr konnte nicht auf recorder.log umgeleitet werden: %v\n", err)
|
||||||
|
} else {
|
||||||
|
appStdStreamsRedirected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
appDiagnosticsConfigured = true
|
||||||
|
}
|
||||||
|
|
||||||
func closeAppLog() {
|
func closeAppLog() {
|
||||||
appLogMu.Lock()
|
appLogMu.Lock()
|
||||||
defer appLogMu.Unlock()
|
defer appLogMu.Unlock()
|
||||||
@ -113,7 +139,13 @@ func appLogWriteLine(line string) {
|
|||||||
appLogMu.Lock()
|
appLogMu.Lock()
|
||||||
defer appLogMu.Unlock()
|
defer appLogMu.Unlock()
|
||||||
|
|
||||||
|
if appStdStreamsRedirected {
|
||||||
|
if appConsoleOut != nil && appConsoleOut != appLogFile {
|
||||||
|
_, _ = appConsoleOut.WriteString(out)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
fmt.Print(out)
|
fmt.Print(out)
|
||||||
|
}
|
||||||
|
|
||||||
if appLogFile != nil {
|
if appLogFile != nil {
|
||||||
_, _ = appLogFile.WriteString(out)
|
_, _ = appLogFile.WriteString(out)
|
||||||
@ -129,6 +161,44 @@ func appLogf(format string, args ...any) {
|
|||||||
appLogWriteLine(fmt.Sprintf(format, args...))
|
appLogWriteLine(fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appLogMultiline(prefix string, text string) {
|
||||||
|
text = strings.TrimRight(text, "\r\n")
|
||||||
|
if text == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(text, "\n") {
|
||||||
|
appLogWriteLine(prefix + strings.TrimRight(line, "\r"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appLogPanic(scope string, recovered any) {
|
||||||
|
scope = strings.TrimSpace(scope)
|
||||||
|
if scope == "" {
|
||||||
|
scope = "unbekannt"
|
||||||
|
}
|
||||||
|
|
||||||
|
appLogln("❌ PANIC in "+scope+":", recovered)
|
||||||
|
appLogln("Stacktrace:")
|
||||||
|
appLogMultiline(" ", string(debug.Stack()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func appRecoverPanic(scope string) bool {
|
||||||
|
if recovered := recover(); recovered != nil {
|
||||||
|
appLogPanic(scope, recovered)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func appGo(scope string, fn func()) {
|
||||||
|
go func() {
|
||||||
|
defer appRecoverPanic(scope)
|
||||||
|
fn()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// appErrorLogSuppressed unterdrückt das automatische ❌-Logging für bekannte,
|
// appErrorLogSuppressed unterdrückt das automatische ❌-Logging für bekannte,
|
||||||
// erwartbare und sehr häufige Fehler (z.B. abgelaufene HLS-Edge-URLs während
|
// erwartbare und sehr häufige Fehler (z.B. abgelaufene HLS-Edge-URLs während
|
||||||
// eines Stream-Reconnects). Der Fehler wird weiterhin zurückgegeben, damit die
|
// eines Stream-Reconnects). Der Fehler wird weiterhin zurückgegeben, damit die
|
||||||
|
|||||||
26
backend/log_redirect_unix.go
Normal file
26
backend/log_redirect_unix.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func redirectProcessStdStreamsToFile(f *os.File) error {
|
||||||
|
if f == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fd := int(f.Fd())
|
||||||
|
if err := syscall.Dup2(fd, int(os.Stdout.Fd())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.Dup2(fd, int(os.Stderr.Fd())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Stdout = f
|
||||||
|
os.Stderr = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
27
backend/log_redirect_windows.go
Normal file
27
backend/log_redirect_windows.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func redirectProcessStdStreamsToFile(f *os.File) error {
|
||||||
|
if f == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
handle := windows.Handle(f.Fd())
|
||||||
|
if err := windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, handle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := windows.SetStdHandle(windows.STD_ERROR_HANDLE, handle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Stdout = f
|
||||||
|
os.Stderr = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -1,26 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEbjCCAtagAwIBAgIRAKGvq9b44yCGC32q8PwkueowDQYJKoZIhvcNAQELBQAw
|
|
||||||
gYsxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEwMC4GA1UECwwnVEVH
|
|
||||||
RFNTRFxSb3RoZXJATDE0UEJCSzk1MTAwMDA2IChSb3RoZXIpMTcwNQYDVQQDDC5t
|
|
||||||
a2NlcnQgVEVHRFNTRFxSb3RoZXJATDE0UEJCSzk1MTAwMDA2IChSb3RoZXIpMB4X
|
|
||||||
DTI2MDUwODEwMDQzN1oXDTI4MDgwODEwMDQzN1owWzEnMCUGA1UEChMebWtjZXJ0
|
|
||||||
IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTAwLgYDVQQLDCdURUdEU1NEXFJvdGhl
|
|
||||||
ckBMMTRQQkJLOTUxMDAwMDYgKFJvdGhlcikwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
|
||||||
DwAwggEKAoIBAQDDghqU3igC3sNP2zAJTZbvZcT1xxjUgk3oA2j0WwNPO+xhCH4K
|
|
||||||
mJ6enU5zrGWI4+T0IHfbo737rvgk6XfTDSvzB9Uags8egBUGxbr9eMBya0hmnYGs
|
|
||||||
zIQGJeI4TIeQ7sa7krCpMB/e1YxR1qYFf1C8oSt43dJCdJP4Ev//BaR7rrFt3D3N
|
|
||||||
CC2OgxVYSh/hR9XNIIEPMoioQIYhle41mQWjyPFt16Fjg9sWrrnHSz7M4cgVVSot
|
|
||||||
+aBlRjbxgOarLyST3XdtjAfC6CqxnzprX1LfznuULjatoS+pFDBDoy2B13xwa9Br
|
|
||||||
f+Xy3CswmSO+AzNomWfbeBkJnl6xb3Tb1urrAgMBAAGjfDB6MA4GA1UdDwEB/wQE
|
|
||||||
AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBR0Zph7hFKq4GpE
|
|
||||||
XO0y3U+T0iFoQDAyBgNVHREEKzApgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAGHBAoAARkwDQYJKoZIhvcNAQELBQADggGBAJShhgQS+9cwuVOJUIW+
|
|
||||||
uy9t1ZAjR9qTd31kHdK1vu294GiavrG8RD+92BRujG4Fq1ambvqcu9sUaC8+0HAP
|
|
||||||
cQvdmnbTxG0ZHkGP7VVj3Poee0jpsVAVtV3aVwKxyO/E7Mv/nEKTqNfnECbEuiqC
|
|
||||||
tOJ8mI3E9tf2/ljzq2PqvfIPFBBtUT2lihNAFXM/06Nzoa1NPdfYd58N+VBFSOSq
|
|
||||||
zEM4fmpcqMThZGKGBoWmHwTE5Mw6mZFLyxHJpOFu5wNzyPJGes5o/F0ot5n+ZGah
|
|
||||||
GbYaGmhEedOxC3/WXRtexJmZ0jrB8Hkx+c/4h6+0HKU/PelrFJS+c2d6h8YGXyhv
|
|
||||||
pUsfyyoQzx7rSgyLfJqXK+52ZtnWNc0yVVMyKJwjvhv4mcP9l/q3UGR/i7xkD5Pc
|
|
||||||
2bLgM+wc8nKI7Yd/xejLpQDC0b/P1XibT1T3GRjiC/eu64G/uFOF+m3P5XvmF8JO
|
|
||||||
0cj8fXwUCq+TVD+22rFmtT+2VYkf8RUsC48Ou+PHccmDyw==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDDghqU3igC3sNP
|
|
||||||
2zAJTZbvZcT1xxjUgk3oA2j0WwNPO+xhCH4KmJ6enU5zrGWI4+T0IHfbo737rvgk
|
|
||||||
6XfTDSvzB9Uags8egBUGxbr9eMBya0hmnYGszIQGJeI4TIeQ7sa7krCpMB/e1YxR
|
|
||||||
1qYFf1C8oSt43dJCdJP4Ev//BaR7rrFt3D3NCC2OgxVYSh/hR9XNIIEPMoioQIYh
|
|
||||||
le41mQWjyPFt16Fjg9sWrrnHSz7M4cgVVSot+aBlRjbxgOarLyST3XdtjAfC6Cqx
|
|
||||||
nzprX1LfznuULjatoS+pFDBDoy2B13xwa9Brf+Xy3CswmSO+AzNomWfbeBkJnl6x
|
|
||||||
b3Tb1urrAgMBAAECggEBAI31EBv72w2KdkKroot+vROCz6quMAdNvgezQif7VcHY
|
|
||||||
fuBN7EcBXltJWUeAbBEjeIESejUPBcmT2DXlF841CC5lB4VCaeV5lsreE9IsNYBf
|
|
||||||
CakIwLmZnltgcovydZT063QTJRcUDHAems5pjw76zMLKO+h9GEiMoUxFb3/atv3e
|
|
||||||
K5HE+uUd6JcPU0q91S6TxqvS9gH30OOkwH6Kl4tzWauQAKg/sVzYafbyTAuEpiMO
|
|
||||||
jLLLAn/sOmiL4knnRVu1FK4aZSH+t9BN9YuwfucXFn9jFgPtIJ0bF1GuqnwIeDI5
|
|
||||||
BTNanHfaZRHD0+FbS0KbhARIFgDveoGrdoEtou0eDOECgYEA5dJqUvk96f7zCrxD
|
|
||||||
eSuUl1MvOjydY3GacfjevZoTxJDqh4Wt2wgLYJc4XB7tI02ci7NncecG8qPfqWq7
|
|
||||||
aFjQAGAYdR+XvE7R2m3OFK9WbRX4cH6b+l7mSq0eOvcAAajn4RuNr/il+7Opw7zX
|
|
||||||
eKUB/pnW36B+ethdr5l+kDUQb8MCgYEA2ccanIT2QAxJb5j0F287NGhaTPuWfxcX
|
|
||||||
2T53I2fIwMiell0YXjwsKwKpckubRB2f5X4b9QJifUTmtxHky/Tx4H9OVhCaxumw
|
|
||||||
edol5YNzZaP/Tx8m2mqpyN2WPbezIFeA+GNcmDAxEo/NT2B64OVCHWKosndvb+Yk
|
|
||||||
GFmVb+g9zbkCgYEA1YFJLZRHJJ+pgour02HdRUgOU/gD72KWrNMbeuEtBCvs9cIG
|
|
||||||
5bjveOiDf3FrtKRhjpc4vuR12+zJ2EZDnIkFk5OypPyYpmRDKL1h+m15yRXkG/5D
|
|
||||||
QbHwF+gEcZsN8nzMDqDeXGCPMuqSCDnjoz0IQVMB//bGCbIANyZOIgJqJqkCgYEA
|
|
||||||
vHc+ZG383fjEJLvtoco1JmmYnD6uQ1Ys4WjZmd5bMdtswxvV1tekMaSgF7WurQgm
|
|
||||||
NGkqsKJbsaVLNOtbYdac7He/x2OfTr02aH2Nhk54M2H1tPd0nFjqjlaVitvLPRX9
|
|
||||||
GviCTYKHNVUVjLgmHzLIQL382FXcLq6wVhJQ7QPDWKECgYA6ZmdB8waN2SFOJRnj
|
|
||||||
5zWTgdImicl4yCu8PaEyloO9whYSdYH8zU3K+FKaowXGNInp3BGCmP9TUwoG8Iwx
|
|
||||||
Ug5d55P2FgwnhCi59Y6dMXih1p94BuQ0vcWPBqztofl7lZbBEnjwzKVycFvcyNFX
|
|
||||||
QFb/UA4/CfQli9ciDtlqzZt8SA==
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
@ -22,19 +22,6 @@ import (
|
|||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
var embeddedTLSOnce sync.Once
|
|
||||||
var embeddedTLSCertPath string
|
|
||||||
var embeddedTLSKeyPath string
|
|
||||||
var embeddedTLSErr error
|
|
||||||
|
|
||||||
func embeddedTLSPathsCached() (string, string, error) {
|
|
||||||
embeddedTLSOnce.Do(func() {
|
|
||||||
embeddedTLSCertPath, embeddedTLSKeyPath, embeddedTLSErr = embeddedTLSFilesDir()
|
|
||||||
})
|
|
||||||
|
|
||||||
return embeddedTLSCertPath, embeddedTLSKeyPath, embeddedTLSErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func escapePowerShell(s string) string {
|
func escapePowerShell(s string) string {
|
||||||
return strings.ReplaceAll(s, "'", "''")
|
return strings.ReplaceAll(s, "'", "''")
|
||||||
}
|
}
|
||||||
@ -654,7 +641,7 @@ func startAIServer(ctx context.Context) (*aiServerProcess, error) {
|
|||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
appGo("ai-server-wait", func() {
|
||||||
err := cmd.Wait()
|
err := cmd.Wait()
|
||||||
|
|
||||||
if err != nil && ctx.Err() == nil && !proc.isStopping() {
|
if err != nil && ctx.Err() == nil && !proc.isStopping() {
|
||||||
@ -664,7 +651,7 @@ func startAIServer(ctx context.Context) (*aiServerProcess, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
close(proc.done)
|
close(proc.done)
|
||||||
}()
|
})
|
||||||
|
|
||||||
if err := waitForAIServer(ctx, aiServerURL(), 120*time.Second); err != nil {
|
if err := waitForAIServer(ctx, aiServerURL(), 120*time.Second); err != nil {
|
||||||
appLogln("⚠️ AI Server noch nicht bereit:", err)
|
appLogln("⚠️ AI Server noch nicht bereit:", err)
|
||||||
@ -820,7 +807,7 @@ func startTrainingBestModelWatcher(ctx context.Context, trainingRoot string) {
|
|||||||
|
|
||||||
lastRestart = time.Now()
|
lastRestart = time.Now()
|
||||||
appLogln("🔄 Training-Modell geändert:", strings.Join(changedPaths, " | "))
|
appLogln("🔄 Training-Modell geändert:", strings.Join(changedPaths, " | "))
|
||||||
go restartAIServer()
|
appGo("ai-server-restart", restartAIServer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -961,12 +948,15 @@ func tlsCertFile() string {
|
|||||||
return resolveMaybeRelativeToExe(raw)
|
return resolveMaybeRelativeToExe(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
certPath, _, err := embeddedTLSPathsCached()
|
certPath, _, err := localTLSPathsCached()
|
||||||
if err == nil && certPath != "" {
|
if err == nil && certPath != "" {
|
||||||
return certPath
|
return certPath
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
appLogln("⚠️ dynamisches TLS-Zertifikat konnte nicht erstellt werden:", err)
|
||||||
|
}
|
||||||
|
|
||||||
return resolveMaybeRelativeToExe("recorder-cert.pem")
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func tlsKeyFile() string {
|
func tlsKeyFile() string {
|
||||||
@ -975,12 +965,16 @@ func tlsKeyFile() string {
|
|||||||
return resolveMaybeRelativeToExe(raw)
|
return resolveMaybeRelativeToExe(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, keyPath, err := embeddedTLSPathsCached()
|
_, keyPath, err := localTLSPathsCached()
|
||||||
if err == nil && keyPath != "" {
|
if err == nil && keyPath != "" {
|
||||||
return keyPath
|
return keyPath
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolveMaybeRelativeToExe("recorder-key.pem")
|
if err != nil {
|
||||||
|
appLogln("⚠️ dynamischer TLS-Key konnte nicht erstellt werden:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func publicAppURL() string {
|
func publicAppURL() string {
|
||||||
@ -1030,7 +1024,13 @@ func publicAppURLs() []string {
|
|||||||
func main() {
|
func main() {
|
||||||
clearAppLog()
|
clearAppLog()
|
||||||
initAppLog()
|
initAppLog()
|
||||||
defer closeAppLog()
|
defer func() {
|
||||||
|
panicked := appRecoverPanic("main")
|
||||||
|
closeAppLog()
|
||||||
|
if panicked {
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
loadAppEnv()
|
loadAppEnv()
|
||||||
|
|
||||||
@ -1055,7 +1055,7 @@ func main() {
|
|||||||
"Die Anwendung wird bereits ausgeführt.",
|
"Die Anwendung wird bereits ausgeführt.",
|
||||||
)
|
)
|
||||||
appLogln("⚠️ Die App läuft bereits oder Port " + appPort() + " ist bereits belegt.")
|
appLogln("⚠️ Die App läuft bereits oder Port " + appPort() + " ist bereits belegt.")
|
||||||
os.Exit(0)
|
return
|
||||||
}
|
}
|
||||||
defer appLn.Close()
|
defer appLn.Close()
|
||||||
|
|
||||||
@ -1071,7 +1071,7 @@ func main() {
|
|||||||
|
|
||||||
startPostworkLeftoverScanOnStartup()
|
startPostworkLeftoverScanOnStartup()
|
||||||
|
|
||||||
go startGeneratedGarbageCollector()
|
appGo("generated-garbage-collector", startGeneratedGarbageCollector)
|
||||||
|
|
||||||
// Altes NSFW-ONNX nicht mehr hart initialisieren.
|
// Altes NSFW-ONNX nicht mehr hart initialisieren.
|
||||||
// Das neue Training-/YOLO-Modell wird über trainingPredictFrame genutzt.
|
// Das neue Training-/YOLO-Modell wird über trainingPredictFrame genutzt.
|
||||||
@ -1089,7 +1089,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
appLogln("❌ auth init:", err)
|
appLogln("❌ auth init:", err)
|
||||||
closeNSFW()
|
closeNSFW()
|
||||||
os.Exit(1)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
store := registerRoutes(mux, auth)
|
store := registerRoutes(mux, auth)
|
||||||
@ -1100,14 +1100,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hintergrund-Worker
|
// Hintergrund-Worker
|
||||||
go startDiskSpaceGuard()
|
appGo("disk-space-guard", startDiskSpaceGuard)
|
||||||
go startChaturbateOnlinePoller(store)
|
appGo("chaturbate-online-poller", func() { startChaturbateOnlinePoller(store) })
|
||||||
go startChaturbateAutoStartWorker(store)
|
appGo("chaturbate-autostart-worker", func() { startChaturbateAutoStartWorker(store) })
|
||||||
go startMyFreeCamsAutoStartWorker(store)
|
appGo("myfreecams-autostart-worker", func() { startMyFreeCamsAutoStartWorker(store) })
|
||||||
|
|
||||||
// optional: Bootstrap nur im Hintergrund
|
// optional: Bootstrap nur im Hintergrund
|
||||||
if getSettings().UseChaturbateAPI {
|
if getSettings().UseChaturbateAPI {
|
||||||
go func() {
|
appGo("chaturbate-api-bootstrap", func() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@ -1116,7 +1116,7 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
appLogln("✅ Chaturbate API geladen")
|
appLogln("✅ Chaturbate API geladen")
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ensureCoversDir(); err != nil {
|
if _, err := ensureCoversDir(); err != nil {
|
||||||
@ -1164,14 +1164,14 @@ func main() {
|
|||||||
stopSig := make(chan os.Signal, 1)
|
stopSig := make(chan os.Signal, 1)
|
||||||
signal.Notify(stopSig, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(stopSig, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
go func() {
|
appGo("shutdown-signal-listener", func() {
|
||||||
<-stopSig
|
<-stopSig
|
||||||
shutdown()
|
shutdown()
|
||||||
}()
|
})
|
||||||
|
|
||||||
serverErrCh := make(chan error, 1)
|
serverErrCh := make(chan error, 1)
|
||||||
|
|
||||||
go func() {
|
appGo("http-server", func() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if httpsEnabled() {
|
if httpsEnabled() {
|
||||||
@ -1186,9 +1186,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
serverErrCh <- nil
|
serverErrCh <- nil
|
||||||
}()
|
})
|
||||||
|
|
||||||
go func() {
|
appGo("ai-server-start", func() {
|
||||||
aiProc, err := startAIServer(appCtx)
|
aiProc, err := startAIServer(appCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
appLogln("⚠️ AI Server konnte nicht gestartet werden:", err)
|
appLogln("⚠️ AI Server konnte nicht gestartet werden:", err)
|
||||||
@ -1205,23 +1205,25 @@ func main() {
|
|||||||
watchedTrainingRoot := aiServerRoot
|
watchedTrainingRoot := aiServerRoot
|
||||||
aiServerMu.Unlock()
|
aiServerMu.Unlock()
|
||||||
|
|
||||||
go startTrainingBestModelWatcher(appCtx, watchedTrainingRoot)
|
appGo("training-best-model-watcher", func() {
|
||||||
}()
|
startTrainingBestModelWatcher(appCtx, watchedTrainingRoot)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
go func() {
|
appGo("startup-notification", func() {
|
||||||
_ = showAppNotification(
|
_ = showAppNotification(
|
||||||
"Recorder gestartet",
|
"Recorder gestartet",
|
||||||
buildStartupNotificationMessage(),
|
buildStartupNotificationMessage(),
|
||||||
)
|
)
|
||||||
}()
|
})
|
||||||
|
|
||||||
go func() {
|
appGo("http-server-error-listener", func() {
|
||||||
err := <-serverErrCh
|
err := <-serverErrCh
|
||||||
if err != nil {
|
if err != nil {
|
||||||
appLogln("❌ HTTP-Server Fehler:", err)
|
appLogln("❌ HTTP-Server Fehler:", err)
|
||||||
shutdown()
|
shutdown()
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
runTray(appCtx, shutdown, buildTrayStats)
|
runTray(appCtx, shutdown, buildTrayStats)
|
||||||
shutdown()
|
shutdown()
|
||||||
|
|||||||
237
backend/tls_dynamic.go
Normal file
237
backend/tls_dynamic.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
cryptoRand "crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
localTLSOnce sync.Once
|
||||||
|
localTLSCertPath string
|
||||||
|
localTLSKeyPath string
|
||||||
|
localTLSErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
func localTLSPathsCached() (string, string, error) {
|
||||||
|
localTLSOnce.Do(func() {
|
||||||
|
localTLSCertPath, localTLSKeyPath, localTLSErr = ensureLocalTLSCertificate()
|
||||||
|
})
|
||||||
|
|
||||||
|
return localTLSCertPath, localTLSKeyPath, localTLSErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func localTLSDir() (string, error) {
|
||||||
|
if dir, err := os.UserConfigDir(); err == nil && strings.TrimSpace(dir) != "" {
|
||||||
|
return filepath.Join(dir, "nsfwapp", "tls"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil || strings.TrimSpace(home) == "" {
|
||||||
|
return "", fmt.Errorf("user config dir konnte nicht ermittelt werden")
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(home, ".config", "nsfwapp", "tls"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func localTLSHosts() []string {
|
||||||
|
seen := map[string]bool{}
|
||||||
|
out := []string{}
|
||||||
|
|
||||||
|
add := func(host string) {
|
||||||
|
host = normalizeOriginHost(host)
|
||||||
|
if host == "" || seen[host] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen[host] = true
|
||||||
|
out = append(out, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, origin := range configuredPublicAppOrigins() {
|
||||||
|
u, err := url.Parse(strings.TrimSpace(origin))
|
||||||
|
if err == nil {
|
||||||
|
add(u.Hostname())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range localAppHostCandidates() {
|
||||||
|
add(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
add("localhost")
|
||||||
|
add("127.0.0.1")
|
||||||
|
add("::1")
|
||||||
|
|
||||||
|
sort.Strings(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsCertificateCoversHosts(certPath string, hosts []string) bool {
|
||||||
|
cert, err := readLeafCertificate(certPath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
if now.Before(cert.NotBefore) || now.After(cert.NotAfter.Add(-30*24*time.Hour)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range hosts {
|
||||||
|
host = normalizeOriginHost(host)
|
||||||
|
if host == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := cert.VerifyHostname(host); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLeafCertificate(certPath string) (*x509.Certificate, error) {
|
||||||
|
b, err := os.ReadFile(certPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(b)
|
||||||
|
if block == nil || block.Type != "CERTIFICATE" {
|
||||||
|
return nil, fmt.Errorf("keine PEM-Zertifikatsdaten")
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509.ParseCertificate(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureLocalTLSCertificate() (string, string, error) {
|
||||||
|
dir, err := localTLSDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
certPath := filepath.Join(dir, "recorder-cert.pem")
|
||||||
|
keyPath := filepath.Join(dir, "recorder-key.pem")
|
||||||
|
hosts := localTLSHosts()
|
||||||
|
|
||||||
|
if fileExistsNonEmpty(certPath) &&
|
||||||
|
fileExistsNonEmpty(keyPath) &&
|
||||||
|
tlsCertificateCoversHosts(certPath, hosts) {
|
||||||
|
appLogln("🔐 TLS-Zertifikat aktiv:", certPath)
|
||||||
|
return certPath, keyPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := generateLocalTLSCertificate(certPath, keyPath, hosts); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
appLogln("🔐 TLS-Zertifikat neu erstellt:", certPath)
|
||||||
|
appLogln("🔐 TLS-Namen:", strings.Join(hosts, ", "))
|
||||||
|
return certPath, keyPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateLocalTLSCertificate(certPath string, keyPath string, hosts []string) error {
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), cryptoRand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serial, err := cryptoRand.Int(cryptoRand.Reader, serialLimit)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
notBefore := time.Now().Add(-1 * time.Hour)
|
||||||
|
notAfter := notBefore.Add(397 * 24 * time.Hour)
|
||||||
|
|
||||||
|
tpl := &x509.Certificate{
|
||||||
|
SerialNumber: serial,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "nsfwapp local",
|
||||||
|
Organization: []string{"nsfwapp"},
|
||||||
|
},
|
||||||
|
NotBefore: notBefore,
|
||||||
|
NotAfter: notAfter,
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
seenDNS := map[string]bool{}
|
||||||
|
seenIP := map[string]bool{}
|
||||||
|
for _, host := range hosts {
|
||||||
|
host = normalizeOriginHost(host)
|
||||||
|
if host == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
key := ip.String()
|
||||||
|
if !seenIP[key] {
|
||||||
|
seenIP[key] = true
|
||||||
|
tpl.IPAddresses = append(tpl.IPAddresses, ip)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !seenDNS[host] {
|
||||||
|
seenDNS[host] = true
|
||||||
|
tpl.DNSNames = append(tpl.DNSNames, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tpl.DNSNames) == 0 && len(tpl.IPAddresses) == 0 {
|
||||||
|
tpl.DNSNames = []string{"localhost"}
|
||||||
|
}
|
||||||
|
|
||||||
|
certDER, err := x509.CreateCertificate(
|
||||||
|
cryptoRand.Reader,
|
||||||
|
tpl,
|
||||||
|
tpl,
|
||||||
|
&key.PublicKey,
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyDER, err := x509.MarshalECPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
certPEM := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: certDER,
|
||||||
|
})
|
||||||
|
keyPEM := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "EC PRIVATE KEY",
|
||||||
|
Bytes: keyDER,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := os.WriteFile(certPath, certPEM, 0o600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Binary file not shown.
@ -1,26 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEbjCCAtagAwIBAgIRAKGvq9b44yCGC32q8PwkueowDQYJKoZIhvcNAQELBQAw
|
|
||||||
gYsxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEwMC4GA1UECwwnVEVH
|
|
||||||
RFNTRFxSb3RoZXJATDE0UEJCSzk1MTAwMDA2IChSb3RoZXIpMTcwNQYDVQQDDC5t
|
|
||||||
a2NlcnQgVEVHRFNTRFxSb3RoZXJATDE0UEJCSzk1MTAwMDA2IChSb3RoZXIpMB4X
|
|
||||||
DTI2MDUwODEwMDQzN1oXDTI4MDgwODEwMDQzN1owWzEnMCUGA1UEChMebWtjZXJ0
|
|
||||||
IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTAwLgYDVQQLDCdURUdEU1NEXFJvdGhl
|
|
||||||
ckBMMTRQQkJLOTUxMDAwMDYgKFJvdGhlcikwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
|
||||||
DwAwggEKAoIBAQDDghqU3igC3sNP2zAJTZbvZcT1xxjUgk3oA2j0WwNPO+xhCH4K
|
|
||||||
mJ6enU5zrGWI4+T0IHfbo737rvgk6XfTDSvzB9Uags8egBUGxbr9eMBya0hmnYGs
|
|
||||||
zIQGJeI4TIeQ7sa7krCpMB/e1YxR1qYFf1C8oSt43dJCdJP4Ev//BaR7rrFt3D3N
|
|
||||||
CC2OgxVYSh/hR9XNIIEPMoioQIYhle41mQWjyPFt16Fjg9sWrrnHSz7M4cgVVSot
|
|
||||||
+aBlRjbxgOarLyST3XdtjAfC6CqxnzprX1LfznuULjatoS+pFDBDoy2B13xwa9Br
|
|
||||||
f+Xy3CswmSO+AzNomWfbeBkJnl6xb3Tb1urrAgMBAAGjfDB6MA4GA1UdDwEB/wQE
|
|
||||||
AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBR0Zph7hFKq4GpE
|
|
||||||
XO0y3U+T0iFoQDAyBgNVHREEKzApgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAGHBAoAARkwDQYJKoZIhvcNAQELBQADggGBAJShhgQS+9cwuVOJUIW+
|
|
||||||
uy9t1ZAjR9qTd31kHdK1vu294GiavrG8RD+92BRujG4Fq1ambvqcu9sUaC8+0HAP
|
|
||||||
cQvdmnbTxG0ZHkGP7VVj3Poee0jpsVAVtV3aVwKxyO/E7Mv/nEKTqNfnECbEuiqC
|
|
||||||
tOJ8mI3E9tf2/ljzq2PqvfIPFBBtUT2lihNAFXM/06Nzoa1NPdfYd58N+VBFSOSq
|
|
||||||
zEM4fmpcqMThZGKGBoWmHwTE5Mw6mZFLyxHJpOFu5wNzyPJGes5o/F0ot5n+ZGah
|
|
||||||
GbYaGmhEedOxC3/WXRtexJmZ0jrB8Hkx+c/4h6+0HKU/PelrFJS+c2d6h8YGXyhv
|
|
||||||
pUsfyyoQzx7rSgyLfJqXK+52ZtnWNc0yVVMyKJwjvhv4mcP9l/q3UGR/i7xkD5Pc
|
|
||||||
2bLgM+wc8nKI7Yd/xejLpQDC0b/P1XibT1T3GRjiC/eu64G/uFOF+m3P5XvmF8JO
|
|
||||||
0cj8fXwUCq+TVD+22rFmtT+2VYkf8RUsC48Ou+PHccmDyw==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDDghqU3igC3sNP
|
|
||||||
2zAJTZbvZcT1xxjUgk3oA2j0WwNPO+xhCH4KmJ6enU5zrGWI4+T0IHfbo737rvgk
|
|
||||||
6XfTDSvzB9Uags8egBUGxbr9eMBya0hmnYGszIQGJeI4TIeQ7sa7krCpMB/e1YxR
|
|
||||||
1qYFf1C8oSt43dJCdJP4Ev//BaR7rrFt3D3NCC2OgxVYSh/hR9XNIIEPMoioQIYh
|
|
||||||
le41mQWjyPFt16Fjg9sWrrnHSz7M4cgVVSot+aBlRjbxgOarLyST3XdtjAfC6Cqx
|
|
||||||
nzprX1LfznuULjatoS+pFDBDoy2B13xwa9Brf+Xy3CswmSO+AzNomWfbeBkJnl6x
|
|
||||||
b3Tb1urrAgMBAAECggEBAI31EBv72w2KdkKroot+vROCz6quMAdNvgezQif7VcHY
|
|
||||||
fuBN7EcBXltJWUeAbBEjeIESejUPBcmT2DXlF841CC5lB4VCaeV5lsreE9IsNYBf
|
|
||||||
CakIwLmZnltgcovydZT063QTJRcUDHAems5pjw76zMLKO+h9GEiMoUxFb3/atv3e
|
|
||||||
K5HE+uUd6JcPU0q91S6TxqvS9gH30OOkwH6Kl4tzWauQAKg/sVzYafbyTAuEpiMO
|
|
||||||
jLLLAn/sOmiL4knnRVu1FK4aZSH+t9BN9YuwfucXFn9jFgPtIJ0bF1GuqnwIeDI5
|
|
||||||
BTNanHfaZRHD0+FbS0KbhARIFgDveoGrdoEtou0eDOECgYEA5dJqUvk96f7zCrxD
|
|
||||||
eSuUl1MvOjydY3GacfjevZoTxJDqh4Wt2wgLYJc4XB7tI02ci7NncecG8qPfqWq7
|
|
||||||
aFjQAGAYdR+XvE7R2m3OFK9WbRX4cH6b+l7mSq0eOvcAAajn4RuNr/il+7Opw7zX
|
|
||||||
eKUB/pnW36B+ethdr5l+kDUQb8MCgYEA2ccanIT2QAxJb5j0F287NGhaTPuWfxcX
|
|
||||||
2T53I2fIwMiell0YXjwsKwKpckubRB2f5X4b9QJifUTmtxHky/Tx4H9OVhCaxumw
|
|
||||||
edol5YNzZaP/Tx8m2mqpyN2WPbezIFeA+GNcmDAxEo/NT2B64OVCHWKosndvb+Yk
|
|
||||||
GFmVb+g9zbkCgYEA1YFJLZRHJJ+pgour02HdRUgOU/gD72KWrNMbeuEtBCvs9cIG
|
|
||||||
5bjveOiDf3FrtKRhjpc4vuR12+zJ2EZDnIkFk5OypPyYpmRDKL1h+m15yRXkG/5D
|
|
||||||
QbHwF+gEcZsN8nzMDqDeXGCPMuqSCDnjoz0IQVMB//bGCbIANyZOIgJqJqkCgYEA
|
|
||||||
vHc+ZG383fjEJLvtoco1JmmYnD6uQ1Ys4WjZmd5bMdtswxvV1tekMaSgF7WurQgm
|
|
||||||
NGkqsKJbsaVLNOtbYdac7He/x2OfTr02aH2Nhk54M2H1tPd0nFjqjlaVitvLPRX9
|
|
||||||
GviCTYKHNVUVjLgmHzLIQL382FXcLq6wVhJQ7QPDWKECgYA6ZmdB8waN2SFOJRnj
|
|
||||||
5zWTgdImicl4yCu8PaEyloO9whYSdYH8zU3K+FKaowXGNInp3BGCmP9TUwoG8Iwx
|
|
||||||
Ug5d55P2FgwnhCi59Y6dMXih1p94BuQ0vcWPBqztofl7lZbBEnjwzKVycFvcyNFX
|
|
||||||
QFb/UA4/CfQli9ciDtlqzZt8SA==
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
Loading…
x
Reference in New Issue
Block a user