Login with extension
This commit is contained in:
parent
093ff3af56
commit
d43ffb680a
2
extension/bwcontest/.vscode/extensions.json
vendored
2
extension/bwcontest/.vscode/extensions.json
vendored
@ -1,6 +1,4 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
|
18
extension/bwcontest/.vscode/launch.json
vendored
18
extension/bwcontest/.vscode/launch.json
vendored
@ -1,7 +1,3 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
@ -15,20 +11,6 @@
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
6
extension/bwcontest/.vscode/settings.json
vendored
6
extension/bwcontest/.vscode/settings.json
vendored
@ -1,11 +1,9 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
"out": false
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
"out": true
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
3
extension/bwcontest/.vscode/tasks.json
vendored
3
extension/bwcontest/.vscode/tasks.json
vendored
@ -1,12 +1,9 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
|
@ -1,9 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "bwcontest" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
BIN
extension/bwcontest/media/icon.png
Normal file
BIN
extension/bwcontest/media/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 192 KiB |
30
extension/bwcontest/media/reset.css
Normal file
30
extension/bwcontest/media/reset.css
Normal file
@ -0,0 +1,30 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
ol,
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
91
extension/bwcontest/media/vscode.css
Normal file
91
extension/bwcontest/media/vscode.css
Normal file
@ -0,0 +1,91 @@
|
||||
:root {
|
||||
--container-padding: 20px;
|
||||
--input-padding-vertical: 6px;
|
||||
--input-padding-horizontal: 4px;
|
||||
--input-margin-vertical: 4px;
|
||||
--input-margin-horizontal: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0 var(--container-padding);
|
||||
color: var(--vscode-foreground);
|
||||
font-size: var(--vscode-font-size);
|
||||
font-weight: var(--vscode-font-weight);
|
||||
font-family: var(--vscode-font-family);
|
||||
background-color: var(--vscode-editor-background);
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: var(--container-padding);
|
||||
}
|
||||
|
||||
body > *,
|
||||
form > * {
|
||||
margin-block-start: var(--input-margin-vertical);
|
||||
margin-block-end: var(--input-margin-vertical);
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline-color: var(--vscode-focusBorder) !important;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--vscode-textLink-foreground);
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:active {
|
||||
color: var(--vscode-textLink-activeForeground);
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: var(--vscode-editor-font-size);
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
outline: 1px solid transparent;
|
||||
outline-offset: 2px !important;
|
||||
color: var(--vscode-button-foreground);
|
||||
background: var(--vscode-button-background);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
background: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
background: var(--vscode-button-secondaryBackground);
|
||||
}
|
||||
|
||||
button.secondary:hover {
|
||||
background: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
input:not([type='checkbox']),
|
||||
textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
font-family: var(--vscode-font-family);
|
||||
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
|
||||
color: var(--vscode-input-foreground);
|
||||
outline-color: var(--vscode-input-border);
|
||||
background-color: var(--vscode-input-background);
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
}
|
2390
extension/bwcontest/package-lock.json
generated
2390
extension/bwcontest/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,36 +12,74 @@
|
||||
"activationEvents": [],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "bwcontest-sidebar-view",
|
||||
"title": "BWContest",
|
||||
"icon": "media/icon.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"bwcontest-sidebar-view": [
|
||||
{
|
||||
"type": "webview",
|
||||
"id": "bwcontest-sidebar",
|
||||
"name": "BWContest",
|
||||
"icon": "media/icon.png",
|
||||
"contextualTitle": "BWContest"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "bwcontest.helloWorld",
|
||||
"category": "BWContest",
|
||||
"title": "Hello World"
|
||||
},
|
||||
{
|
||||
"command": "bwcontest.testThing",
|
||||
"title": "Test Thing"
|
||||
"command": "bwcontest.askQuestion",
|
||||
"category": "BWContest",
|
||||
"title": "Ask Question"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"compile": "rollup -c && tsc -p ./",
|
||||
"watch": "concurrently \"rollup -c -w\" \"tsc -watch -p ./\"",
|
||||
"pretest": "npm run compile && npm run lint",
|
||||
"lint": "eslint src --ext ts",
|
||||
"test": "node ./out/test/runTest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "^1.78.0",
|
||||
"@rollup/plugin-commonjs": "^24.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.2",
|
||||
"@rollup/plugin-typescript": "^11.1.0",
|
||||
"@tsconfig/svelte": "^4.0.1",
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "16.x",
|
||||
"@types/vscode": "^1.78.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
||||
"@typescript-eslint/parser": "^5.59.1",
|
||||
"@vscode/test-electron": "^2.3.0",
|
||||
"concurrently": "^8.0.1",
|
||||
"eslint": "^8.39.0",
|
||||
"glob": "^8.1.0",
|
||||
"mocha": "^10.2.0",
|
||||
"typescript": "^5.0.4",
|
||||
"@vscode/test-electron": "^2.3.0"
|
||||
"postcss": "^8.4.23",
|
||||
"rollup-plugin-css-only": "^4.3.0",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-svelte": "^7.1.4",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"svelte": "^3.59.0",
|
||||
"svelte-check": "^3.3.1",
|
||||
"svelte-preprocess": "^5.0.3",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.4.0"
|
||||
}
|
||||
}
|
||||
|
49
extension/bwcontest/rollup.config.js
Normal file
49
extension/bwcontest/rollup.config.js
Normal file
@ -0,0 +1,49 @@
|
||||
import svelte from "rollup-plugin-svelte";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import sveltePreprocess from "svelte-preprocess";
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import css from "rollup-plugin-css-only";
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
export default fs
|
||||
.readdirSync(path.join(__dirname, "webviews", "pages"))
|
||||
.map((input) => {
|
||||
const name = input.split(".")[0];
|
||||
return {
|
||||
input: "webviews/pages/" + input,
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: "iife",
|
||||
name: "app",
|
||||
file: "out/compiled/" + name + ".js",
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
preprocess: sveltePreprocess(),
|
||||
emitCss: true
|
||||
}),
|
||||
css({ output: name + ".css" }),
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: ["svelte"],
|
||||
}),
|
||||
commonjs(),
|
||||
typescript({
|
||||
tsconfig: "webviews/tsconfig.json",
|
||||
sourceMap: !production,
|
||||
inlineSources: !production,
|
||||
}),
|
||||
production && terser(),
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
};
|
||||
});
|
164
extension/bwcontest/src/BWPanel.ts
Normal file
164
extension/bwcontest/src/BWPanel.ts
Normal file
@ -0,0 +1,164 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { getNonce } from './getNonce';
|
||||
|
||||
export class BWPanel {
|
||||
/**
|
||||
* Track the currently panel. Only allow a single panel to exist at a time.
|
||||
*/
|
||||
public static currentPanel: BWPanel | undefined;
|
||||
|
||||
public static readonly viewType = 'bwpanel';
|
||||
|
||||
private readonly _panel: vscode.WebviewPanel;
|
||||
private readonly _extensionUri: vscode.Uri;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
|
||||
public static createOrShow(extensionUri: vscode.Uri) {
|
||||
const column = vscode.window.activeTextEditor
|
||||
? vscode.window.activeTextEditor.viewColumn
|
||||
: undefined;
|
||||
|
||||
// If we already have a panel, show it.
|
||||
if (BWPanel.currentPanel) {
|
||||
BWPanel.currentPanel._panel.reveal(column);
|
||||
BWPanel.currentPanel._update();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, create a new panel.
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
BWPanel.viewType,
|
||||
'VSinder',
|
||||
column || vscode.ViewColumn.One,
|
||||
{
|
||||
// Enable javascript in the webview
|
||||
enableScripts: true,
|
||||
|
||||
// And restrict the webview to only loading content from our extension's `media` directory.
|
||||
localResourceRoots: [
|
||||
vscode.Uri.joinPath(extensionUri, 'media'),
|
||||
vscode.Uri.joinPath(extensionUri, 'out/compiled')
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
BWPanel.currentPanel = new BWPanel(panel, extensionUri);
|
||||
}
|
||||
|
||||
public static kill() {
|
||||
BWPanel.currentPanel?.dispose();
|
||||
BWPanel.currentPanel = undefined;
|
||||
}
|
||||
|
||||
public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
|
||||
BWPanel.currentPanel = new BWPanel(panel, extensionUri);
|
||||
}
|
||||
|
||||
private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
|
||||
this._panel = panel;
|
||||
this._extensionUri = extensionUri;
|
||||
|
||||
// Set the webview's initial html content
|
||||
this._update();
|
||||
|
||||
// Listen for when the panel is disposed
|
||||
// This happens when the user closes the panel or when the panel is closed programatically
|
||||
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
|
||||
|
||||
// // Handle messages from the webview
|
||||
// this._panel.webview.onDidReceiveMessage(
|
||||
// (message) => {
|
||||
// switch (message.command) {
|
||||
// case "alert":
|
||||
// vscode.window.showErrorMessage(message.text);
|
||||
// return;
|
||||
// }
|
||||
// },
|
||||
// null,
|
||||
// this._disposables
|
||||
// );
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
BWPanel.currentPanel = undefined;
|
||||
|
||||
// Clean up our resources
|
||||
this._panel.dispose();
|
||||
|
||||
while (this._disposables.length) {
|
||||
const x = this._disposables.pop();
|
||||
if (x) {
|
||||
x.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _update() {
|
||||
const webview = this._panel.webview;
|
||||
|
||||
this._panel.webview.html = this._getHtmlForWebview(webview);
|
||||
webview.onDidReceiveMessage(async (data) => {
|
||||
switch (data.type) {
|
||||
case 'onInfo': {
|
||||
if (!data.value) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInformationMessage(data.value);
|
||||
break;
|
||||
}
|
||||
case 'onError': {
|
||||
if (!data.value) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showErrorMessage(data.value);
|
||||
break;
|
||||
}
|
||||
// case "tokens": {
|
||||
// await Util.globalState.update(accessTokenKey, data.accessToken);
|
||||
// await Util.globalState.update(refreshTokenKey, data.refreshToken);
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _getHtmlForWebview(webview: vscode.Webview) {
|
||||
// // And the uri we use to load this script in the webview
|
||||
const scriptUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this._extensionUri, 'out/compiled', 'HelloWorld.js')
|
||||
);
|
||||
|
||||
// Uri to load styles into webview
|
||||
const stylesResetUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css')
|
||||
);
|
||||
const stylesMainUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css')
|
||||
);
|
||||
// const cssUri = webview.asWebviewUri(
|
||||
// vscode.Uri.joinPath(this._extensionUri, 'out', 'compiled/swiper.css')
|
||||
// );
|
||||
|
||||
// // Use a nonce to only allow specific scripts to be run
|
||||
const nonce = getNonce();
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!--
|
||||
Use a content security policy to only allow loading images from https or from our extension directory,
|
||||
and only allow scripts that have a specific nonce.
|
||||
-->
|
||||
<meta http-equiv="Content-Security-Policy" content="img-src https: data:; style-src 'unsafe-inline' ${webview.cspSource}; script-src 'nonce-${nonce}';">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="${stylesResetUri}" rel="stylesheet">
|
||||
<link href="${stylesMainUri}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
<script src=${scriptUri} nonce="${nonce}">
|
||||
</script>
|
||||
</html>`;
|
||||
}
|
||||
}
|
82
extension/bwcontest/src/SidebarProvider.ts
Normal file
82
extension/bwcontest/src/SidebarProvider.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { getNonce } from './getNonce';
|
||||
|
||||
export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
_view?: vscode.WebviewView;
|
||||
|
||||
constructor(private readonly _extensionUri: vscode.Uri) {}
|
||||
|
||||
public resolveWebviewView(webviewView: vscode.WebviewView) {
|
||||
this._view = webviewView;
|
||||
|
||||
webviewView.webview.options = {
|
||||
// Allow scripts in the webview
|
||||
enableScripts: true,
|
||||
|
||||
localResourceRoots: [this._extensionUri]
|
||||
};
|
||||
|
||||
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
|
||||
|
||||
webviewView.webview.onDidReceiveMessage(async (data) => {
|
||||
switch (data.type) {
|
||||
case 'onInfo': {
|
||||
if (!data.value) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInformationMessage(data.value);
|
||||
break;
|
||||
}
|
||||
case 'onError': {
|
||||
if (!data.value) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showErrorMessage(data.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private _getHtmlForWebview(webview: vscode.Webview) {
|
||||
const styleResetUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css')
|
||||
);
|
||||
const styleVSCodeUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css')
|
||||
);
|
||||
|
||||
const scriptUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this._extensionUri, 'out', 'compiled/sidebar.js')
|
||||
);
|
||||
const styleMainUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this._extensionUri, 'out', 'compiled/sidebar.css')
|
||||
);
|
||||
|
||||
// Use a nonce to only allow a specific script to be run.
|
||||
const nonce = getNonce();
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!--
|
||||
Use a content security policy to only allow loading images from https or from our extension directory,
|
||||
and only allow scripts that have a specific nonce.
|
||||
-->
|
||||
<meta http-equiv="Content-Security-Policy" content=" img-src https: data:; style-src 'unsafe-inline' ${webview.cspSource}; script-src 'nonce-${nonce}';">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="${styleResetUri}" rel="stylesheet">
|
||||
<link href="${styleVSCodeUri}" rel="stylesheet">
|
||||
<link href="${styleMainUri}" rel="stylesheet">
|
||||
<script nonce="${nonce}">
|
||||
const vscode = acquireVsCodeApi();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</html>`;
|
||||
}
|
||||
}
|
@ -1,13 +1,29 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { BWPanel } from './BWPanel';
|
||||
import { SidebarProvider } from './SidebarProvider';
|
||||
import { notDeepEqual } from 'assert';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('Congratulations, your extension "bwcontest" is now active!');
|
||||
const sidebarProvider = new SidebarProvider(context.extensionUri);
|
||||
context.subscriptions.push(
|
||||
vscode.window.registerWebviewViewProvider('bwcontest-sidebar', sidebarProvider)
|
||||
);
|
||||
|
||||
let disposable = vscode.commands.registerCommand('bwcontest.helloWorld', () => {
|
||||
vscode.window.showInformationMessage('Hello World from BWContest!');
|
||||
});
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('bwcontest.helloWorld', () => {
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('bwcontest.askQuestion', async () => {
|
||||
const answer = await vscode.window.showInformationMessage('How was your day?', 'good', 'bad');
|
||||
if (answer === 'bad') {
|
||||
vscode.window.showInformationMessage('Sorry to hear that');
|
||||
} else {
|
||||
console.log(answer);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
8
extension/bwcontest/src/getNonce.ts
Normal file
8
extension/bwcontest/src/getNonce.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export function getNonce() {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import { runTests } from '@vscode/test-electron';
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||
|
||||
// The path to test runner
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, './suite/index');
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests', err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
@ -1,15 +0,0 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../../extension';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
@ -1,38 +0,0 @@
|
||||
import * as path from 'path';
|
||||
import * as Mocha from 'mocha';
|
||||
import * as glob from 'glob';
|
||||
|
||||
export function run(): Promise<void> {
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha({
|
||||
ui: 'tdd',
|
||||
color: true
|
||||
});
|
||||
|
||||
const testsRoot = path.resolve(__dirname, '..');
|
||||
|
||||
return new Promise((c, e) => {
|
||||
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
// Add files to the test suite
|
||||
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
|
||||
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run(failures => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
@ -3,9 +3,7 @@
|
||||
"module": "commonjs",
|
||||
"target": "ES2020",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"ES2020"
|
||||
],
|
||||
"lib": ["ES2020"],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": true /* enable all strict type-checking options */
|
||||
@ -13,5 +11,6 @@
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
}
|
||||
},
|
||||
"exclude": ["webviews"]
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<h1>Test!</h1>
|
61
extension/bwcontest/webviews/components/Sidebar.svelte
Normal file
61
extension/bwcontest/webviews/components/Sidebar.svelte
Normal file
@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
function postMessage(message: any) {
|
||||
vscode.postMessage(message);
|
||||
}
|
||||
|
||||
let teamname: HTMLInputElement;
|
||||
let password: HTMLInputElement;
|
||||
|
||||
let sessionToken: string | undefined;
|
||||
|
||||
async function onLogin() {
|
||||
try {
|
||||
const res = await fetch("http://localhost:5173/api/team/login", {method: "POST", headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
|
||||
teamname: teamname.value, password: password.value
|
||||
})});
|
||||
if (res.status !== 200) {
|
||||
postMessage({type: 'onError', value: 'Error logging in'});
|
||||
return;
|
||||
}
|
||||
const data = await res.json();
|
||||
if (data.success === false) {
|
||||
postMessage({type: 'onError', value: data.message ?? "Unknown error logging in"});
|
||||
return;
|
||||
}
|
||||
sessionToken = data.token;
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function onLogout() {
|
||||
const res = await fetch("http://localhost:5173/api/team/logout", {method: "POST", headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({
|
||||
token: sessionToken,
|
||||
})})
|
||||
if (res.status !== 200) {
|
||||
postMessage({type: 'onError', value: 'Error logging out'});
|
||||
return;
|
||||
}
|
||||
const data = await res.json();
|
||||
if (data.success === true) {
|
||||
postMessage({type: 'onInfo', value: 'BWContest: Logged out'});
|
||||
sessionToken = undefined;
|
||||
} else {
|
||||
postMessage({type: 'onError', value: 'Log out unsuccessful'});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>Contest</h1>
|
||||
|
||||
{#if sessionToken === undefined}
|
||||
<label for="teamname">Team Name</label>
|
||||
<input bind:this={teamname} id="teamname" type="text"/>
|
||||
|
||||
<label for="password">Password</label>
|
||||
<input bind:this={password} id="password" type="password"/>
|
||||
|
||||
<button on:click={onLogin}>Login</button>
|
||||
{:else}
|
||||
<button on:click={onLogout}>Logout</button>
|
||||
{/if}
|
9
extension/bwcontest/webviews/global.d.ts
vendored
Normal file
9
extension/bwcontest/webviews/global.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/// <reference types="svelte" />
|
||||
|
||||
type VSCode = {
|
||||
postMessage(message: any): void;
|
||||
getState(): any;
|
||||
setState(state: any): void;
|
||||
};
|
||||
|
||||
declare const vscode: VSCode;
|
7
extension/bwcontest/webviews/pages/HelloWorld.ts
Normal file
7
extension/bwcontest/webviews/pages/HelloWorld.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import App from '../components/HelloWorld.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body
|
||||
});
|
||||
|
||||
export default app;
|
7
extension/bwcontest/webviews/pages/sidebar.ts
Normal file
7
extension/bwcontest/webviews/pages/sidebar.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import App from '../components/Sidebar.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body
|
||||
});
|
||||
|
||||
export default app;
|
6
extension/bwcontest/webviews/tsconfig.json
Normal file
6
extension/bwcontest/webviews/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"include": ["./**/*"],
|
||||
"exclude": ["../node_modules/*"],
|
||||
"compilerOptions": { "strict": true }
|
||||
}
|
@ -57,6 +57,15 @@ model Team {
|
||||
Submission Submission[]
|
||||
contests Contest[] @relation("TeamContestRelation")
|
||||
password String
|
||||
activeTeam ActiveTeam?
|
||||
}
|
||||
|
||||
model ActiveTeam {
|
||||
id Int @id @default(autoincrement())
|
||||
teamId Int @unique
|
||||
team Team @relation(fields: [teamId], references: [id])
|
||||
sessionToken String? @unique
|
||||
sessionCreatedAt DateTime?
|
||||
}
|
||||
|
||||
model Contest {
|
||||
|
@ -21,6 +21,17 @@ async function removeExpiredSessions(userId: number) {
|
||||
}
|
||||
|
||||
export const handle = (async ({ event, resolve }) => {
|
||||
if (event.request.method === 'OPTIONS') {
|
||||
return new Response('ok', {
|
||||
headers: {
|
||||
'Access-Control-Allow-Credentials': 'true',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (event.url.pathname.startsWith('/login')) {
|
||||
if (event.cookies.get('token')) {
|
||||
const session = await db.session.findUnique({ where: { token: event.cookies.get('token') } });
|
||||
|
31
web/src/routes/api/team/login/+server.ts
Normal file
31
web/src/routes/api/team/login/+server.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { z } from 'zod';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/prisma';
|
||||
import * as UUID from 'uuid';
|
||||
|
||||
const loginPostData = z
|
||||
.object({
|
||||
teamname: z.string(),
|
||||
password: z.string()
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const POST = (async ({ request }) => {
|
||||
const data = loginPostData.safeParse(await request.json());
|
||||
if (!data.success) {
|
||||
throw error(400);
|
||||
}
|
||||
const team = await db.team.findUnique({
|
||||
where: { name: data.data.teamname },
|
||||
include: { activeTeam: true }
|
||||
});
|
||||
if (!team || !team.activeTeam || team.password !== data.data.password) {
|
||||
return json({ success: false, message: 'Invalid login' });
|
||||
}
|
||||
const activeTeam = await db.activeTeam.update({
|
||||
where: { id: team.activeTeam.id },
|
||||
data: { sessionToken: UUID.v4(), sessionCreatedAt: new Date() }
|
||||
});
|
||||
return json({ success: true, token: activeTeam.sessionToken });
|
||||
}) satisfies RequestHandler;
|
22
web/src/routes/api/team/logout/+server.ts
Normal file
22
web/src/routes/api/team/logout/+server.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/prisma';
|
||||
|
||||
const logoutPostData = z
|
||||
.object({
|
||||
token: z.string()
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const POST = (async ({ request }) => {
|
||||
const data = logoutPostData.safeParse(await request.json());
|
||||
if (!data.success) {
|
||||
throw error(400);
|
||||
}
|
||||
await db.activeTeam.update({
|
||||
where: { sessionToken: data.data.token },
|
||||
data: { sessionToken: null, sessionCreatedAt: null }
|
||||
});
|
||||
return json({ success: true });
|
||||
}) satisfies RequestHandler;
|
Loading…
Reference in New Issue
Block a user