Skip to content

Commit

Permalink
Merge pull request #13 from ARYPROGRAMMER/develop/home
Browse files Browse the repository at this point in the history
feat: code exec engine-piston
  • Loading branch information
ARYPROGRAMMER authored Dec 17, 2024
2 parents 75bf2c4 + 7f852b3 commit d9ea2a9
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 32 deletions.
23 changes: 0 additions & 23 deletions .github/workflows/label.yml

This file was deleted.

2 changes: 2 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
FilterApi,
FunctionReference,
} from "convex/server";
import type * as codeExecutions from "../codeExecutions.js";
import type * as http from "../http.js";
import type * as users from "../users.js";

Expand All @@ -25,6 +26,7 @@ import type * as users from "../users.js";
* ```
*/
declare const fullApi: ApiFromModules<{
codeExecutions: typeof codeExecutions;
http: typeof http;
users: typeof users;
}>;
Expand Down
35 changes: 35 additions & 0 deletions convex/codeExecutions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ConvexError, v } from "convex/values";
import { mutation } from "./_generated/server";

export const saveCodeExecution = mutation({
args: {
language: v.string(),
code: v.string(),
output: v.optional(v.string()),
error: v.optional(v.string()),
},

handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) {
throw new ConvexError("User not authenticated");
}

const user = await ctx.db
.query("users")
.withIndex("by_user_id")
.filter((q) => q.eq(q.field("userId"), identity.subject))
.first();

if (!user?.isPro && args.language !== "javascript") {
throw new ConvexError(
"Only Pro users can execute code in languages other than JavaScript"
);
}

await ctx.db.insert("codeExecutions", {
...args,
userId: identity.subject,
});
},
});
72 changes: 68 additions & 4 deletions src/app/(home)/_components/RunButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,73 @@
import React from 'react'
"use client";

import {
getExecutionResult,
useCodeEditorState,
} from "@/store/useCodeEditorStore";
import { useUser } from "@clerk/nextjs";
import { Loader2, Play } from "lucide-react";
import React from "react";
import { motion } from "framer-motion";
import { useMutation } from "convex/react";
import { api } from "../../../../convex/_generated/api";

function RunButton() {
const { user } = useUser();
const { runCode, language, isRunning } = useCodeEditorState();
const saveCodeExecution = useMutation(api.codeExecutions.saveCodeExecution);
const handleRun = async () => {
await runCode();
const result = getExecutionResult();

if (user && result) {
//convex saving
await saveCodeExecution({
language,
code: result.code,
output: result.output || undefined,
error: result.error || undefined,
});
}
};

return (
<div>RunBuilding...</div>
)
<motion.button
onClick={handleRun}
disabled={isRunning}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className={`
group relative inline-flex items-center gap-2.5 px-5 py-2.5
disabled:cursor-not-allowed
focus:outline-none
`}
>
<div className="absolute inset-0 bg-gradient-to-r from-blue-600 to-blue-500 rounded-xl opacity-100 transition-opacity group-hover:opacity-90" />

<div className="relative flex items-center gap-2.5">
{isRunning ? (
<>
<div className="relative">
<Loader2 className="w-4 h-4 animate-spin text-white/70" />
<div className="absolute inset-0 blur animate-pulse" />
</div>
<span className="text-sm font-medium text-white/90">
Executing...
</span>
</>
) : (
<>
<div className="relative flex items-center justify-center w-4 h-4">
<Play className="w-4 h-4 text-white/90 transition-transform group-hover:scale-110 group-hover:text-white" />
</div>
<span className="text-sm font-medium text-white/90 group-hover:text-white">
Execute Code
</span>
</>
)}
</div>
</motion.button>
);
}

export default RunButton
export default RunButton;
90 changes: 85 additions & 5 deletions src/store/useCodeEditorStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { create } from "zustand";
import { LANGUAGE_CONFIG } from "@/app/(home)/_constants";
import { Monaco } from "@monaco-editor/react";
import { CodeEditorState } from "@/types";
import { version } from "os";

const getInitialState = () => {
//initial load
Expand Down Expand Up @@ -51,23 +52,102 @@ export const useCodeEditorState = create<CodeEditorState>((set, get) => {
set({ fontSize });
},

setLanguage: (language: string)=>{
setLanguage: (language: string) => {
const currentCode = get().editor?.getValue();
if (currentCode) {
localStorage.setItem(`editor-code-${get().language}`, currentCode);
}
localStorage.setItem("editor-language", language);
set({
language,
output:"",
error:null,
output: "",
error: null,
});
},
runCode: async () => {
const { language, getCode } = get();
const code = getCode();
if (!code) {
set({ error: "Code is empty" });
return;
}

//TODO : Implement code execution
set({ isRunning: true, error: null, output: "" });

},
try {
const runtime = LANGUAGE_CONFIG[language].pistonRuntime;
const res = await fetch("https://emkc.org/api/v2/piston/execute", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
language: runtime.language,
version: runtime.version,
files: [{ content: code }],
}),
});
const data = await res.json();
console.log("data from piston ", data);
if (data.message) {
set({
error: data.message,
executionResult: { code, output: "", error: data.message },
});

return;
}

// error handling

if (data.compile && data.compile.code !== 0) {
const error = data.compile.stderr || data.compile.output;
set({
error,
executionResult: {
code,
output: "",
error,
},
});
return;
}

if (data.run && data.run.code !== 0) {
const error = data.run.stderr || data.run.output;
set({
error,
executionResult: {
code,
output: "",
error,
},
});
return;
}

const output = data.run.output;
set({
output: output.trim(),
executionResult: { code, output: output.trim(), error: null },
});
} catch (error) {
console.log("error from piston ", error);
set({
error: "ERROR RUNNING CODE",

executionResult: {
code,
output: "",
error: "ERROR RUNNING CODE",
},
});
} finally {
set({ isRunning: false });
}
},
};
});


export const getExecutionResult = () => useCodeEditorState.getState().executionResult;

0 comments on commit d9ea2a9

Please sign in to comment.