Updates
This commit is contained in:
parent
58ef4eaaae
commit
f26227c7a8
@ -15,8 +15,6 @@ type Props = {
|
|||||||
export default function AdminHero({ title, ctas, description }: Props) {
|
export default function AdminHero({ title, ctas, description }: Props) {
|
||||||
const { pageProps } = useContext(AppContext);
|
const { pageProps } = useContext(AppContext);
|
||||||
|
|
||||||
console.log("pageProps.pageUrl", pageProps.pageUrl);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="w-full grid-cell-content justify-between">
|
<Row className="w-full grid-cell-content justify-between">
|
||||||
<Stack className="gap-2">
|
<Stack className="gap-2">
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
import Stack from "@/twui/components/layout/Stack";
|
||||||
|
import { useContext, useEffect, useRef } from "react";
|
||||||
|
import { AppContext } from "@/src/pages/_app";
|
||||||
|
import {
|
||||||
|
NormalizedServerObject,
|
||||||
|
ParsedDeploymentServiceConfig,
|
||||||
|
WebSocketDataType,
|
||||||
|
} from "@/src/types";
|
||||||
|
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
|
||||||
|
import Center from "@/twui/components/layout/Center";
|
||||||
|
import Loading from "@/twui/components/elements/Loading";
|
||||||
|
import useWebSocketEventHandler from "@/twui/components/hooks/useWebSocketEventHandler";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
service: ParsedDeploymentServiceConfig;
|
||||||
|
server: NormalizedServerObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ServiceClusterServerViews({ service, server }: Props) {
|
||||||
|
const { pageProps, ws } = useContext(AppContext);
|
||||||
|
|
||||||
|
const { data } = useWebSocketEventHandler<WebSocketDataType>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ws?.socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.sendData({
|
||||||
|
event: "client:ping",
|
||||||
|
server,
|
||||||
|
service,
|
||||||
|
});
|
||||||
|
}, [ws]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("data", data);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
return <Stack className="gap-0 w-full"></Stack>;
|
||||||
|
}
|
||||||
@ -1,10 +1,14 @@
|
|||||||
import Stack from "@/twui/components/layout/Stack";
|
import Stack from "@/twui/components/layout/Stack";
|
||||||
import { useContext } from "react";
|
import { useContext, useRef } from "react";
|
||||||
import { AppContext } from "@/src/pages/_app";
|
import { AppContext } from "@/src/pages/_app";
|
||||||
import {
|
import {
|
||||||
NormalizedServerObject,
|
NormalizedServerObject,
|
||||||
ParsedDeploymentServiceConfig,
|
ParsedDeploymentServiceConfig,
|
||||||
} from "@/src/types";
|
} from "@/src/types";
|
||||||
|
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
|
||||||
|
import Center from "@/twui/components/layout/Center";
|
||||||
|
import Loading from "@/twui/components/elements/Loading";
|
||||||
|
import ServiceClusterServerViews from "./cluster-server-views";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
service: ParsedDeploymentServiceConfig;
|
service: ParsedDeploymentServiceConfig;
|
||||||
@ -13,20 +17,23 @@ type Props = {
|
|||||||
|
|
||||||
export default function ServiceClusterServer({ service, server }: Props) {
|
export default function ServiceClusterServer({ service, server }: Props) {
|
||||||
const { pageProps } = useContext(AppContext);
|
const { pageProps } = useContext(AppContext);
|
||||||
const { deployment, children_services } = pageProps;
|
|
||||||
|
|
||||||
const all_services = [service, ...(children_services || [])];
|
const elementRef = useRef<HTMLDivElement>(undefined);
|
||||||
|
const { isIntersecting } = useIntersectionObserver({ elementRef });
|
||||||
const deployment_name = deployment?.deployment_name;
|
|
||||||
const service_name = service?.service_name;
|
|
||||||
|
|
||||||
const cluster_servers = service.servers;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="w-full grid-cell col-span-1">
|
<Stack className="grid-cell gap-0" componentRef={elementRef as any}>
|
||||||
{cluster_servers?.map((server, index) => {
|
<Stack className="grid-cell-content">
|
||||||
return null;
|
<code>{server.private_ip}</code>
|
||||||
})}
|
</Stack>
|
||||||
|
<hr />
|
||||||
|
{isIntersecting ? (
|
||||||
|
<ServiceClusterServerViews {...{ server, service }} />
|
||||||
|
) : (
|
||||||
|
<Center>
|
||||||
|
<Loading />
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { AppContext } from "@/src/pages/_app";
|
|||||||
import { ParsedDeploymentServiceConfig } from "@/src/types";
|
import { ParsedDeploymentServiceConfig } from "@/src/types";
|
||||||
import Row from "@/twui/components/layout/Row";
|
import Row from "@/twui/components/layout/Row";
|
||||||
import H2 from "@/twui/components/layout/H2";
|
import H2 from "@/twui/components/layout/H2";
|
||||||
|
import ServiceClusterServer from "./cluster-server";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
service: ParsedDeploymentServiceConfig;
|
service: ParsedDeploymentServiceConfig;
|
||||||
@ -12,25 +13,25 @@ type Props = {
|
|||||||
|
|
||||||
export default function ServiceCluster({ service, cluster_index }: Props) {
|
export default function ServiceCluster({ service, cluster_index }: Props) {
|
||||||
const { pageProps } = useContext(AppContext);
|
const { pageProps } = useContext(AppContext);
|
||||||
const { deployment, children_services } = pageProps;
|
|
||||||
|
|
||||||
const all_services = [service, ...(children_services || [])];
|
|
||||||
|
|
||||||
const deployment_name = deployment?.deployment_name;
|
|
||||||
const service_name = service?.service_name;
|
|
||||||
|
|
||||||
const cluster_servers = service.servers;
|
const cluster_servers = service.servers;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="w-full grid-cell col-span-1">
|
<Stack className="w-full grid-cell col-span-1 gap-0">
|
||||||
<Stack className="grid-cell-content">
|
<Stack className="grid-cell-content">
|
||||||
<H2>Cluster #{cluster_index}</H2>
|
<H2>Cluster #{cluster_index}</H2>
|
||||||
<Row className="">
|
|
||||||
{cluster_servers?.map((server, index) => {
|
|
||||||
return <code>{server.private_ip}</code>;
|
|
||||||
})}
|
|
||||||
</Row>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<hr />
|
||||||
|
<Row className="nested-grid-frame xl:grid-cols-2">
|
||||||
|
{cluster_servers?.map((server, index) => {
|
||||||
|
return (
|
||||||
|
<ServiceClusterServer
|
||||||
|
server={server}
|
||||||
|
service={service}
|
||||||
|
key={index}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export default function ServiceClusters() {
|
|||||||
const all_services = [service, ...(children_services || [])];
|
const all_services = [service, ...(children_services || [])];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="w-full nested-grid-frame grid-cols-1 xl:grid-cols-2">
|
<Stack className="w-full nested-grid-frame grid-cols-1">
|
||||||
{all_services.map((srv, index) => {
|
{all_services.map((srv, index) => {
|
||||||
if (!srv) return null;
|
if (!srv) return null;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -20,4 +20,6 @@ export const AppData = {
|
|||||||
AuthCookieName: `x-turboci-admin-auth-key`,
|
AuthCookieName: `x-turboci-admin-auth-key`,
|
||||||
AuthCSRFCookieName: `x-turboci-admin-csrf`,
|
AuthCSRFCookieName: `x-turboci-admin-csrf`,
|
||||||
CookieExpirationTime: 1000 * 60 * 60 * 24 * 7, // One Week
|
CookieExpirationTime: 1000 * 60 * 60 * 24 * 7, // One Week
|
||||||
|
|
||||||
|
DynamicPortStart: 4700,
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
38
src/nginx/conf.d/default.conf
Normal file
38
src/nginx/conf.d/default.conf
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 20M;
|
||||||
|
|
||||||
|
# Restrict port range to 4700 and above
|
||||||
|
location ^/ttyd/(?<port>(?:470[0-9]|47[1-9]\d|4[89]\d{2}|[5-9]\d{3}|[1-9]\d{4,}))(/.*)? {
|
||||||
|
proxy_pass http://localhost:$port$2;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /ws {
|
||||||
|
proxy_pass http://localhost:3773;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
keepalive_timeout 75s;
|
||||||
|
|
||||||
|
tcp_nodelay on;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:3772;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/nginx/default-nginx.conf
Normal file
83
src/nginx/default-nginx.conf
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
user www-data;
|
||||||
|
worker_processes auto;
|
||||||
|
pid /run/nginx.pid;
|
||||||
|
include /etc/nginx/modules-enabled/*.conf;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 768;
|
||||||
|
# multi_accept on;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
|
||||||
|
##
|
||||||
|
# Basic Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
# server_tokens off;
|
||||||
|
|
||||||
|
# server_names_hash_bucket_size 64;
|
||||||
|
# server_name_in_redirect off;
|
||||||
|
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
##
|
||||||
|
# SSL Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Logging Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log;
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Gzip Settings
|
||||||
|
##
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
|
||||||
|
# gzip_vary on;
|
||||||
|
# gzip_proxied any;
|
||||||
|
# gzip_comp_level 6;
|
||||||
|
# gzip_buffers 16 8k;
|
||||||
|
# gzip_http_version 1.1;
|
||||||
|
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
|
||||||
|
##
|
||||||
|
# Virtual Host Configs
|
||||||
|
##
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
include /etc/nginx/sites-enabled/*;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#mail {
|
||||||
|
# # See sample authentication script at:
|
||||||
|
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
|
||||||
|
#
|
||||||
|
# # auth_http localhost/auth.php;
|
||||||
|
# # pop3_capabilities "TOP" "USER";
|
||||||
|
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
|
||||||
|
#
|
||||||
|
# server {
|
||||||
|
# listen localhost:110;
|
||||||
|
# protocol pop3;
|
||||||
|
# proxy on;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# server {
|
||||||
|
# listen localhost:143;
|
||||||
|
# protocol imap;
|
||||||
|
# proxy on;
|
||||||
|
# }
|
||||||
|
#}
|
||||||
91
src/nginx/default-server.conf
Normal file
91
src/nginx/default-server.conf
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
##
|
||||||
|
# You should look at the following URL's in order to grasp a solid understanding
|
||||||
|
# of Nginx configuration files in order to fully unleash the power of Nginx.
|
||||||
|
# https://www.nginx.com/resources/wiki/start/
|
||||||
|
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
|
||||||
|
# https://wiki.debian.org/Nginx/DirectoryStructure
|
||||||
|
#
|
||||||
|
# In most cases, administrators will remove this file from sites-enabled/ and
|
||||||
|
# leave it as reference inside of sites-available where it will continue to be
|
||||||
|
# updated by the nginx packaging team.
|
||||||
|
#
|
||||||
|
# This file will automatically load configuration files provided by other
|
||||||
|
# applications, such as Drupal or Wordpress. These applications will be made
|
||||||
|
# available underneath a path with that package name, such as /drupal8.
|
||||||
|
#
|
||||||
|
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
|
||||||
|
##
|
||||||
|
|
||||||
|
# Default server configuration
|
||||||
|
#
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
#
|
||||||
|
# listen 443 ssl default_server;
|
||||||
|
# listen [::]:443 ssl default_server;
|
||||||
|
#
|
||||||
|
# Note: You should disable gzip for SSL traffic.
|
||||||
|
# See: https://bugs.debian.org/773332
|
||||||
|
#
|
||||||
|
# Read up on ssl_ciphers to ensure a secure configuration.
|
||||||
|
# See: https://bugs.debian.org/765782
|
||||||
|
#
|
||||||
|
# Self signed certs generated by the ssl-cert package
|
||||||
|
# Don't use them in a production server!
|
||||||
|
#
|
||||||
|
# include snippets/snakeoil.conf;
|
||||||
|
|
||||||
|
root /var/www/html;
|
||||||
|
|
||||||
|
# Add index.php to the list if you are using PHP
|
||||||
|
index index.html index.htm index.nginx-debian.html;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# First attempt to serve request as file, then
|
||||||
|
# as directory, then fall back to displaying a 404.
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# pass PHP scripts to FastCGI server
|
||||||
|
#
|
||||||
|
#location ~ \.php$ {
|
||||||
|
# include snippets/fastcgi-php.conf;
|
||||||
|
#
|
||||||
|
# # With php-fpm (or other unix sockets):
|
||||||
|
# fastcgi_pass unix:/run/php/php7.4-fpm.sock;
|
||||||
|
# # With php-cgi (or other tcp sockets):
|
||||||
|
# fastcgi_pass 127.0.0.1:9000;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# deny access to .htaccess files, if Apache's document root
|
||||||
|
# concurs with nginx's one
|
||||||
|
#
|
||||||
|
#location ~ /\.ht {
|
||||||
|
# deny all;
|
||||||
|
#}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Virtual Host configuration for example.com
|
||||||
|
#
|
||||||
|
# You can move that to a different file under sites-available/ and symlink that
|
||||||
|
# to sites-enabled/ to enable it.
|
||||||
|
#
|
||||||
|
#server {
|
||||||
|
# listen 80;
|
||||||
|
# listen [::]:80;
|
||||||
|
#
|
||||||
|
# server_name example.com;
|
||||||
|
#
|
||||||
|
# root /var/www/example.com;
|
||||||
|
# index index.html;
|
||||||
|
#
|
||||||
|
# location / {
|
||||||
|
# try_files $uri $uri/ =404;
|
||||||
|
# }
|
||||||
|
#}
|
||||||
34
src/nginx/nginx.conf
Normal file
34
src/nginx/nginx.conf
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
user www-data;
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
error_log /var/log/nginx/error.log notice;
|
||||||
|
pid /run/nginx.pid;
|
||||||
|
include /etc/nginx/modules-enabled/*.conf;
|
||||||
|
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
||||||
@ -115,3 +115,7 @@ code {
|
|||||||
#admin-main h2 {
|
#admin-main h2 {
|
||||||
@apply text-xl font-semibold;
|
@apply text-xl font-semibold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
@apply border-foreground-light/10 dark:border-foreground-dark/10;
|
||||||
|
}
|
||||||
|
|||||||
@ -231,6 +231,8 @@ export const WebSocketEvents = [
|
|||||||
export type WebSocketDataType = {
|
export type WebSocketDataType = {
|
||||||
event: (typeof WebSocketEvents)[number];
|
event: (typeof WebSocketEvents)[number];
|
||||||
message?: string;
|
message?: string;
|
||||||
|
service?: ParsedDeploymentServiceConfig;
|
||||||
|
server?: NormalizedServerObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WebSocketType = {
|
export type WebSocketType = {
|
||||||
|
|||||||
44
src/utils/grab-next-available-port.ts
Normal file
44
src/utils/grab-next-available-port.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { dlopen, ptr, FFIType } from "bun:ffi";
|
||||||
|
import { AppData } from "../data/app-data";
|
||||||
|
|
||||||
|
const libc = dlopen("libc.so.6", {
|
||||||
|
socket: {
|
||||||
|
args: [FFIType.i32, FFIType.i32, FFIType.i32],
|
||||||
|
returns: FFIType.i32,
|
||||||
|
},
|
||||||
|
bind: {
|
||||||
|
args: [FFIType.i32, FFIType.ptr, FFIType.i32],
|
||||||
|
returns: FFIType.i32,
|
||||||
|
},
|
||||||
|
close: { args: [FFIType.i32], returns: FFIType.i32 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const AF_INET = 2;
|
||||||
|
const SOCK_STREAM = 1;
|
||||||
|
const INADDR_ANY = 0;
|
||||||
|
|
||||||
|
function makeSockaddr(port: number): Buffer {
|
||||||
|
const buf = Buffer.alloc(16);
|
||||||
|
buf.writeUInt16LE(AF_INET, 0);
|
||||||
|
buf.writeUInt16BE(port, 2);
|
||||||
|
buf.writeUInt32BE(INADDR_ANY, 4);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getNextAvailablePort(
|
||||||
|
startPort = AppData["DynamicPortStart"],
|
||||||
|
maxPort = 65535,
|
||||||
|
): number {
|
||||||
|
for (let port = startPort; port <= maxPort; port++) {
|
||||||
|
const fd = libc.symbols.socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) continue;
|
||||||
|
|
||||||
|
const addr = makeSockaddr(port);
|
||||||
|
const result = libc.symbols.bind(fd, ptr(addr), addr.byteLength);
|
||||||
|
libc.symbols.close(fd);
|
||||||
|
|
||||||
|
if (result === 0) return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No available port found in range ${startPort}-${maxPort}`);
|
||||||
|
}
|
||||||
2
twui
2
twui
@ -1 +1 @@
|
|||||||
Subproject commit 9f8527fc4d851c1fecd6600bd60c490de998676f
|
Subproject commit 2c101420ccc7dc1c56eb7bb7c41f2cca83af334a
|
||||||
Loading…
Reference in New Issue
Block a user