diff --git a/build/prepare.js b/build/prepare.js index 9cfb1e29..6d59ad0b 100644 --- a/build/prepare.js +++ b/build/prepare.js @@ -62,11 +62,11 @@ const UIConfig = { exclude: [ 'packages/ui-default/public', ], - include: [ - 'packages/ui-default/**/*.ts', - 'packages/**/public/**/*.ts', - 'plugins/**/public/**/*.ts', - ], + include: ['ts', 'tsx'].flatMap((i) => [ + `packages/ui-default/**/*.${i}`, + `packages/**/public/**/*.${i}`, + `plugins/**/public/**/*.${i}`, + ]), compilerOptions: { experimentalDecorators: true, esModuleInterop: true, diff --git a/install/install.ts b/install/install.ts index ad291624..c8e9a328 100644 --- a/install/install.ts +++ b/install/install.ts @@ -4,6 +4,8 @@ import { execSync, ExecSyncOptions } from 'child_process'; import { existsSync, readFileSync, writeFileSync } from 'fs'; import net from 'net'; +import os from 'os'; +import path from 'path'; const exec = (command: string, args?: ExecSyncOptions) => { try { @@ -217,6 +219,8 @@ function rollbackResolveField() { return true; } +const tmpFile = path.join(os.tmpdir(), `${Math.random().toString()}.js`); + const Steps = () => [ { init: 'install.preparing', @@ -315,12 +319,12 @@ connect-timeout = 10`); operations: [ 'pm2 start mongod', () => sleep(3000), - () => writeFileSync('/tmp/createUser.js', `db.createUser(${JSON.stringify({ + () => writeFileSync(tmpFile, `db.createUser(${JSON.stringify({ user: 'hydro', pwd: password, roles: [{ role: 'readWrite', db: 'hydro' }], })})`), - ['mongo 127.0.0.1:27017/hydro /tmp/createUser.js', { retry: true }], + [`mongo 127.0.0.1:27017/hydro ${tmpFile}`, { retry: true }], () => writeFileSync(`${process.env.HOME}/.hydro/config.json`, JSON.stringify({ host: '127.0.0.1', port: 27017, diff --git a/package.json b/package.json index 5733a1cb..40e7c89b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@types/autocannon": "^7.9.0", "@types/cross-spawn": "^6.0.2", "@types/mocha": "^10.0.0", - "@types/node": "^18.11.3", + "@types/node": "^18.11.9", "@types/semver": "^7.3.13", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^5.43.0", @@ -61,7 +61,7 @@ "ora": "^6.1.2", "semver": "^7.3.8", "supertest": "^6.3.1", - "typescript": "4.8.4" + "typescript": "4.9.3" }, "packageManager": "yarn@3.2.3" } diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 8ccbc3e2..68678222 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -12,11 +12,11 @@ }, "preferUnplugged": true, "dependencies": { - "@aws-sdk/client-s3": "^3.210.0", - "@aws-sdk/lib-storage": "^3.210.0", - "@aws-sdk/middleware-endpoint": "^3.208.0", - "@aws-sdk/s3-presigned-post": "^3.210.0", - "@aws-sdk/s3-request-presigner": "^3.210.0", + "@aws-sdk/client-s3": "^3.212.0", + "@aws-sdk/lib-storage": "^3.212.0", + "@aws-sdk/middleware-endpoint": "^3.212.0", + "@aws-sdk/s3-presigned-post": "^3.212.0", + "@aws-sdk/s3-request-presigner": "^3.212.0", "@graphql-tools/schema": "^8.5.1", "@hydrooj/utils": "workspace:*", "adm-zip": "0.5.5", diff --git a/packages/hydrooj/src/handler/misc.ts b/packages/hydrooj/src/handler/misc.ts index 9db61434..a909931c 100644 --- a/packages/hydrooj/src/handler/misc.ts +++ b/packages/hydrooj/src/handler/misc.ts @@ -3,7 +3,7 @@ import { statSync } from 'fs'; import { pick } from 'lodash'; import { lookup } from 'mime-types'; import { - BadRequestError, ForbiddenError, ValidationError, + BadRequestError, ForbiddenError, NotFoundError, ValidationError, } from '../error'; import { PRIV } from '../model/builtin'; import * as oplog from '../model/oplog'; @@ -96,6 +96,7 @@ export class FSDownloadHandler extends Handler { @param('noDisposition', Types.Boolean) async get(domainId: string, uid: number, filename: string, noDisposition = false) { const targetUser = await user.getById('system', uid); + if (!targetUser) throw new NotFoundError(uid); if (this.user._id !== uid && !targetUser.hasPriv(PRIV.PRIV_CREATE_FILE)) throw new ForbiddenError('Access denied'); this.response.addHeader('Cache-Control', 'public'); const target = `user/${uid}/${filename}`; diff --git a/packages/ui-default/build/config/webpack.ts b/packages/ui-default/build/config/webpack.ts index 76c10382..3e327f56 100644 --- a/packages/ui-default/build/config/webpack.ts +++ b/packages/ui-default/build/config/webpack.ts @@ -248,7 +248,10 @@ export default function (env: { watch?: boolean, production?: boolean, measure?: patterns: [ { from: root('static') }, { from: root('components/navigation/nav-logo-small_dark.png'), to: 'components/navigation/nav-logo-small_dark.png' }, + { from: root(`${dirname(require.resolve('streamsaver/package.json'))}/mitm.html`), to: 'streamsaver/mitm.html' }, + { from: root(`${dirname(require.resolve('streamsaver/package.json'))}/sw.js`), to: 'streamsaver/sw.js' }, { from: root(`${dirname(require.resolve('vditor/package.json'))}/dist`), to: 'vditor/dist' }, + { from: root(`${dirname(require.resolve('graphiql/package.json'))}/graphiql.min.css`), to: 'graphiql.min.css' }, { from: `${dirname(require.resolve('monaco-themes/package.json'))}/themes`, to: 'monaco/themes/' }, ], }), @@ -268,13 +271,6 @@ export default function (env: { watch?: boolean, production?: boolean, measure?: id: 'vs/language/yaml/yamlWorker', entry: require.resolve('monaco-yaml/yaml.worker.js'), }, - }, { - label: 'graphql', - entry: require.resolve('monaco-graphql/esm/monaco.contribution.js'), - worker: { - id: 'vs/language/graphql/graphqlWorker', - entry: require.resolve('monaco-graphql/esm/graphql.worker.js'), - }, }], }), ...env.measure ? [new BundleAnalyzerPlugin({ analyzerPort: 'auto' })] : [], diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index a5c6540a..18082d9e 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -52,7 +52,7 @@ "fancy-log": "^2.0.0", "flatpickr": "^4.6.13", "friendly-errors-webpack-plugin": "^1.7.0", - "graphiql": "1.8.9", + "graphiql": "2.1.0", "gulp": "^4.0.2", "gulp-iconfont": "^11.0.1", "gulp-if": "^3.0.0", @@ -63,12 +63,11 @@ "jquery.easing": "^1.4.1", "jquery.transit": "^0.9.12", "matchmedia-polyfill": "^0.3.2", - "mini-css-extract-plugin": "^2.6.1", + "mini-css-extract-plugin": "^2.7.0", "moment": "^2.29.4", "monaco-editor": "0.33.0", "monaco-editor-nls": "^2.0.0", "monaco-editor-webpack-plugin": "^7.0.1", - "monaco-graphql": "^1.1.6", "monaco-themes": "^0.4.3", "monaco-yaml": "^4.0.2", "nanoid": "^4.0.0", diff --git a/packages/ui-default/pages/api.page.tsx b/packages/ui-default/pages/api.page.tsx index 6524c284..9b7355a5 100644 --- a/packages/ui-default/pages/api.page.tsx +++ b/packages/ui-default/pages/api.page.tsx @@ -1,9 +1,4 @@ -import 'graphiql/src/css/doc-explorer.css'; - -import type { GraphQLSchema } from 'graphql'; -import { load as loadYaml } from 'js-yaml'; -import type * as monaco from 'monaco-editor'; -import type { SchemaConfig } from 'monaco-graphql/src/typings'; +import { pick } from 'lodash'; import React from 'react'; import { createRoot } from 'react-dom/client'; import { NamedPage } from 'vj/misc/Page'; @@ -18,154 +13,17 @@ query Example( uname } }`; -const variablesString = 'name: Hydro'; -const resultsString = '{}'; -const schemaSdlString = `\ -""" -Loading schema... -"""`; - -const page = new NamedPage('api', async () => { - const [{ DocExplorer }, { buildClientSchema, getIntrospectionQuery, printSchema }, { load }] = await Promise.all([ - import('graphiql/esm/components/DocExplorer'), - import('graphql/utilities'), - import('vj/components/monaco/loader'), - ]); - const { monaco } = await load(['graphql', 'json', 'yaml']); - const { initializeMode } = await import('monaco-graphql/esm/initializeMode'); - - const variablesModel = monaco.editor.createModel( - variablesString, - 'yaml', - monaco.Uri.file('/1/variables.yaml'), - ); - const variablesEditor = monaco.editor.create( - document.getElementById('variables') as HTMLElement, - { - model: variablesModel, - language: 'yaml', - formatOnPaste: true, - formatOnType: true, - comments: { - insertSpace: true, - ignoreEmptyLines: true, - }, - }, - ); - const operationModel = monaco.editor.createModel( - defaultQuery, - 'graphql', - monaco.Uri.file('/1/operation.graphql'), +export default new NamedPage('api', async () => { + const { GraphiQL } = await import('graphiql'); + GraphiQL.Logo = function () { + return

; + } as any; + createRoot(document.getElementById('main')!).render( + request.post('/api', body, pick(opts, 'headers'))} + defaultQuery={defaultQuery} + variables='{"name": "Hydro"}' + />, ); - const operationEditor = monaco.editor.create( - document.getElementById('operation') as HTMLElement, - { - model: operationModel, - formatOnPaste: true, - formatOnType: true, - folding: true, - language: 'graphql', - }, - ); - - const schemaModel = monaco.editor.createModel( - schemaSdlString, - 'graphql', - monaco.Uri.file('/1/schema.graphqls'), - ); - const schemaEditor = monaco.editor.create( - document.getElementById('schema-sdl') as HTMLElement, - { - model: schemaModel, - formatOnPaste: true, - formatOnType: true, - folding: true, - readOnly: true, - language: 'graphql', - }, - ); - - const resultsModel = monaco.editor.createModel( - resultsString, - 'json', - monaco.Uri.file('/1/results.json'), - ); - const resultsEditor = monaco.editor.create( - document.getElementById('results') as HTMLElement, - { - model: resultsModel, - language: 'json', - wordWrap: 'on', - readOnly: true, - showFoldingControls: 'always', - }, - ); - - const monacoGraphQLAPI = initializeMode({ - formattingOptions: { - prettierConfig: { - printWidth: 120, - }, - }, - }); - - const toolbar = $('#toolbar')!; - toolbar.html(); - const executeOpButton = $(''); - toolbar.append(executeOpButton); - - const operationUri = operationModel.uri.toString(); - let schema: SchemaConfig; - let clientSchema: GraphQLSchema; - try { - const { data } = await request.post('/api?schema', { - query: getIntrospectionQuery(), - operationName: 'IntrospectionQuery', - }); - clientSchema = buildClientSchema(data); - schema = { - introspectionJSON: data, - documentString: printSchema(clientSchema), - uri: monaco.Uri.parse('/api?schema').toString(), - }; - } catch { - schemaModel.setValue('"""\nFailed to load schema.\n"""'); - } - monacoGraphQLAPI.setSchemaConfig([ - { ...schema, fileMatch: [operationUri, schemaModel.uri.toString()] }, - ]); - schemaEditor.setValue(schema.documentString || ''); - - const operationHandler = async () => { - try { - resultsEditor.setValue(JSON.stringify({ message: 'Executing...' }, null, 2)); - const result = await request.post('/api?schema', { - query: operationEditor.getValue(), - variables: loadYaml(variablesEditor.getValue()), - }); - resultsEditor.setValue(JSON.stringify(result, null, 2)); - } catch (err) { - if (err instanceof Error) { - resultsEditor.setValue(err.toString()); - } - } - }; - executeOpButton.on('click', operationHandler); - executeOpButton.on('touchend', operationHandler); - const opAction: monaco.editor.IActionDescriptor = { - id: 'graphql-run', - label: 'Run Operation', - contextMenuOrder: 0, - contextMenuGroupId: 'graphql', - keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter], - run: operationHandler, - }; - operationEditor.addAction(opAction); - variablesEditor.addAction(opAction); - resultsEditor.addAction(opAction); - - createRoot(document.getElementById('docs')).render(); }); - -export default page; diff --git a/packages/ui-default/static/streamsaver/mitm.html b/packages/ui-default/static/streamsaver/mitm.html deleted file mode 100644 index 238ba59c..00000000 --- a/packages/ui-default/static/streamsaver/mitm.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/ui-default/static/streamsaver/sw.js b/packages/ui-default/static/streamsaver/sw.js deleted file mode 100644 index b9476fb1..00000000 --- a/packages/ui-default/static/streamsaver/sw.js +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-disable */ -self.addEventListener('install', () => { - self.skipWaiting(); -}); -self.addEventListener('activate', (event) => { - event.waitUntil(self.clients.claim()); -}); -const map = new Map(); -self.onmessage = (event) => { - if (event.data === 'ping') { - return; - } - const { data } = event; - const downloadUrl = data.url || `${self.registration.scope + Math.random()}/${typeof data === 'string' ? data : data.filename}`; - const port = event.ports[0]; - const metadata = new Array(3); - metadata[1] = data; - metadata[2] = port; - if (event.data.readableStream) { - metadata[0] = event.data.readableStream; - } else if (event.data.transferringReadable) { - port.onmessage = (evt) => { - port.onmessage = null; - metadata[0] = evt.data.readableStream; - }; - } else metadata[0] = createStream(port); - map.set(downloadUrl, metadata); - port.postMessage({ download: downloadUrl }); -}; -function createStream(port) { - return new ReadableStream({ - start(controller) { - port.onmessage = ({ data }) => { - if (data === 'end') return controller.close(); - if (data === 'abort') { - controller.error('Aborted the download'); - return; - } - controller.enqueue(data); - }; - }, - cancel() { - console.log('user aborted'); - }, - }); -} -self.onfetch = (event) => { - const { url } = event.request; - if (url.endsWith('/ping')) return event.respondWith(new Response('pong')); - const hijacke = map.get(url); - if (!hijacke) return null; - const [stream, data, port] = hijacke; - map.delete(url); - const responseHeaders = new Headers({ - 'Content-Type': 'application/octet-stream; charset=utf-8', - 'Content-Security-Policy': "default-src 'none'", - 'X-Content-Security-Policy': "default-src 'none'", - 'X-WebKit-CSP': "default-src 'none'", - 'X-XSS-Protection': '1; mode=block', - }); - const headers = new Headers(data.headers || {}); - if (headers.has('Content-Length')) { - responseHeaders.set('Content-Length', headers.get('Content-Length')); - } - if (headers.has('Content-Disposition')) { - responseHeaders.set('Content-Disposition', headers.get('Content-Disposition')); - } - if (data.size) { - console.warn('Depricated'); - responseHeaders.set('Content-Length', data.size); - } - let fileName = typeof data === 'string' ? data : data.filename; - if (fileName) { - console.warn('Depricated'); - // Make filename RFC5987 compatible - fileName = encodeURIComponent(fileName).replace(/['()]/g, escape).replace(/\*/g, '%2A'); - responseHeaders.set('Content-Disposition', `attachment; filename*=UTF-8''${fileName}`); - } - event.respondWith(new Response(stream, { headers: responseHeaders })); - port.postMessage({ debug: 'Download started' }); -}; diff --git a/packages/ui-default/templates/api.html b/packages/ui-default/templates/api.html index fb7f1215..82129fae 100644 --- a/packages/ui-default/templates/api.html +++ b/packages/ui-default/templates/api.html @@ -1,43 +1,10 @@ {% extends "layout/basic.html" %} {% block content %} -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {% endblock %} diff --git a/packages/utils/package.json b/packages/utils/package.json index 2f9125c2..5cea6257 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -16,7 +16,7 @@ "mongodb": "^3.7.3", "reggol": "^1.3.1", "source-map-support": "^0.5.21", - "systeminformation": "^5.12.14" + "systeminformation": "^5.12.15" }, "devDependencies": { "@types/fs-extra": "^9.0.13",