diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b51ffb9..ac4b6f05a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ - Feat: Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`) - Feat: クライアントを起動している間、デバイスの画面が自動でオフになるのを防ぐオプションを追加 - Feat: 新しい実績を追加 +- Feat: リモートサーバーのAPIを叩く用の関数を追加(`Mk:apiExternal`) - Enhance: ノート詳細ページでリノート一覧、リアクション一覧タブを追加 - ノートのメニューからは当該項目は消えました - Enhance: センシティブなメディアを目立たせる設定を追加 diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 8aed5797e..8093335a2 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -5,8 +5,8 @@ // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する -import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api.js'; -export { pendingApiRequestsCount, api, apiGet }; +import { pendingApiRequestsCount, api, apiExternal, apiGet } from '@/scripts/api.js'; +export { pendingApiRequestsCount, api, apiExternal, apiGet }; import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue'; import { EventEmitter } from 'eventemitter3'; import insertTextAtCursor from 'insert-text-at-cursor'; diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts index 9f60e52ce..f049a51b9 100644 --- a/packages/frontend/src/scripts/aiscript/api.ts +++ b/packages/frontend/src/scripts/aiscript/api.ts @@ -48,6 +48,16 @@ export function createAiScriptEnv(opts) { return values.ERROR('request_failed', utils.jsToVal(err)); }); }), + 'Mk:apiExternal': values.FN_NATIVE(async ([host, ep, param, token]) => { + utils.assertString(host); + utils.assertString(ep); + if (token) utils.assertString(token); + return os.apiExternal(host.value, ep.value, utils.valToJs(param), token?.value).then(res => { + return utils.jsToVal(res); + }, err => { + return values.ERROR('request_failed', utils.jsToVal(err)); + }); + }), 'Mk:save': values.FN_NATIVE(([key, value]) => { utils.assertString(key); miLocalStorage.setItem(`aiscript:${opts.storageKey}:${key.value}`, JSON.stringify(utils.valToJs(value))); diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/api.ts index 9259c8801..080977e5e 100644 --- a/packages/frontend/src/scripts/api.ts +++ b/packages/frontend/src/scripts/api.ts @@ -11,6 +11,7 @@ export const pendingApiRequestsCount = ref(0); // Implements Misskey.api.ApiClient.request export function api(endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise { + if (endpoint.includes('://')) throw new Error('invalid endpoint'); pendingApiRequestsCount.value++; const onFinally = () => { @@ -23,7 +24,50 @@ export function api -1 ? endpoint : `${apiUrl}/${endpoint}`, { + window.fetch(`${apiUrl}/${endpoint}`, { + method: 'POST', + body: JSON.stringify(data), + credentials: 'omit', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + }, + signal, + }).then(async (res) => { + const body = res.status === 204 ? null : await res.json(); + + if (res.status === 200) { + resolve(body); + } else if (res.status === 204) { + resolve(); + } else { + reject(body.error); + } + }).catch(reject); + }); + + promise.then(onFinally, onFinally); + + return promise; +} + +export function apiExternal(hostUrl: string, endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise { + if (!/^https?:\/\//.test(hostUrl)) throw new Error('invalid host name'); + if (endpoint.includes('://')) throw new Error('invalid endpoint'); + pendingApiRequestsCount.value++; + + const onFinally = () => { + pendingApiRequestsCount.value--; + }; + + const promise = new Promise((resolve, reject) => { + // Append a credential + (data as any).i = token; + + const fullUrl = (hostUrl.slice(-1) === '/' ? hostUrl.slice(0, -1) : hostUrl) + + '/api/' + (endpoint.slice(0, 1) === '/' ? endpoint.slice(1) : endpoint); + // Send request + window.fetch(fullUrl, { method: 'POST', body: JSON.stringify(data), credentials: 'omit',