diff --git a/packages/hydrooj/src/service/server.ts b/packages/hydrooj/src/service/server.ts index cad5626b8..e86a69b1a 100644 --- a/packages/hydrooj/src/service/server.ts +++ b/packages/hydrooj/src/service/server.ts @@ -144,8 +144,18 @@ export async function apply(ctx: Context) { }); if (process.env.HYDRO_CLI) return; ctx.inject(['server'], ({ server, on }) => { + let endpoint = builtinConfig.file.endPoint; + if (builtinConfig.file.type === 's3' && !builtinConfig.file.pathStyle) { + try { + const parsed = new URL(builtinConfig.file.endPoint); + parsed.hostname = `${builtinConfig.file.bucket}.${parsed.hostname}`; + endpoint = parsed.toString(); + } catch (e) { + logger.warn('Failed to parse file endpoint'); + } + } const proxyMiddleware = proxy('/fs', { - target: builtinConfig.file.endPoint, + target: endpoint, changeOrigin: true, rewrite: (p) => p.replace('/fs', ''), }); diff --git a/packages/ui-default/build/config/webpack.ts b/packages/ui-default/build/config/webpack.ts index 74d5e5de7..ab02df446 100644 --- a/packages/ui-default/build/config/webpack.ts +++ b/packages/ui-default/build/config/webpack.ts @@ -23,7 +23,7 @@ const { list, targets, } = compat({ - targets: '> 0.5%, chrome 80', + targets: '> 1%, chrome 70, firefox 60, safari 16, ios_saf 16, not ie 11, not op_mini all', modules: [ 'core-js/stable', ], @@ -290,7 +290,7 @@ export default async function (env: { watch?: boolean, production?: boolean, mea rewriteSources: (source) => source.replace('@hydrooj/ui-default/../../node_modules/', ''), }, release: (process.env.CI && isNew) ? { - name: version, + name: `hydro-web@${version}`, uploadLegacySourcemaps: root('public'), } : {}, }), diff --git a/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx b/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx index 9afc9b171..c9322d3af 100644 --- a/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx +++ b/packages/ui-default/components/autocomplete/components/CustomSelectAutoComplete.tsx @@ -11,10 +11,7 @@ const CustomSelectAutoComplete = React.forwardRef((props, ref) => ( ref={ref as any} fetchItems={(keys) => props.data.filter((i) => (i._id ? keys.includes(i._id) : keys.includes(i)))} - queryItems={(query) => props.data.filter((i) => (i.name - ? i.name.toLowerCase().match(query.toLowerCase()) - : i.toString().toLowerCase().match(query.toLowerCase()) - ))} + queryItems={(query) => props.data.filter((i) => (i.name || i).toString().toLowerCase().includes(query.toLowerCase()))} itemText={(item) => `${item.name || item}`} itemKey={(item) => `${item._id?.toString() || item.name || item}`} renderItem={(item) => `${item.name || item}`} diff --git a/packages/ui-default/entry.js b/packages/ui-default/entry.js index c13f762bb..5acfa7e0a 100644 --- a/packages/ui-default/entry.js +++ b/packages/ui-default/entry.js @@ -31,10 +31,12 @@ console.log( window.UiContext = JSON.parse(window.UiContext); window.UserContext = JSON.parse(window.UserContext); if (process.env.NODE_ENV === 'production' && !UiContext.sentry_disable) { - window.captureException = (e) => Sentry.captureException(e); + window.captureException = (e) => { + if (!e.isUserFacingError) Sentry.captureException(e); + }; Sentry.init({ dsn: UiContext.sentry_dsn || 'https://2f95d53751e08c74c1af1c4b93ccaff7@sentry.hydro.ac/2', - release: process.env.VERSION, + release: `hydro-web@${process.env.VERSION}`, integrations: [ Sentry.browserTracingIntegration(), Sentry.replayIntegration(), diff --git a/packages/ui-default/polyfill.ts b/packages/ui-default/polyfill.ts index d635f0a1c..f498b7eb6 100644 --- a/packages/ui-default/polyfill.ts +++ b/packages/ui-default/polyfill.ts @@ -17,6 +17,18 @@ if (window.MediaQueryList && !MediaQueryList.prototype.addEventListener) { MediaQueryList.prototype.addListener(listener); }; } +if (typeof window['WeakRef'] === 'undefined') { + // @ts-ignore + window.WeakRef = (function (wm) { + function WeakRef(target) { + wm.set(this, target); + } + WeakRef.prototype.deref = function () { + return wm.get(this); + }; + return WeakRef; + }(new WeakMap())); +} if (!(window.matchMedia('all').addListener || window.matchMedia('all').addEventListener)) { const localMatchMedia = window.matchMedia; const hasMediaQueries = localMatchMedia('only all').matches; diff --git a/packages/ui-default/utils/base.ts b/packages/ui-default/utils/base.ts index d174c012b..c2b9e3169 100644 --- a/packages/ui-default/utils/base.ts +++ b/packages/ui-default/utils/base.ts @@ -82,7 +82,8 @@ export const zIndexManager = { }; export const request = { - async ajax(options: Record) { + ajax(options: Record) { + const stack = new Error().stack; return new Promise((resolve, reject) => { $ .ajax({ @@ -96,9 +97,12 @@ export const request = { if (textStatus === 'abort') { const err = new Error(i18n('Aborted')) as any; err.aborted = true; + err.isUserFacingError = true; reject(err); } else if (jqXHR.readyState === 0) { - reject(new Error(i18n('Network error'))); + const err = new Error(i18n('Network error')) as any; + err.isUserFacingError = true; + reject(err); } else if (typeof jqXHR.responseJSON === 'object' && jqXHR.responseJSON.error) { const { error } = jqXHR.responseJSON; if (error.params) { @@ -117,6 +121,9 @@ export const request = { } }) .done(resolve); + }).catch((e: Error) => { + e.stack = stack; + throw e; }); }, diff --git a/packages/utils/lib/common.ts b/packages/utils/lib/common.ts index 657dcbd68..9d7d68ba8 100644 --- a/packages/utils/lib/common.ts +++ b/packages/utils/lib/common.ts @@ -22,11 +22,14 @@ declare global { const defaultDict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; -String.random = function random(digit = 32, dict = defaultDict) { +export function randomstring(digit = 32, dict = defaultDict) { let str = ''; for (let i = 1; i <= digit; i++) str += dict[Math.floor(Math.random() * dict.length)]; return str; -}; +} +try { + String.random = randomstring; +} catch (e) { } // Cannot add property random, object is not extensible String.prototype.format ||= function formatStr(...args) { let result = this;