Skip to content
Merged
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
102 changes: 102 additions & 0 deletions QUICKSTART-DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Quickstart Guide

This guide will help you get the Unkey deployment platform up and running locally for development and testing.

## Prerequisites

- Docker and Docker Compose
- A terminal/command line

## Step 1: Start the Platform

1. Start all services using Docker Compose:

```bash
docker-compose up -d
```

This will start:

- MySQL database (port 3306)
- Dashboard (port 3000)
- Control plane services
- Supporting infrastructure

2. Wait for all services to be healthy (this may take 1-2 minutes):

```bash
docker-compose ps
```

## Step 2: Set Up Your Workspace

1. Open your browser and navigate to the dashboard:

```
http://localhost:3000
```

2. Sign in or create an account through the authentication flow

3. Once logged in, you'll automatically have a workspace created. Navigate to:

```
http://localhost:3000/projects
```

4. Create a new project by filling out the form:

- **Name**: Choose any name (e.g., "My Test App")
- **Slug**: This will auto-generate based on the name
- **Git URL**: Optional, leave blank for testing

5. After creating the project, **copy the Project ID** from the project details. It will look like:

```
proj_xxxxxxxxxxxxxxxxxx
```

6. Also note your **Workspace ID** (you can find this settings). It will look like:

```
ws_xxxxxxxxxxxxxxxxxx
```

## Step 3: Deploy a Version

1. Navigate to the go directory:

```bash
cd go
```

2. Create a version using the CLI with your copied IDs:

```bash
go run . version create \
--context=./demo_api \
--workspace-id=YOUR_WORKSPACE_ID \
--project-id=YOUR_PROJECT_ID
```

Keep the context as shown, there's a demo api in that folder.
Replace `YOUR_WORKSPACE_ID` and `YOUR_PROJECT_ID` with the actual values you copied from the dashboard.

3. The CLI will show real-time progress as your deployment goes through these stages:
- Downloading Docker image
- Building rootfs
- Uploading rootfs
- Creating VM
- Booting VM
- Assigning domains
- Completed

## Step 4: View Your Deployment

1. Return to the dashboard and navigate to:

```
http://localhost:3000/versions
http://localhost:3000/deployments
```

24 changes: 24 additions & 0 deletions apps/dashboard/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM node:lts

WORKDIR /unkey

# Install pnpm
RUN npm install -g pnpm

# Copy everything
COPY . .

# Install dependencies
RUN pnpm install -r

# Move to dashboard directory
WORKDIR /unkey/apps/dashboard
RUN pnpm build
EXPOSE 3000

# Set hostname to 0.0.0.0 to allow external connections
ENV HOSTNAME="0.0.0.0"
ENV PORT=3000

# Run in development mode for now
CMD ["pnpm","start", "--hostname", "0.0.0.0"]
59 changes: 59 additions & 0 deletions apps/dashboard/app/(app)/projects/[projectId]/branches/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use client";

import { trpc } from "@/lib/trpc/client";
import { useParams } from "next/navigation";

export default function ProjectBranchesPage() {
const params = useParams();
const projectId = params?.projectId as string;

const { data, isLoading, error } = trpc.project.branches.useQuery(
{
projectId,
},
{
enabled: !!projectId, // Only run query if projectId exists
},
);

if (!projectId) {
return (
<div style={{ padding: "20px" }}>
<p>Invalid project ID</p>
</div>
);
}

if (isLoading) {
return (
<div style={{ padding: "20px" }}>
<p>Loading...</p>
</div>
);
}

if (error) {
return (
<div style={{ padding: "20px" }}>
<h1>Error</h1>
<p style={{ color: "red" }}>Failed to load branches: {error.message}</p>
</div>
);
}

return (
<div style={{ padding: "20px" }}>
<h1>Branches for {data?.project.name}</h1>
<p>
Project: {data?.project.slug} ({data?.project.id})
</p>

<h2>All Branches</h2>
{data?.branches && data.branches.length > 0 ? (
<pre>{JSON.stringify(data.branches, null, 2)}</pre>
) : (
<p>No branches found for this project.</p>
)}
</div>
);
}
137 changes: 137 additions & 0 deletions apps/dashboard/app/(app)/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"use client";

import { trpc } from "@/lib/trpc/client";
import { useState } from "react";

export default function ProjectsPage() {
const [name, setName] = useState("");
const [slug, setSlug] = useState("");
const [gitUrl, setGitUrl] = useState("");

const { data, isLoading, refetch } = trpc.project.list.useQuery();
const createProject = trpc.project.create.useMutation({
onSuccess: () => {
refetch();
setName("");
setSlug("");
setGitUrl("");
},
});

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!name || !slug) {
return;
}

createProject.mutate({
name,
slug,
gitRepositoryUrl: gitUrl || undefined,
});
};

return (
<div style={{ padding: "20px" }}>
<h1>Projects</h1>

<h2>Existing Projects</h2>
{isLoading ? (
<p>Loading...</p>
) : data?.projects && data.projects.length > 0 ? (
<div>
{data.projects.map((project) => (
<div
key={project.id}
style={{
marginBottom: "20px",
padding: "15px",
border: "1px solid #ddd",
borderRadius: "4px",
}}
>
<h3>{project.name}</h3>
<p>Slug: {project.slug}</p>
<p>ID: {project.id}</p>
{project.gitRepositoryUrl && <p>Git: {project.gitRepositoryUrl}</p>}
<p>Created: {new Date(project.createdAt).toLocaleString()}</p>
<a
href={`/projects/${project.id}/branches`}
style={{ color: "#0070f3", textDecoration: "underline" }}
>
View Branches →
</a>
</div>
))}
</div>
) : (
<p>No projects found.</p>
)}

<h2>Create New Project</h2>
<form onSubmit={handleSubmit} style={{ marginTop: "20px" }}>
<div style={{ marginBottom: "10px" }}>
<label>
Name:
<input
type="text"
value={name}
onChange={(e) => {
setName(e.target.value);
// Auto-generate slug
const slug = e.target.value
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
setSlug(slug);
}}
style={{ marginLeft: "10px", padding: "5px" }}
/>
</label>
</div>

<div style={{ marginBottom: "10px" }}>
<label>
Slug:
<input
type="text"
value={slug}
onChange={(e) => setSlug(e.target.value)}
style={{ marginLeft: "10px", padding: "5px" }}
/>
</label>
</div>

<div style={{ marginBottom: "10px" }}>
<label>
Git URL (optional):
<input
type="text"
value={gitUrl}
onChange={(e) => setGitUrl(e.target.value)}
style={{ marginLeft: "10px", padding: "5px" }}
/>
</label>
</div>

<button
type="submit"
disabled={!name || !slug || createProject.isLoading}
style={{
padding: "10px 20px",
backgroundColor: "#0070f3",
color: "white",
border: "none",
borderRadius: "4px",
}}
>
{createProject.isLoading ? "Creating..." : "Create Project"}
</button>
</form>

{createProject.error && <p style={{ color: "red" }}>Error: {createProject.error.message}</p>}
</div>
);
}
Loading
Loading