diff --git a/apps/daemon/src/session/index.ts b/apps/daemon/src/session/index.ts
index 451c451..b1051eb 100644
--- a/apps/daemon/src/session/index.ts
+++ b/apps/daemon/src/session/index.ts
@@ -6,16 +6,15 @@ import deleteSession from "./delete.js";
import { getFile, listFiles, sendFile } from "./file.js";
import manageSession from "./manage.js";
import screenshot from "./screenshot.js";
-// fill this
export default new Elysia({ prefix: "/sessions" })
.put(
- "/",
+ "/create",
async ({ body }) => {
const session = await createSession(body);
return {
success: true,
id: session.Id,
- created: session.Created,
+ created: Number.parseInt(session.Created),
};
},
{
diff --git a/apps/web/server.ts b/apps/web/server.ts
index ef273bb..4faf5aa 100644
--- a/apps/web/server.ts
+++ b/apps/web/server.ts
@@ -1,6 +1,4 @@
import { createServer } from "node:http";
-import auth from "@stardust/common/auth";
-import { fromNodeHeaders } from "better-auth/node";
import { createProxyMiddleware } from "http-proxy-middleware";
import next from "next";
const dev = process.env.NODE_ENV !== "production";
diff --git a/apps/web/src/app/(main)/create-session-button.tsx b/apps/web/src/app/(main)/create-session-button.tsx
index 3d349ef..3f439da 100644
--- a/apps/web/src/app/(main)/create-session-button.tsx
+++ b/apps/web/src/app/(main)/create-session-button.tsx
@@ -1,10 +1,10 @@
"use client";
import { Button } from "@/components/ui/button";
-// import { createSession } from "@/lib/session";
+import { createSession } from "@/lib/session/create";
import { useRouter } from "next/navigation";
import { useTransition } from "react";
import { toast } from "sonner";
-export function CreateSessionButton({ image }: { image: string }) {
+export function CreateSessionButton({ workspace }: { workspace: string }) {
const [isPending, startTransition] = useTransition();
const router = useRouter();
return (
@@ -12,12 +12,12 @@ export function CreateSessionButton({ image }: { image: string }) {
disabled={isPending}
onClick={() =>
startTransition(async () => {
- // const session = await createSession(image).catch(() => {
- // toast.error("Error creating session");
- // });
- // if (!session) return;
- // router.push(`/view/${session[0].id}`);
- console.log("w riz?");
+ const session = await createSession(workspace).catch(() => {
+ toast.error("Error creating session");
+ });
+ if (!session) return;
+ // @ts-expect-error to be fixed later
+ router.push(`/view/${session[0].id}`);
})
}
>
diff --git a/apps/web/src/app/(main)/page.tsx b/apps/web/src/app/(main)/page.tsx
index 4a8b512..437ba51 100644
--- a/apps/web/src/app/(main)/page.tsx
+++ b/apps/web/src/app/(main)/page.tsx
@@ -53,7 +53,7 @@ export default async function Dashboard() {
Close
-
+
diff --git a/apps/web/src/app/(main)/sessions/page.tsx b/apps/web/src/app/(main)/sessions/page.tsx
index a8f6700..8bafb63 100644
--- a/apps/web/src/app/(main)/sessions/page.tsx
+++ b/apps/web/src/app/(main)/sessions/page.tsx
@@ -4,8 +4,8 @@ import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Button } from "@/components/ui/button";
import { CardTitle } from "@/components/ui/card";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
-import { getNode } from "@/lib/sessions/client";
-import { inspectSession } from "@/lib/sessions/inspect";
+import { getNode } from "@/lib/session/client";
+import { inspectSession } from "@/lib/session/inspect";
import auth from "@stardust/common/auth";
import db, { type SelectSession } from "@stardust/db";
import { Container, Loader2, PauseCircle, PlayCircle, ScreenShare, Square, Trash2 } from "lucide-react";
diff --git a/apps/web/src/lib/sessions/client.ts b/apps/web/src/lib/session/client.ts
similarity index 100%
rename from apps/web/src/lib/sessions/client.ts
rename to apps/web/src/lib/session/client.ts
diff --git a/apps/web/src/lib/session/create.ts b/apps/web/src/lib/session/create.ts
new file mode 100644
index 0000000..0f4bff9
--- /dev/null
+++ b/apps/web/src/lib/session/create.ts
@@ -0,0 +1,59 @@
+"use server";
+
+import auth from "@stardust/common/auth";
+import { stardustConnector } from "@stardust/common/daemon/client";
+import { getConfig } from "@stardust/config";
+import db, { session } from "@stardust/db";
+import { headers } from "next/headers";
+
+export async function createSession(workspace: string) {
+ console.log(`✨ Stardust: Creating session using workspace ${workspace}`);
+ const config = getConfig();
+ const userSession = await auth.api.getSession({ headers: await headers() });
+ if (!userSession?.user) throw new Error("User not found");
+ // todo: move these limits to each node instead
+ if (config.session?.usageLimits) {
+ const { role, allSessions } = await db.transaction(async (tx) => ({
+ role: (
+ await tx.query.user.findFirst({
+ where: (user, { eq }) => eq(user.id, userSession?.user?.id as string),
+ columns: {
+ role: true,
+ },
+ })
+ )?.role,
+ allSessions: await tx.select().from(session),
+ }));
+ if (config.session?.usageLimits.instance && config.session?.usageLimits.instance <= allSessions.length)
+ throw new Error("Instance limit exceeded");
+ if (config.session.usageLimits.user && role !== "admin") {
+ const userSessions = allSessions.filter((v) => v.userId === userSession.user.id);
+ if (config.session?.usageLimits.user <= userSessions.length) throw new Error("User session limit exceeded");
+ }
+ }
+ // todo: make this pick node with lowest resource usage
+ const sessionNode = config.nodes[0];
+ const node = stardustConnector(`http://${sessionNode.hostname}:${sessionNode.port || 4000}`, sessionNode.token);
+ const { data: container, error } = await node.sessions.create.put({
+ workspace,
+ user: userSession.user.id,
+ });
+ if (error) throw error;
+ const expiry = new Date();
+ expiry.setMinutes(expiry.getMinutes() + (config.session?.keepaliveDuration || 1440));
+ return db
+ .insert(session)
+ .values({
+ id: container.id,
+ dockerImage: workspace,
+ createdAt: new Date(container.created),
+ expiresAt: expiry,
+ userId: userSession.user.id,
+ node: sessionNode.id,
+ })
+ .returning()
+ .catch(async (e) => {
+ await node.sessions({ ...container }).delete();
+ throw e;
+ });
+}
diff --git a/apps/web/src/lib/sessions/inspect.ts b/apps/web/src/lib/session/inspect.ts
similarity index 100%
rename from apps/web/src/lib/sessions/inspect.ts
rename to apps/web/src/lib/session/inspect.ts
diff --git a/packages/db/schema/index.ts b/packages/db/schema/index.ts
index a086ec8..c14fee6 100644
--- a/packages/db/schema/index.ts
+++ b/packages/db/schema/index.ts
@@ -1,5 +1,5 @@
import { relations } from "drizzle-orm";
-import { bigint, pgTable, text } from "drizzle-orm/pg-core";
+import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { user } from "./auth";
export type SelectUser = typeof user.$inferSelect;
@@ -20,8 +20,8 @@ export const session = pgTable("session", {
id: text("id").primaryKey().notNull(),
dockerImage: text("dockerImage").notNull(),
node: text("node").notNull(),
- createdAt: bigint("createdAt", { mode: "number" }).notNull(),
- expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
+ createdAt: timestamp("createdAt").notNull(),
+ expiresAt: timestamp("expiresAt").notNull(),
userId: text("userId")
.notNull()
.references(() => user.id),