summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2025-05-06 23:19:23 -0400
committerHazelnoot <acomputerdog@gmail.com>2025-05-08 11:23:20 -0400
commitb2ea03383cd53ac213c4dee6dbd086ab6f54daa7 (patch)
treed2fd601cb1c24d3e290aa4cef53c6d6df99953c2
parentfix "cannot use 'in' operator" error (diff)
downloadsharkey-b2ea03383cd53ac213c4dee6dbd086ab6f54daa7.tar.gz
sharkey-b2ea03383cd53ac213c4dee6dbd086ab6f54daa7.tar.bz2
sharkey-b2ea03383cd53ac213c4dee6dbd086ab6f54daa7.zip
implement '/v1/apps/verify_credentials'
-rw-r--r--packages/backend/src/server/api/AuthenticateService.ts2
-rw-r--r--packages/backend/src/server/api/endpoint-list.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/app/current.ts73
-rw-r--r--packages/backend/src/server/api/mastodon/MastodonConverters.ts12
-rw-r--r--packages/backend/src/server/api/mastodon/endpoints/apps.ts9
-rw-r--r--packages/megalodon/src/index.ts2
-rw-r--r--packages/megalodon/src/mastodon/entities/application.ts3
-rw-r--r--packages/megalodon/src/misskey.ts9
-rw-r--r--packages/megalodon/src/misskey/entities/app.ts2
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md4
-rw-r--r--packages/misskey-js/src/autogen/apiClientJSDoc.ts11
-rw-r--r--packages/misskey-js/src/autogen/endpoint.ts2
-rw-r--r--packages/misskey-js/src/autogen/entities.ts1
-rw-r--r--packages/misskey-js/src/autogen/types.ts61
14 files changed, 183 insertions, 9 deletions
diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts
index 601618553e..397626c49d 100644
--- a/packages/backend/src/server/api/AuthenticateService.ts
+++ b/packages/backend/src/server/api/AuthenticateService.ts
@@ -84,6 +84,8 @@ export class AuthenticateService implements OnApplicationShutdown {
return [user, {
id: accessToken.id,
permission: app.permission,
+ appId: app.id,
+ app,
} as MiAccessToken];
} else {
return [user, accessToken];
diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts
index 1c5a781fd9..a78c3e9ae6 100644
--- a/packages/backend/src/server/api/endpoint-list.ts
+++ b/packages/backend/src/server/api/endpoint-list.ts
@@ -128,6 +128,7 @@ export * as 'antennas/update' from './endpoints/antennas/update.js';
export * as 'ap/get' from './endpoints/ap/get.js';
export * as 'ap/show' from './endpoints/ap/show.js';
export * as 'app/create' from './endpoints/app/create.js';
+export * as 'app/current' from './endpoints/app/current.js';
export * as 'app/show' from './endpoints/app/show.js';
export * as 'auth/accept' from './endpoints/auth/accept.js';
export * as 'auth/session/generate' from './endpoints/auth/session/generate.js';
diff --git a/packages/backend/src/server/api/endpoints/app/current.ts b/packages/backend/src/server/api/endpoints/app/current.ts
new file mode 100644
index 0000000000..39b5ef347c
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/app/current.ts
@@ -0,0 +1,73 @@
+/*
+ * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { AppsRepository } from '@/models/_.js';
+import { AppEntityService } from '@/core/entities/AppEntityService.js';
+import { DI } from '@/di-symbols.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['app'],
+
+ errors: {
+ credentialRequired: {
+ message: 'Credential required.',
+ code: 'CREDENTIAL_REQUIRED',
+ id: '1384574d-a912-4b81-8601-c7b1c4085df1',
+ httpStatusCode: 401,
+ },
+ noAppLogin: {
+ message: 'Not logged in with an app.',
+ code: 'NO_APP_LOGIN',
+ id: '339a4ad2-48c3-47fc-bd9d-2408f05120f8',
+ },
+ },
+
+ res: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'App',
+ },
+
+ // 10 calls per 5 seconds
+ limit: {
+ duration: 1000 * 5,
+ max: 10,
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {},
+ required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ @Inject(DI.appsRepository)
+ private appsRepository: AppsRepository,
+
+ private appEntityService: AppEntityService,
+ ) {
+ super(meta, paramDef, async (_, user, token) => {
+ if (!user) {
+ throw new ApiError(meta.errors.credentialRequired);
+ }
+ if (!token || !token.appId) {
+ throw new ApiError(meta.errors.noAppLogin);
+ }
+
+ const app = token.app ?? await this.appsRepository.findOneByOrFail({ id: token.appId });
+
+ return await this.appEntityService.pack(app, user, {
+ detail: true,
+ includeSecret: false,
+ });
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/mastodon/MastodonConverters.ts b/packages/backend/src/server/api/mastodon/MastodonConverters.ts
index cf625d6e94..375ea1ef08 100644
--- a/packages/backend/src/server/api/mastodon/MastodonConverters.ts
+++ b/packages/backend/src/server/api/mastodon/MastodonConverters.ts
@@ -4,7 +4,7 @@
*/
import { Inject, Injectable } from '@nestjs/common';
-import { Entity, MastodonEntity } from 'megalodon';
+import { Entity, MastodonEntity, MisskeyEntity } from 'megalodon';
import mfm from '@transfem-org/sfm-js';
import { MastodonNotificationType } from 'megalodon/lib/src/mastodon/notification.js';
import { NotificationType } from 'megalodon/lib/src/notification.js';
@@ -369,6 +369,15 @@ export class MastodonConverters {
type: convertNotificationType(notification.type as NotificationType),
};
}
+
+ public convertApplication(app: MisskeyEntity.App): MastodonEntity.Application {
+ return {
+ name: app.name,
+ scopes: app.permission,
+ redirect_uri: app.callbackUrl,
+ redirect_uris: [app.callbackUrl],
+ };
+ }
}
function simpleConvert<T>(data: T): T {
@@ -459,4 +468,3 @@ export function convertRelationship(relationship: Partial<Entity.Relationship> &
note: relationship.note ?? '',
};
}
-
diff --git a/packages/backend/src/server/api/mastodon/endpoints/apps.ts b/packages/backend/src/server/api/mastodon/endpoints/apps.ts
index 5fce838f47..72b520c74a 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/apps.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/apps.ts
@@ -5,6 +5,7 @@
import { Injectable } from '@nestjs/common';
import { MastodonClientService } from '@/server/api/mastodon/MastodonClientService.js';
+import { MastodonConverters } from '@/server/api/mastodon/MastodonConverters.js';
import type { FastifyInstance } from 'fastify';
const readScope = [
@@ -59,6 +60,7 @@ type AuthMastodonRoute = { Body?: AuthPayload, Querystring: AuthPayload };
export class ApiAppsMastodon {
constructor(
private readonly clientService: MastodonClientService,
+ private readonly mastoConverters: MastodonConverters,
) {}
public register(fastify: FastifyInstance): void {
@@ -108,6 +110,13 @@ export class ApiAppsMastodon {
return reply.send(response);
});
+
+ fastify.get('/v1/apps/verify_credentials', async (_request, reply) => {
+ const client = this.clientService.getClient(_request);
+ const data = await client.verifyAppCredentials();
+ const response = this.mastoConverters.convertApplication(data.data);
+ return reply.send(response);
+ });
}
}
diff --git a/packages/megalodon/src/index.ts b/packages/megalodon/src/index.ts
index 50663c3ce5..bacd0574d4 100644
--- a/packages/megalodon/src/index.ts
+++ b/packages/megalodon/src/index.ts
@@ -9,6 +9,7 @@ import * as NotificationType from './notification'
import FilterContext from './filter_context'
import Converter from './converter'
import MastodonEntity from './mastodon/entity';
+import MisskeyEntity from './misskey/entity';
export {
Response,
@@ -23,4 +24,5 @@ export {
Entity,
Converter,
MastodonEntity,
+ MisskeyEntity,
}
diff --git a/packages/megalodon/src/mastodon/entities/application.ts b/packages/megalodon/src/mastodon/entities/application.ts
index a3f07997ee..f402152bf6 100644
--- a/packages/megalodon/src/mastodon/entities/application.ts
+++ b/packages/megalodon/src/mastodon/entities/application.ts
@@ -3,5 +3,8 @@ namespace MastodonEntity {
name: string
website?: string | null
vapid_key?: string | null
+ scopes: string[]
+ redirect_uris: string[]
+ redirect_uri?: string
}
}
diff --git a/packages/megalodon/src/misskey.ts b/packages/megalodon/src/misskey.ts
index 669eb0f106..bc38e27ce5 100644
--- a/packages/megalodon/src/misskey.ts
+++ b/packages/megalodon/src/misskey.ts
@@ -102,7 +102,7 @@ export default class Misskey implements MegalodonInterface {
website: null,
redirect_uri: res.data.callbackUrl,
client_id: '',
- client_secret: res.data.secret
+ client_secret: res.data.secret!
}
return OAuth.AppData.from(appData)
})
@@ -122,11 +122,8 @@ export default class Misskey implements MegalodonInterface {
// ======================================
// apps
// ======================================
- public async verifyAppCredentials(): Promise<Response<Entity.Application>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('misskey does not support')
- reject(err)
- })
+ public async verifyAppCredentials(): Promise<Response<MisskeyAPI.Entity.App>> {
+ return await this.client.post<MisskeyAPI.Entity.App>('/api/app/current');
}
// ======================================
diff --git a/packages/megalodon/src/misskey/entities/app.ts b/packages/megalodon/src/misskey/entities/app.ts
index 40a704b944..49c431596f 100644
--- a/packages/megalodon/src/misskey/entities/app.ts
+++ b/packages/megalodon/src/misskey/entities/app.ts
@@ -4,6 +4,6 @@ namespace MisskeyEntity {
name: string
callbackUrl: string
permission: Array<string>
- secret: string
+ secret?: string
}
}
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 18cb070af5..44700add31 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -553,6 +553,9 @@ type AppCreateRequest = operations['app___create']['requestBody']['content']['ap
type AppCreateResponse = operations['app___create']['responses']['200']['content']['application/json'];
// @public (undocumented)
+type AppCurrentResponse = operations['app___current']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
type AppShowRequest = operations['app___show']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1643,6 +1646,7 @@ declare namespace entities {
ApShowResponse,
AppCreateRequest,
AppCreateResponse,
+ AppCurrentResponse,
AppShowRequest,
AppShowResponse,
AuthAcceptRequest,
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 75b3c5769e..0dfe042811 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -1318,6 +1318,17 @@ declare module '../api.js' {
*
* **Credential required**: *No*
*/
+ request<E extends 'app/current', P extends Endpoints[E]['req']>(
+ endpoint: E,
+ params: P,
+ credential?: string | null,
+ ): Promise<SwitchCaseResponseType<E, P>>;
+
+ /**
+ * No description provided.
+ *
+ * **Credential required**: *No*
+ */
request<E extends 'app/show', P extends Endpoints[E]['req']>(
endpoint: E,
params: P,
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 9293a5e950..b424927316 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -159,6 +159,7 @@ import type {
ApShowResponse,
AppCreateRequest,
AppCreateResponse,
+ AppCurrentResponse,
AppShowRequest,
AppShowResponse,
AuthAcceptRequest,
@@ -778,6 +779,7 @@ export type Endpoints = {
'ap/get': { req: ApGetRequest; res: ApGetResponse };
'ap/show': { req: ApShowRequest; res: ApShowResponse };
'app/create': { req: AppCreateRequest; res: AppCreateResponse };
+ 'app/current': { req: EmptyRequest; res: AppCurrentResponse };
'app/show': { req: AppShowRequest; res: AppShowResponse };
'auth/accept': { req: AuthAcceptRequest; res: EmptyResponse };
'auth/session/generate': { req: AuthSessionGenerateRequest; res: AuthSessionGenerateResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index f71407a6ae..39359e3cfa 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -162,6 +162,7 @@ export type ApShowRequest = operations['ap___show']['requestBody']['content']['a
export type ApShowResponse = operations['ap___show']['responses']['200']['content']['application/json'];
export type AppCreateRequest = operations['app___create']['requestBody']['content']['application/json'];
export type AppCreateResponse = operations['app___create']['responses']['200']['content']['application/json'];
+export type AppCurrentResponse = operations['app___current']['responses']['200']['content']['application/json'];
export type AppShowRequest = operations['app___show']['requestBody']['content']['application/json'];
export type AppShowResponse = operations['app___show']['responses']['200']['content']['application/json'];
export type AuthAcceptRequest = operations['auth___accept']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 57e98f2f88..077ea35729 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -1086,6 +1086,15 @@ export type paths = {
*/
post: operations['app___create'];
};
+ '/app/current': {
+ /**
+ * app/current
+ * @description No description provided.
+ *
+ * **Credential required**: *No*
+ */
+ post: operations['app___current'];
+ };
'/app/show': {
/**
* app/show
@@ -13072,6 +13081,58 @@ export type operations = {
};
};
/**
+ * app/current
+ * @description No description provided.
+ *
+ * **Credential required**: *No*
+ */
+ app___current: {
+ responses: {
+ /** @description OK (with results) */
+ 200: {
+ content: {
+ 'application/json': components['schemas']['App'];
+ };
+ };
+ /** @description Client error */
+ 400: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Authentication error */
+ 401: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Forbidden error */
+ 403: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description I'm Ai */
+ 418: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Too many requests */
+ 429: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Internal server error */
+ 500: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ };
+ };
+ /**
* app/show
* @description No description provided.
*