feat: correctly show whether a project has been deployed
This commit is contained in:
parent
1630a5a9cd
commit
a59246b1ef
@ -31,9 +31,30 @@ export class DokkuClient extends SSHSocketClient {
|
|||||||
|
|
||||||
// List all deployed Dokku apps
|
// List all deployed Dokku apps
|
||||||
async listApps(): Promise<string[]> {
|
async listApps(): Promise<string[]> {
|
||||||
const response = await this.sendCommand("apps:list")
|
const response = await this.sendCommand("--quiet apps:list")
|
||||||
// Split the output by newline and remove the header
|
return response.output.split("\n")
|
||||||
return response.output.split("\n").slice(1)
|
}
|
||||||
|
|
||||||
|
// Get the creation timestamp of an app
|
||||||
|
async getAppCreatedAt(appName: string): Promise<number> {
|
||||||
|
const response = await this.sendCommand(
|
||||||
|
`apps:report --app-created-at ${appName}`
|
||||||
|
)
|
||||||
|
const createdAt = parseInt(response.output.trim(), 10)
|
||||||
|
|
||||||
|
if (isNaN(createdAt)) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to retrieve creation timestamp for app ${appName}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if an app exists
|
||||||
|
async appExists(appName: string): Promise<boolean> {
|
||||||
|
const response = await this.sendCommand(`apps:exists ${appName}`)
|
||||||
|
return response.output.includes("App") === false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +156,28 @@ export class Sandbox {
|
|||||||
return { success: true, apps: await this.dokkuClient.listApps() }
|
return { success: true, apps: await this.dokkuClient.listApps() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle getting app creation timestamp
|
||||||
|
const handleGetAppCreatedAt: SocketHandler = async ({ appName }) => {
|
||||||
|
if (!this.dokkuClient)
|
||||||
|
throw new Error(
|
||||||
|
"Failed to retrieve app creation timestamp: No Dokku client"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
createdAt: await this.dokkuClient.getAppCreatedAt(appName),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle checking if an app exists
|
||||||
|
const handleAppExists: SocketHandler = async ({ appName }) => {
|
||||||
|
if (!this.dokkuClient)
|
||||||
|
throw new Error("Failed to check app existence: No Dokku client")
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
exists: await this.dokkuClient.appExists(appName),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle deploying code
|
// Handle deploying code
|
||||||
const handleDeploy: SocketHandler = async (_: any) => {
|
const handleDeploy: SocketHandler = async (_: any) => {
|
||||||
if (!this.gitClient) throw Error("No git client")
|
if (!this.gitClient) throw Error("No git client")
|
||||||
@ -253,7 +275,9 @@ export class Sandbox {
|
|||||||
getFolder: handleGetFolder,
|
getFolder: handleGetFolder,
|
||||||
saveFile: handleSaveFile,
|
saveFile: handleSaveFile,
|
||||||
moveFile: handleMoveFile,
|
moveFile: handleMoveFile,
|
||||||
list: handleListApps,
|
listApps: handleListApps,
|
||||||
|
getAppCreatedAt: handleGetAppCreatedAt,
|
||||||
|
getAppExists: handleAppExists,
|
||||||
deploy: handleDeploy,
|
deploy: handleDeploy,
|
||||||
createFile: handleCreateFile,
|
createFile: handleCreateFile,
|
||||||
createFolder: handleCreateFolder,
|
createFolder: handleCreateFolder,
|
||||||
|
@ -146,6 +146,8 @@ io.on("connection", async (socket) => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.emit("ready")
|
||||||
|
|
||||||
// Handle disconnection event
|
// Handle disconnection event
|
||||||
socket.on("disconnect", async () => {
|
socket.on("disconnect", async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
import { useTerminal } from "@/context/TerminalContext"
|
import { useTerminal } from "@/context/TerminalContext"
|
||||||
import { Sandbox, User } from "@/lib/types"
|
import { Sandbox, User } from "@/lib/types"
|
||||||
import { Globe } from "lucide-react"
|
import { Globe } from "lucide-react"
|
||||||
import { useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
export default function DeployButtonModal({
|
export default function DeployButtonModal({
|
||||||
userData,
|
userData,
|
||||||
@ -18,8 +18,19 @@ export default function DeployButtonModal({
|
|||||||
userData: User
|
userData: User
|
||||||
data: Sandbox
|
data: Sandbox
|
||||||
}) {
|
}) {
|
||||||
const { deploy } = useTerminal()
|
const { deploy, getAppExists } = useTerminal()
|
||||||
const [isDeploying, setIsDeploying] = useState(false)
|
const [isDeploying, setIsDeploying] = useState(false)
|
||||||
|
const [isDeployed, setIsDeployed] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkDeployment = async () => {
|
||||||
|
if (getAppExists) {
|
||||||
|
const exists = await getAppExists(data.id)
|
||||||
|
setIsDeployed(exists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkDeployment()
|
||||||
|
}, [data.id, getAppExists])
|
||||||
|
|
||||||
const handleDeploy = () => {
|
const handleDeploy = () => {
|
||||||
if (isDeploying) {
|
if (isDeploying) {
|
||||||
@ -30,6 +41,7 @@ export default function DeployButtonModal({
|
|||||||
setIsDeploying(true)
|
setIsDeploying(true)
|
||||||
deploy(() => {
|
deploy(() => {
|
||||||
setIsDeploying(false)
|
setIsDeploying(false)
|
||||||
|
setIsDeployed(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,8 +64,9 @@ export default function DeployButtonModal({
|
|||||||
<DeploymentOption
|
<DeploymentOption
|
||||||
icon={<Globe className="text-gray-500 w-5 h-5" />}
|
icon={<Globe className="text-gray-500 w-5 h-5" />}
|
||||||
domain={`${data.id}.gitwit.app`}
|
domain={`${data.id}.gitwit.app`}
|
||||||
timestamp="Deployed 1h ago"
|
timestamp="Deployed 1m ago"
|
||||||
user={userData.name}
|
user={userData.name}
|
||||||
|
isDeployed={isDeployed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
@ -61,7 +74,7 @@ export default function DeployButtonModal({
|
|||||||
className="mt-4 w-full bg-[#0a0a0a] text-white hover:bg-[#262626]"
|
className="mt-4 w-full bg-[#0a0a0a] text-white hover:bg-[#262626]"
|
||||||
onClick={handleDeploy}
|
onClick={handleDeploy}
|
||||||
>
|
>
|
||||||
{isDeploying ? "Deploying..." : "Update"}
|
{isDeploying ? "Deploying..." : isDeployed ? "Update" : "Deploy"}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
@ -74,16 +87,19 @@ function DeploymentOption({
|
|||||||
domain,
|
domain,
|
||||||
timestamp,
|
timestamp,
|
||||||
user,
|
user,
|
||||||
|
isDeployed,
|
||||||
}: {
|
}: {
|
||||||
icon: React.ReactNode
|
icon: React.ReactNode
|
||||||
domain: string
|
domain: string
|
||||||
timestamp: string
|
timestamp: string
|
||||||
user: string
|
user: string
|
||||||
|
isDeployed: boolean
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2 w-full text-left p-2 rounded-md border border-gray-700 bg-gray-900">
|
<div className="flex flex-col gap-2 w-full text-left p-2 rounded-md border border-gray-700 bg-gray-900">
|
||||||
<div className="flex items-start gap-2 relative">
|
<div className="flex items-start gap-2 relative">
|
||||||
<div className="flex-shrink-0">{icon}</div>
|
<div className="flex-shrink-0">{icon}</div>
|
||||||
|
{isDeployed ? (
|
||||||
<a
|
<a
|
||||||
href={`https://${domain}`}
|
href={`https://${domain}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -92,9 +108,12 @@ function DeploymentOption({
|
|||||||
>
|
>
|
||||||
{domain}
|
{domain}
|
||||||
</a>
|
</a>
|
||||||
|
) : (
|
||||||
|
<span className="font-semibold text-gray-300">{domain}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-400 mt-0 ml-7">
|
<p className="text-sm text-gray-400 mt-0 ml-7">
|
||||||
{timestamp} • {user}
|
{isDeployed ? `${timestamp} • ${user}` : "Never deployed"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,7 @@ interface TerminalContextType {
|
|||||||
createNewTerminal: (command?: string) => Promise<void>
|
createNewTerminal: (command?: string) => Promise<void>
|
||||||
closeTerminal: (id: string) => void
|
closeTerminal: (id: string) => void
|
||||||
deploy: (callback: () => void) => void
|
deploy: (callback: () => void) => void
|
||||||
|
getAppExists: ((appName: string) => Promise<boolean>) | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const TerminalContext = createContext<TerminalContextType | undefined>(
|
const TerminalContext = createContext<TerminalContextType | undefined>(
|
||||||
@ -35,6 +36,19 @@ export const TerminalProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
>([])
|
>([])
|
||||||
const [activeTerminalId, setActiveTerminalId] = useState<string>("")
|
const [activeTerminalId, setActiveTerminalId] = useState<string>("")
|
||||||
const [creatingTerminal, setCreatingTerminal] = useState<boolean>(false)
|
const [creatingTerminal, setCreatingTerminal] = useState<boolean>(false)
|
||||||
|
const [isSocketReady, setIsSocketReady] = useState<boolean>(false)
|
||||||
|
|
||||||
|
// Listen for the "ready" signal from the socket
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (socket) {
|
||||||
|
socket.on("ready", () => {
|
||||||
|
setIsSocketReady(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (socket) socket.off("ready")
|
||||||
|
}
|
||||||
|
}, [socket])
|
||||||
|
|
||||||
const createNewTerminal = async (command?: string): Promise<void> => {
|
const createNewTerminal = async (command?: string): Promise<void> => {
|
||||||
if (!socket) return
|
if (!socket) return
|
||||||
@ -78,6 +92,18 @@ export const TerminalProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAppExists = async (appName: string): Promise<boolean> => {
|
||||||
|
console.log("Is there a socket: " + !!socket)
|
||||||
|
if (!socket) {
|
||||||
|
console.error("Couldn't check if app exists: No socket")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const response: { exists: boolean } = await new Promise((resolve) =>
|
||||||
|
socket.emit("getAppExists", { appName }, resolve)
|
||||||
|
)
|
||||||
|
return response.exists
|
||||||
|
}
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
terminals,
|
terminals,
|
||||||
setTerminals,
|
setTerminals,
|
||||||
@ -88,6 +114,7 @@ export const TerminalProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
createNewTerminal,
|
createNewTerminal,
|
||||||
closeTerminal,
|
closeTerminal,
|
||||||
deploy,
|
deploy,
|
||||||
|
getAppExists: isSocketReady ? getAppExists : null,
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user