Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions cloud-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# DataConnect Cloud Connector Runtime
# Extends n.eko chromium with our API server and connector automation.
#
# Build: docker build -f cloud-server/Dockerfile -t data-connect-cloud .
# Run: docker compose up

FROM ghcr.io/m1k1o/neko/chromium:3.0

# Install Node.js for our API server
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Layer-cached dependency install
COPY cloud-server/package.json cloud-server/package-lock.json* ./cloud-server/
COPY playwright-runner/package.json playwright-runner/package-lock.json* ./playwright-runner/
RUN cd cloud-server && npm ci --ignore-scripts
RUN cd playwright-runner && npm ci --ignore-scripts

# Source & build
COPY connectors/ ./connectors/
COPY cloud-server/ ./cloud-server/
COPY playwright-runner/ ./playwright-runner/
RUN cd cloud-server && npm run build

# Pre-built frontend
COPY dist/ ./dist/

# Supervisord configs for our services
COPY cloud-server/supervisord/*.conf /etc/neko/supervisord/

# Override Chromium config and launcher script
COPY cloud-server/neko/chromium.conf /etc/neko/supervisord/chromium.conf
COPY cloud-server/neko/start-chromium.sh /usr/local/bin/start-chromium.sh
RUN chmod +x /usr/local/bin/start-chromium.sh
COPY cloud-server/neko/policies.json /etc/chromium/policies/managed/policies.json
# Override xorg.conf to add portrait modes for mobile viewports
COPY cloud-server/neko/xorg.conf /etc/neko/xorg.conf

# Clean stale Chromium profile locks on startup (different container hostname)
RUN echo '#!/bin/sh\nrm -f /home/neko/.config/chromium/SingletonLock /home/neko/.config/chromium/SingletonSocket /home/neko/.config/chromium/SingletonCookie\nexec "$@"' > /usr/local/bin/cleanup-locks.sh && chmod +x /usr/local/bin/cleanup-locks.sh
ENTRYPOINT ["/usr/local/bin/cleanup-locks.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]

VOLUME ["/home/neko/.config/chromium"]
EXPOSE 3000
28 changes: 28 additions & 0 deletions cloud-server/deploy/docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
services:
data-connect-cloud:
image: gcr.io/corsali-development/data-connect-cloud
ports:
- "3000:3000"
- "8080:8080"
- "59000:59000/udp"
- "59000:59000/tcp"
volumes:
- profile-data:/data
shm_size: "2gb"
restart: unless-stopped
environment:
- NODE_ENV=production
- AUTH_TOKEN=${AUTH_TOKEN:-happyfriday}
- NEKO_SERVER_BIND=0.0.0.0:8080
- NEKO_LEGACY=true
- NEKO_WEBRTC_UDPMUX=59000
- NEKO_WEBRTC_TCPMUX=59000
- NEKO_WEBRTC_ICELITE=1
- NEKO_WEBRTC_NAT1TO1=${PUBLIC_IP}
- NEKO_MEMBER_PROVIDER=noauth
- NEKO_SESSION_IMPLICIT_HOSTING=true
- NEKO_IMPLICITCONTROL=true
- NEKO_DESKTOP_SCREEN=1280x720@30

volumes:
profile-data:
151 changes: 151 additions & 0 deletions cloud-server/deploy/gcp-deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/env bash
# Deploy data-connect-cloud to a GCP Compute Engine VM.
#
# Usage:
# ./cloud-server/deploy/gcp-deploy.sh [create|update|ssh|teardown]
#
# Prerequisites:
# - gcloud CLI authenticated
# - Docker image built locally or in GCR
#
# The script creates a VM, installs Docker, pushes the image to GCR,
# pulls it on the VM, and runs it with docker compose.

set -euo pipefail

PROJECT="corsali-development"
ZONE="us-central1-a"
VM_NAME="data-connect-cloud"
MACHINE_TYPE="e2-medium"
IMAGE_NAME="gcr.io/${PROJECT}/data-connect-cloud"
FIREWALL_TAG="data-connect-cloud"
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"

log() { echo "[$(date '+%H:%M:%S')] $*"; }

create_firewall_rules() {
log "Creating firewall rules..."

# API + n.eko HTTP + WebRTC
gcloud compute firewall-rules create allow-${FIREWALL_TAG} \
--project="$PROJECT" \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:3000,tcp:8080,tcp:59000,udp:59000 \
--target-tags="$FIREWALL_TAG" \
--source-ranges=0.0.0.0/0 \
--description="data-connect-cloud: API (3000), n.eko (8080), WebRTC (59000)" \
2>/dev/null || log "Firewall rule already exists, skipping."
}

create_vm() {
log "Creating VM ${VM_NAME}..."

gcloud compute instances create "$VM_NAME" \
--project="$PROJECT" \
--zone="$ZONE" \
--machine-type="$MACHINE_TYPE" \
--tags="$FIREWALL_TAG" \
--image-family=ubuntu-2404-lts-amd64 \
--image-project=ubuntu-os-cloud \
--boot-disk-size=30GB \
--scopes=storage-ro \
--metadata=startup-script='#!/bin/bash
if ! command -v docker &>/dev/null; then
curl -fsSL https://get.docker.com | sh
usermod -aG docker $(logname || echo ubuntu)
fi
'

log "Waiting for VM to be ready..."
sleep 30

# Wait for Docker to be installed
for i in $(seq 1 12); do
if gcloud compute ssh "$VM_NAME" --project="$PROJECT" --zone="$ZONE" \
--command="docker --version" 2>/dev/null; then
break
fi
log "Waiting for Docker install... (attempt $i/12)"
sleep 10
done

create_firewall_rules
log "VM created. External IP:"
gcloud compute instances describe "$VM_NAME" \
--project="$PROJECT" --zone="$ZONE" \
--format="get(networkInterfaces[0].accessConfigs[0].natIP)"
}

build_and_push() {
log "Building and pushing Docker image..."
cd "$REPO_ROOT"
docker build -f cloud-server/Dockerfile -t "$IMAGE_NAME" .
docker push "$IMAGE_NAME"
}

deploy_to_vm() {
local EXTERNAL_IP
EXTERNAL_IP=$(gcloud compute instances describe "$VM_NAME" \
--project="$PROJECT" --zone="$ZONE" \
--format="get(networkInterfaces[0].accessConfigs[0].natIP)")

log "Deploying to VM (IP: ${EXTERNAL_IP})..."

# Copy the production compose file
gcloud compute scp \
"${REPO_ROOT}/cloud-server/deploy/docker-compose.prod.yml" \
"${VM_NAME}:~/docker-compose.yml" \
--project="$PROJECT" --zone="$ZONE"

# Pull and run on the VM
gcloud compute ssh "$VM_NAME" --project="$PROJECT" --zone="$ZONE" --command="
sudo gcloud auth configure-docker gcr.io --quiet &&
sudo docker pull ${IMAGE_NAME} &&
sudo docker compose down 2>/dev/null || true
sudo sh -c 'export PUBLIC_IP=${EXTERNAL_IP} && docker compose up -d' &&
sleep 5 &&
sudo docker compose logs --tail=20
"

log "Deployed! Access at:"
log " API: http://${EXTERNAL_IP}:3000"
log " Neko: http://${EXTERNAL_IP}:8080"
}

do_ssh() {
gcloud compute ssh "$VM_NAME" --project="$PROJECT" --zone="$ZONE"
}

do_teardown() {
log "Tearing down VM ${VM_NAME}..."
gcloud compute instances delete "$VM_NAME" \
--project="$PROJECT" --zone="$ZONE" --quiet
log "VM deleted."
}

case "${1:-}" in
create)
create_vm
build_and_push
deploy_to_vm
;;
update)
build_and_push
deploy_to_vm
;;
ssh)
do_ssh
;;
teardown)
do_teardown
;;
*)
echo "Usage: $0 [create|update|ssh|teardown]"
echo " create - Create VM, build image, deploy"
echo " update - Rebuild image and redeploy"
echo " ssh - SSH into the VM"
echo " teardown - Delete the VM"
exit 1
;;
esac
22 changes: 22 additions & 0 deletions cloud-server/neko/chromium.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[program:chromium]
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
command=/usr/local/bin/start-chromium.sh
stopsignal=INT
autorestart=true
priority=800
user=%(ENV_USER)s
stdout_logfile=/var/log/neko/chromium.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=10
redirect_stderr=true

[program:openbox]
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
command=/usr/bin/openbox --config-file /etc/neko/openbox.xml
autorestart=true
priority=300
user=%(ENV_USER)s
stdout_logfile=/var/log/neko/openbox.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=10
redirect_stderr=true
29 changes: 29 additions & 0 deletions cloud-server/neko/policies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"AutofillAddressEnabled": false,
"AutofillCreditCardEnabled": false,
"BrowserSignin": 0,
"DefaultCookiesSetting": 1,
"DefaultNotificationsSetting": 2,
"DeveloperToolsAvailability": 1,
"EditBookmarksEnabled": false,
"FullscreenAllowed": true,
"IncognitoModeAvailability": 1,
"SyncDisabled": true,
"AutoplayAllowed": true,
"BrowserAddPersonEnabled": false,
"BrowserGuestModeEnabled": false,
"DefaultPopupsSetting": 2,
"VideoCaptureAllowed": true,
"AllowFileSelectionDialogs": false,
"PromptForDownloadLocation": false,
"BookmarkBarEnabled": false,
"PasswordManagerEnabled": false,
"BrowserLabsEnabled": false,
"URLAllowlist": [
"file:///home/neko/Downloads"
],
"URLBlocklist": [
"file://*",
"chrome://policy"
]
}
18 changes: 18 additions & 0 deletions cloud-server/neko/start-chromium.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh
exec /usr/bin/chromium \
--window-position=0,0 \
--display="${DISPLAY}" \
--user-data-dir=/home/neko/.config/chromium \
--no-first-run \
--no-sandbox \
--start-maximized \
--kiosk \
--force-dark-mode \
--disable-file-system \
--disable-gpu \
--disable-software-rasterizer \
--disable-dev-shm-usage \
--remote-debugging-port=9222 \
--remote-debugging-address=0.0.0.0 \
--remote-allow-origins=* \
${CHROMIUM_MOBILE_FLAGS}
Loading