[sandbox] Add C# for sandbox
This commit is contained in:
parent
2060d079c8
commit
f5e8990c0a
@ -5,41 +5,25 @@ WORKDIR /app
|
|||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
|
|
||||||
RUN apt-get install curl -y
|
RUN apt-get install curl -y
|
||||||
|
|
||||||
RUN apt-get install -y ca-certificates curl gnupg
|
RUN apt-get install -y ca-certificates curl gnupg
|
||||||
RUN mkdir -p /etc/apt/keyrings
|
RUN mkdir -p /etc/apt/keyrings
|
||||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||||
ENV NODE_MAJOR=18
|
ENV NODE_MAJOR=18
|
||||||
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
RUN apt-get install nodejs -y
|
|
||||||
|
|
||||||
RUN apt-get install git -y
|
RUN apt-get install nodejs git openjdk-17-jdk-headless dotnet-sdk-7.0 -y
|
||||||
|
|
||||||
|
ENV DOTNET_NOLOGO=true
|
||||||
|
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
|
||||||
|
|
||||||
RUN git config --global user.name "Admin"
|
RUN git config --global user.name "Admin"
|
||||||
|
|
||||||
RUN git config --global user.email noemail@example.com
|
RUN git config --global user.email noemail@example.com
|
||||||
|
|
||||||
WORKDIR /opt
|
|
||||||
|
|
||||||
RUN apt-get install wget -y
|
|
||||||
|
|
||||||
RUN wget -O java.tar.gz https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.7%2B7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.7_7.tar.gz
|
|
||||||
|
|
||||||
RUN tar -xzvf java.tar.gz
|
|
||||||
|
|
||||||
RUN rm java.tar.gz
|
|
||||||
|
|
||||||
ENV JAVA_PATH=/opt/jdk-17.0.7+7/bin
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
CMD ["node", "build"]
|
CMD ["node", "build"]
|
9
sandbox/package-lock.json
generated
9
sandbox/package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"simple-git": "^3.22.0",
|
"simple-git": "^3.22.0",
|
||||||
|
"tree-kill": "^1.2.2",
|
||||||
"url-join": "^5.0.0",
|
"url-join": "^5.0.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
},
|
},
|
||||||
@ -153,6 +154,14 @@
|
|||||||
"url": "https://github.com/steveukx/git-js?sponsor=1"
|
"url": "https://github.com/steveukx/git-js?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tree-kill": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||||
|
"bin": {
|
||||||
|
"tree-kill": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"simple-git": "^3.22.0",
|
"simple-git": "^3.22.0",
|
||||||
|
"tree-kill": "^1.2.2",
|
||||||
"url-join": "^5.0.0",
|
"url-join": "^5.0.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import os, { EOL } from 'os';
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { simpleGit, SimpleGit } from 'simple-git';
|
import { simpleGit, SimpleGit } from 'simple-git';
|
||||||
import { runJava } from './run/java.js';
|
import { runJava } from './run/java.js';
|
||||||
|
import { runCSharp } from './run/csharp.js';
|
||||||
|
|
||||||
export const timeoutSeconds = 30;
|
export const timeoutSeconds = 30;
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ const submissionGetData = z
|
|||||||
contestName: z.string(),
|
contestName: z.string(),
|
||||||
teamId: z.number(),
|
teamId: z.number(),
|
||||||
teamName: z.string(),
|
teamName: z.string(),
|
||||||
|
teamLanguage: z.enum(['Java', 'CSharp']),
|
||||||
problem: z.object({
|
problem: z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
pascalName: z.string(),
|
pascalName: z.string(),
|
||||||
@ -81,7 +83,7 @@ async function cloneAndRun(submissionData: SubmissionGetData) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tmpDir = os.tmpdir();
|
const tmpDir = os.tmpdir();
|
||||||
const buildDir = join(tmpDir, 'bwcontest_java');
|
const buildDir = join(tmpDir, 'bwcontest-build');
|
||||||
if (fs.existsSync(buildDir)) {
|
if (fs.existsSync(buildDir)) {
|
||||||
fs.removeSync(buildDir);
|
fs.removeSync(buildDir);
|
||||||
}
|
}
|
||||||
@ -100,16 +102,32 @@ async function cloneAndRun(submissionData: SubmissionGetData) {
|
|||||||
await git.clone(teamRepoUrl, '.');
|
await git.clone(teamRepoUrl, '.');
|
||||||
await git.checkout(submissionData.submission.commitHash);
|
await git.checkout(submissionData.submission.commitHash);
|
||||||
const problemName = submissionData.submission.problem.pascalName;
|
const problemName = submissionData.submission.problem.pascalName;
|
||||||
let runResult: RunResult;
|
let runResult: RunResult | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
runResult = await runJava(
|
if (submissionData.submission.teamLanguage === 'Java') {
|
||||||
javaBinPath,
|
let res = await runJava({
|
||||||
buildDir,
|
srcDir: buildDir,
|
||||||
join(repoDir, problemName, problemName + '.java'),
|
mainFile: join(repoDir, problemName, problemName + '.java'),
|
||||||
problemName,
|
mainClass: problemName,
|
||||||
submissionData.submission.problem.realInput
|
input: submissionData.submission.problem.realInput
|
||||||
);
|
});
|
||||||
|
if (res.success === true) {
|
||||||
|
runResult = await res.runResult;
|
||||||
|
} else {
|
||||||
|
runResult = res.runResult;
|
||||||
|
}
|
||||||
|
} else if (submissionData.submission.teamLanguage === 'CSharp') {
|
||||||
|
let res = await runCSharp({
|
||||||
|
srcDir: join(repoDir, problemName),
|
||||||
|
input: submissionData.submission.problem.realInput
|
||||||
|
});
|
||||||
|
if (res.success === true) {
|
||||||
|
runResult = await res.runResult;
|
||||||
|
} else {
|
||||||
|
runResult = res.runResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
runResult = {
|
runResult = {
|
||||||
kind: 'SandboxError',
|
kind: 'SandboxError',
|
||||||
@ -117,6 +135,7 @@ async function cloneAndRun(submissionData: SubmissionGetData) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runResult !== undefined) {
|
||||||
printRunResult(runResult);
|
printRunResult(runResult);
|
||||||
|
|
||||||
const postBodyObject: SubmissionPostData = {
|
const postBodyObject: SubmissionPostData = {
|
||||||
@ -140,6 +159,9 @@ async function cloneAndRun(submissionData: SubmissionGetData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`- POST: Succeeded`);
|
console.log(`- POST: Succeeded`);
|
||||||
|
} else {
|
||||||
|
console.warn(`runResult is undefined`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function printRunResult(runResult: RunResult) {
|
function printRunResult(runResult: RunResult) {
|
||||||
@ -163,11 +185,7 @@ function printRunResult(runResult: RunResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateEnv(): boolean {
|
function validateEnv(): boolean {
|
||||||
return (
|
return process.env.ADMIN_URL !== undefined && process.env.REPO_URL !== undefined;
|
||||||
process.env.ADMIN_URL !== undefined &&
|
|
||||||
process.env.REPO_URL !== undefined &&
|
|
||||||
process.env.JAVA_PATH !== undefined
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
89
sandbox/src/run/csharp.ts
Normal file
89
sandbox/src/run/csharp.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import * as fs from 'fs-extra';
|
||||||
|
import { join } from 'path';
|
||||||
|
import os = require('os');
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
import kill from 'tree-kill';
|
||||||
|
import { RunResult, timeoutSeconds } from '../index.js';
|
||||||
|
import { IRunner, IRunnerReturn } from './types.js';
|
||||||
|
|
||||||
|
export const runCSharp: IRunner = async function (params: {
|
||||||
|
srcDir: string;
|
||||||
|
input: string;
|
||||||
|
outputCallback?: (data: string) => void;
|
||||||
|
}): IRunnerReturn {
|
||||||
|
console.log(`- RUN: ${params.srcDir}`);
|
||||||
|
const child = spawn('dotnet run', { shell: true, cwd: params.srcDir });
|
||||||
|
|
||||||
|
let outputBuffer = '';
|
||||||
|
child.stdout.setEncoding('utf8');
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
outputBuffer += data.toString();
|
||||||
|
params.outputCallback?.(data.toString());
|
||||||
|
});
|
||||||
|
child.stderr.setEncoding('utf8');
|
||||||
|
child.stderr.on('data', (data) => {
|
||||||
|
outputBuffer += data.toString();
|
||||||
|
params.outputCallback?.(data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
let runStartTime = performance.now();
|
||||||
|
child.stdin.write(params.input);
|
||||||
|
child.stdin.end();
|
||||||
|
|
||||||
|
let timeLimitExceeded = false;
|
||||||
|
let completedNormally = false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
runResult: new Promise<RunResult>((resolve) => {
|
||||||
|
child.on('close', () => {
|
||||||
|
completedNormally = !timeLimitExceeded;
|
||||||
|
|
||||||
|
let runEndTime = performance.now();
|
||||||
|
const runtimeMilliseconds = Math.floor(runEndTime - runStartTime);
|
||||||
|
|
||||||
|
if (completedNormally) {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
resolve({
|
||||||
|
kind: 'Completed',
|
||||||
|
output: outputBuffer,
|
||||||
|
exitCode: child.exitCode!,
|
||||||
|
runtimeMilliseconds
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(`Process terminated, total sandbox time: ${runtimeMilliseconds}ms`);
|
||||||
|
resolve({
|
||||||
|
kind: 'TimeLimitExceeded',
|
||||||
|
output: outputBuffer,
|
||||||
|
resultKindReason: `Timeout after ${timeoutSeconds} seconds`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let timeoutHandle = setTimeout(() => {
|
||||||
|
if (completedNormally) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Run timed out after ${timeoutSeconds} seconds, killing process...`);
|
||||||
|
timeLimitExceeded = true;
|
||||||
|
|
||||||
|
child.stdin.end();
|
||||||
|
child.stdin.destroy();
|
||||||
|
child.stdout.destroy();
|
||||||
|
child.stderr.destroy();
|
||||||
|
if (child.pid !== undefined) {
|
||||||
|
kill(child.pid);
|
||||||
|
}
|
||||||
|
}, timeoutSeconds * 1000);
|
||||||
|
}),
|
||||||
|
killFunc() {
|
||||||
|
if (child.pid !== undefined) {
|
||||||
|
if (!completedNormally && !timeLimitExceeded) {
|
||||||
|
kill(child.pid);
|
||||||
|
params.outputCallback?.('\n[Manually stopped]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
@ -1,34 +1,40 @@
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { exec, spawn } from 'child_process';
|
import { exec, spawn } from 'child_process';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import { RunResult, RunResultKind, timeoutSeconds } from '../index.js';
|
import { RunResult, timeoutSeconds } from '../index.js';
|
||||||
|
import { IRunner, IRunnerParams, IRunnerReturn } from './types.js';
|
||||||
|
import kill from 'tree-kill';
|
||||||
|
|
||||||
const execPromise = util.promisify(exec);
|
const execPromise = util.promisify(exec);
|
||||||
|
|
||||||
export async function runJava(
|
interface IRunnerParamsJava extends IRunnerParams {
|
||||||
javaBinPath: string,
|
srcDir: string;
|
||||||
buildDir: string,
|
mainFile: string;
|
||||||
mainFile: string,
|
mainClass: string;
|
||||||
mainClass: string,
|
input: string;
|
||||||
input: string
|
outputCallback?: (data: string) => void;
|
||||||
): Promise<RunResult> {
|
}
|
||||||
console.log(`- BUILD: ${mainFile}`);
|
|
||||||
const compileCommand = `${join(javaBinPath, 'javac')} -cp ${join(
|
export const runJava: IRunner<IRunnerParamsJava> = async function (
|
||||||
buildDir,
|
params: IRunnerParamsJava
|
||||||
'src'
|
): IRunnerReturn {
|
||||||
)} ${mainFile} -d ${join(buildDir, 'build')}`;
|
console.log(`- BUILD: ${params.mainFile}`);
|
||||||
|
const compileCommand = `javac -cp ${join(params.srcDir, 'src')} ${params.mainFile} -d ${join(params.srcDir, 'build')}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await execPromise(compileCommand);
|
await execPromise(compileCommand);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const buildErrorText = e?.toString() ?? 'Unknown build errors.';
|
const buildErrorText = e?.toString() ?? 'Unknown build errors.';
|
||||||
console.log('Build errors: ' + buildErrorText);
|
console.log('Build errors: ' + buildErrorText);
|
||||||
return { kind: 'CompileFailed', resultKindReason: buildErrorText };
|
return {
|
||||||
|
success: false,
|
||||||
|
runResult: { kind: 'CompileFailed', resultKindReason: buildErrorText }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`- RUN: ${mainClass}`);
|
console.log(`- RUN: ${params.mainClass}`);
|
||||||
const runCommand = `${join(javaBinPath, 'java')} -cp "${join(buildDir, 'build')}" ${mainClass}`;
|
const runCommand = `java -cp "${join(params.srcDir, 'build')}" ${params.mainClass}`;
|
||||||
return new Promise((resolve) => {
|
|
||||||
let outputBuffer = '';
|
let outputBuffer = '';
|
||||||
const child = spawn(runCommand, { shell: true });
|
const child = spawn(runCommand, { shell: true });
|
||||||
child.stdout.setEncoding('utf8');
|
child.stdout.setEncoding('utf8');
|
||||||
@ -41,12 +47,15 @@ export async function runJava(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let runStartTime = performance.now();
|
let runStartTime = performance.now();
|
||||||
child.stdin.write(input);
|
child.stdin.write(params.input);
|
||||||
child.stdin.end();
|
child.stdin.end();
|
||||||
|
|
||||||
let timeLimitExceeded = false;
|
let timeLimitExceeded = false;
|
||||||
let completedNormally = false;
|
let completedNormally = false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
runResult: new Promise<RunResult>(async (resolve) => {
|
||||||
child.on('close', () => {
|
child.on('close', () => {
|
||||||
completedNormally = !timeLimitExceeded;
|
completedNormally = !timeLimitExceeded;
|
||||||
|
|
||||||
@ -85,5 +94,14 @@ export async function runJava(
|
|||||||
child.stderr.destroy();
|
child.stderr.destroy();
|
||||||
child.kill('SIGKILL');
|
child.kill('SIGKILL');
|
||||||
}, timeoutSeconds * 1000);
|
}, timeoutSeconds * 1000);
|
||||||
});
|
}),
|
||||||
|
killFunc() {
|
||||||
|
if (child.pid !== undefined) {
|
||||||
|
if (!completedNormally && !timeLimitExceeded) {
|
||||||
|
kill(child.pid);
|
||||||
|
params.outputCallback?.('\n[Manually stopped]');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
16
sandbox/src/run/types.d.ts
vendored
Normal file
16
sandbox/src/run/types.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { RunResult } from '../index.ts';
|
||||||
|
|
||||||
|
interface IRunnerParams {
|
||||||
|
srcDir: string;
|
||||||
|
input: string;
|
||||||
|
outputCallback?: (data: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRunnerReturn = Promise<
|
||||||
|
| { success: true; killFunc: () => void; runResult: Promise<RunResult> }
|
||||||
|
| { success: false; runResult: RunResult }
|
||||||
|
>;
|
||||||
|
|
||||||
|
interface IRunner<T extends IRunnerParams = IRunnerParams> {
|
||||||
|
(params: T): IRunnerReturn;
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { stretchTextarea } from '$lib/util';
|
import { stretchTextarea } from '$lib/util';
|
||||||
import FormAlert from '$lib/FormAlert.svelte';
|
import FormAlert from '$lib/FormAlert.svelte';
|
||||||
|
import { theme } from '../../../stores';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
export let form: Actions;
|
export let form: Actions;
|
||||||
@ -72,7 +73,7 @@
|
|||||||
<textarea use:stretchTextarea class="code mb-3 form-control" disabled>{data.output}</textarea>
|
<textarea use:stretchTextarea class="code mb-3 form-control" disabled>{data.output}</textarea>
|
||||||
|
|
||||||
<h3>Diff</h3>
|
<h3>Diff</h3>
|
||||||
<div class="mt-3" id="diff" />
|
<div class="mt-3" id="diff" class:d2h-dark-color-scheme={$theme === 'dark'} class:d2h-light-color-scheme={$theme === 'light'}/>
|
||||||
|
|
||||||
<form method="POST" action="?/submit" use:enhance>
|
<form method="POST" action="?/submit" use:enhance>
|
||||||
<h5>Message</h5>
|
<h5>Message</h5>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
import { enhance } from '$app/forms';
|
import { enhance } from '$app/forms';
|
||||||
import { stretchTextarea } from '$lib/util';
|
import { stretchTextarea } from '$lib/util';
|
||||||
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
import ConfirmModal from '$lib/ConfirmModal.svelte';
|
||||||
|
import { theme } from '../../../stores';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
export let form: Actions;
|
export let form: Actions;
|
||||||
@ -141,56 +142,10 @@
|
|||||||
<h3 style="text-align:center">Output</h3>
|
<h3 style="text-align:center">Output</h3>
|
||||||
<textarea use:stretchTextarea class="code mb-3 form-control" disabled>{data.output}</textarea>
|
<textarea use:stretchTextarea class="code mb-3 form-control" disabled>{data.output}</textarea>
|
||||||
<h3 style="text-align:center">Diff</h3>
|
<h3 style="text-align:center">Diff</h3>
|
||||||
<div id="diff" class="dark-diff" />
|
<div
|
||||||
|
id="diff"
|
||||||
|
class="dark-diff"
|
||||||
|
class:d2h-dark-color-scheme={$theme === 'dark'}
|
||||||
|
class:d2h-light-color-scheme={$theme === 'light'}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
:global(.dark-diff) {
|
|
||||||
:global(.d2h-code-side-linenumber),
|
|
||||||
:global(.d2h-info),
|
|
||||||
:global(.d2h-emptyplaceholder),
|
|
||||||
:global(.d2h-code-side-emptyplaceholder),
|
|
||||||
:global(.d2h-file-header),
|
|
||||||
:global(.d2h-tag) {
|
|
||||||
background-color: var(--bs-body-bg);
|
|
||||||
color: var(--bs-body-color);
|
|
||||||
}
|
|
||||||
:global(span) {
|
|
||||||
color: var(--bs-body-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.d2h-file-wrapper) {
|
|
||||||
border-color: var(--bs-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.d2h-file-header) {
|
|
||||||
border-bottom-color: var(--bs-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.d2h-info) {
|
|
||||||
border-color: var(--bs-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.d2h-del) {
|
|
||||||
background-color: var(--bs-danger-border-subtle);
|
|
||||||
border-color: var(--bs-danger);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(del) {
|
|
||||||
background-color: rgba(210, 85, 97, 0.5);
|
|
||||||
}
|
|
||||||
:global(.d2h-ins) {
|
|
||||||
background-color: var(--bs-success-border-subtle);
|
|
||||||
border-color: var(--bs-success);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.d2h-code-side-emptyplaceholder),
|
|
||||||
:global(.d2h-emptyplaceholder) {
|
|
||||||
border-color: var(--bs-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(ins) {
|
|
||||||
background-color: rgba(13, 125, 75, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -21,6 +21,7 @@ export const GET = (async () => {
|
|||||||
contestName: submissions[0].contest.name,
|
contestName: submissions[0].contest.name,
|
||||||
teamId: submissions[0].team.id,
|
teamId: submissions[0].team.id,
|
||||||
teamName: submissions[0].team.name,
|
teamName: submissions[0].team.name,
|
||||||
|
teamLanguage: submissions[0].team.language,
|
||||||
problem: {
|
problem: {
|
||||||
id: submissions[0].problemId,
|
id: submissions[0].problemId,
|
||||||
pascalName: submissions[0].problem.pascalName,
|
pascalName: submissions[0].problem.pascalName,
|
||||||
|
Loading…
Reference in New Issue
Block a user