# retoor <retoor@molodetz.nl>

from .._shared import endpoint, field

GROUP = {
    "slug": "workspaces",
    "title": "Dev Workspaces",
    "intro": """
# Dev Workspaces

A workspace is a browser editor attached to one of your projects. It runs your project files, a
terminal, and preinstalled Python, Rust, Nim and Swift toolchains. `sudo` and `apt install` work
with no extra setup; ports below 1024 cannot bind, so use a high port and publish it through a
tunnel.

The editor opens with a **DevPlace Code** terminal already running the `dpc` coding agent and a
plain shell beside it, and it trusts every folder, so nothing opens in Restricted Mode. Its
appearance and boot behaviour are your own preferences, readable and writable through the two
`/workspace/editor` endpoints below and explained on
[the workspace editor page](/docs/workspace-editor.html). Editor preferences apply on the next
workspace start.

A **tunnel** publishes one port from inside your container on a public HTTPS hostname of the form
`<port>-<name>.tunnel.pravda.education`. **Tunnel URLs are public and unauthenticated** - anyone with
the link can reach whatever you are serving. Forwarding a port in the editor's **Ports** view creates
the tunnel for you through the same endpoint; un-forwarding it does not remove the tunnel.

Workspaces are bounded: a count limit per user, a disk quota, an egress quota, and a tunnel limit.
An idle workspace is warned about, then stopped, then warned again, then removed. Every warning
arrives as a `workspace` notification and states exactly what happens next and when.
""",
    "endpoints": [
        endpoint(
            id="workspace-get",
            method="GET",
            path="/projects/{slug}/workspace",
            title="Read workspace",
            summary=(
                "State, quota usage, idle countdown, tunnels and open moderation flags "
                "for your workspace on this project. The phase (stopped, starting, ready, "
                "stopping, crashed, suspended) is derived from the desired state, the "
                "container status and a live probe of the editor port, so editor_ready "
                "is true only when the editor will actually open."
            ),
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
            ],
            sample_response={
                "has_workspace": True,
                "viewer_can_workspace": True,
                "workspace_count": 1,
                "max_workspaces": 2,
                "editor_url": "/projects/my-project/containers/instances/INSTANCE_UID/code/",
                "workspace": {
                    "uid": "INSTANCE_UID",
                    "owner_uid": "USER_UID",
                    "status": "running",
                    "desired_state": "running",
                    "phase": "ready",
                    "phase_label": "Ready",
                    "editor_ready": True,
                    "suspended": False,
                    "tunnel_name": "brave-otter",
                    "primary_url": "https://brave-otter.tunnel.pravda.education",
                    "disk_bytes": 5242880,
                    "disk_quota_mb": 2048,
                    "disk_percent": 1,
                    "egress_bytes": 10240,
                    "egress_quota_mb": 10240,
                    "egress_percent": 0,
                    "idle_stop_minutes": 60,
                    "retention_days": 14,
                    "max_tunnels": 5,
                    "tunnels": [],
                    "flags": [],
                },
            },
        ),
        endpoint(
            id="workspace-open",
            method="POST",
            path="/projects/{slug}/workspace",
            title="Open or resume workspace",
            summary=(
                "Create the workspace if you have none for this project, otherwise resume "
                "it. Idempotent. Refused when you are at your workspace limit, over disk "
                "quota, or suspended."
            ),
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
            ],
            sample_response={"ok": True, "redirect": "/projects/my-project/workspace"},
        ),
        endpoint(
            id="workspace-stop",
            method="POST",
            path="/projects/{slug}/workspace/stop",
            title="Stop workspace",
            summary="Stop the container. Files and tunnels are kept.",
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
            ],
            sample_response={"ok": True, "redirect": "/projects/my-project/workspace"},
        ),
        endpoint(
            id="workspace-delete",
            method="POST",
            path="/projects/{slug}/workspace/delete",
            title="Delete workspace",
            summary="Remove the workspace and its tunnels. An administrator can restore it.",
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
            ],
            sample_response={"ok": True, "redirect": "/projects/my-project/workspace"},
        ),
        endpoint(
            id="workspace-editor-get",
            method="GET",
            path="/projects/{slug}/workspace/editor",
            title="Read editor profile",
            summary=(
                "The resolved DevPlace editor profile for this workspace: theme, layout, "
                "panel preset, font sizes, zoom, boot terminals, how the editor opens, the "
                "container size, and where each value comes from."
            ),
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
            ],
            sample_response={
                "editor": {
                    "trust_all": True,
                    "theme": "devplace-dark",
                    "font_size": 14,
                    "terminal_font_size": 13,
                    "zoom_level": 0,
                    "layout": "standard",
                    "panel_preset": "normal",
                    "boot_agent": "dpc",
                    "boot_shell": True,
                    "window_mode": "tab",
                    "window_width": 1600,
                    "window_height": 1000,
                    "cpu_millicores": 2000,
                    "cpu_cores": 2.0,
                    "memory_mb": 2048,
                    "disk_quota_mb": 2048,
                    "sources": {"theme": "user", "font_size": "site"},
                },
                "restart_required": False,
            },
        ),
        endpoint(
            id="workspace-editor-set",
            method="POST",
            path="/projects/{slug}/workspace/editor",
            title="Set editor preferences",
            summary=(
                "Change your own editor preferences. Only the fields you send are "
                "changed; within those, an empty string or zero means inherit the site "
                "default, and `reset` drops every preference. Applies on the next "
                "workspace start, and the response says whether a restart is needed."
            ),
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
                field("theme", "body", "string", False, "devplace-dark",
                      "devplace-dark, devplace-light or system."),
                field("layout", "body", "string", False, "standard",
                      "standard, terminal-focus or zen."),
                field("panel_preset", "body", "string", False, "tall",
                      "short, normal, tall or maximized."),
                field("font_size", "body", "integer", False, "14",
                      "Editor font size in pixels. Zero inherits."),
                field("terminal_font_size", "body", "integer", False, "13",
                      "Terminal font size in pixels. Zero inherits."),
                field("zoom_level", "body", "integer", False, "0",
                      "Window zoom, -5 to 5. Send -99 to inherit."),
                field("boot_agent", "body", "string", False, "dpc",
                      "dpc or none."),
                field("boot_shell", "body", "integer", False, "1",
                      "1 opens a shell on boot, 0 skips it, -1 inherits."),
                field("window_mode", "body", "string", False, "tab",
                      "tab, window or fullscreen."),
                field("window_width", "body", "integer", False, "1600",
                      "Editor window width in pixels. Zero inherits."),
                field("window_height", "body", "integer", False, "1000",
                      "Editor window height in pixels. Zero inherits."),
                field("reset", "body", "boolean", False, "false",
                      "Drop every preference and fall back to the site defaults."),
            ],
            sample_response={
                "ok": True,
                "redirect": "/projects/my-project/workspace",
                "data": {"restart_required": True},
            },
        ),
        endpoint(
            id="workspace-tunnels-list",
            method="GET",
            path="/projects/{slug}/workspace/tunnels",
            title="List tunnels",
            summary="Every public tunnel published by this workspace.",
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
            ],
            sample_response={
                "tunnels": [
                    {
                        "uid": "TUNNEL_UID",
                        "hostname": "8080-brave-otter.tunnel.pravda.education",
                        "label": "web",
                        "container_port": 8080,
                        "status": "active",
                        "cert_status": "valid",
                        "request_count": 12,
                        "bytes_out": 40960,
                    }
                ]
            },
        ),
        endpoint(
            id="workspace-tunnel-create",
            method="POST",
            path="/projects/{slug}/workspace/tunnels",
            title="Create tunnel",
            summary=(
                "Publish a container port on a public HTTPS hostname. The URL is public "
                "and unauthenticated. Refused past the tunnel limit. The certificate is "
                "ordered right away, so the hostname answers plain HTTP for a few seconds "
                "before it serves HTTPS. Forwarding a port in the editor calls this for you."
            ),
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
                field("container_port", "body", "integer", True, 8080, "Port inside the container."),
                field("label", "body", "string", False, "web", "Human label."),
            ],
            sample_response={
                "ok": True,
                "data": {
                    "uid": "TUNNEL_UID",
                    "hostname": "8080-brave-otter.tunnel.pravda.education",
                    "status": "pending",
                },
            },
        ),
        endpoint(
            id="workspace-tunnel-delete",
            method="POST",
            path="/projects/{slug}/workspace/tunnels/{uid}/delete",
            title="Delete tunnel",
            summary="Remove a tunnel. The public URL stops serving immediately.",
            auth="user",
            params=[
                field("slug", "path", "string", True, "my-project", "Project slug or uid."),
                field("uid", "path", "string", True, "TUNNEL_UID", "Tunnel uid."),
            ],
            sample_response={"ok": True, "redirect": "/projects/my-project/workspace"},
        ),
    ],
}
