diff --git a/src/app.ts b/src/app.ts index 45c315d..ccf36e8 100644 --- a/src/app.ts +++ b/src/app.ts @@ -24,11 +24,12 @@ import { } from './controllers/api/accounts.ts'; import { appCredentialsController, createAppController } from './controllers/api/apps.ts'; import { emptyArrayController, emptyObjectController } from './controllers/api/fallback.ts'; -import { homeController, publicController } from './controllers/api/timelines.ts'; -import instanceController from './controllers/api/instance.ts'; +import { instanceController } from './controllers/api/instance.ts'; +import { notificationsController } from './controllers/api/notifications.ts'; import { createTokenController, oauthAuthorizeController, oauthController } from './controllers/api/oauth.ts'; import { frontendConfigController } from './controllers/api/pleroma.ts'; import { preferencesController } from './controllers/api/preferences.ts'; +import { relayController } from './controllers/nostr/relay.ts'; import { searchController } from './controllers/api/search.ts'; import { contextController, @@ -37,14 +38,14 @@ import { statusController, } from './controllers/api/statuses.ts'; import { streamingController } from './controllers/api/streaming.ts'; +import { homeController, publicController } from './controllers/api/timelines.ts'; import { trendingTagsController } from './controllers/api/trends.ts'; -import { relayController } from './controllers/nostr/relay.ts'; import { indexController } from './controllers/site.ts'; import { hostMetaController } from './controllers/well-known/host-meta.ts'; import { nodeInfoController, nodeInfoSchemaController } from './controllers/well-known/nodeinfo.ts'; import { nostrController } from './controllers/well-known/nostr.ts'; import { webfingerController } from './controllers/well-known/webfinger.ts'; -import { auth19, requireAuth } from './middleware/auth19.ts'; +import { auth19, requirePubkey } from './middleware/auth19.ts'; import { auth98 } from './middleware/auth98.ts'; interface AppEnv extends HonoEnv { @@ -92,8 +93,8 @@ app.post('/oauth/authorize', oauthAuthorizeController); app.get('/oauth/authorize', oauthController); app.post('/api/v1/acccounts', createAccountController); -app.get('/api/v1/accounts/verify_credentials', requireAuth, verifyCredentialsController); -app.patch('/api/v1/accounts/update_credentials', requireAuth, updateCredentialsController); +app.get('/api/v1/accounts/verify_credentials', requirePubkey, verifyCredentialsController); +app.patch('/api/v1/accounts/update_credentials', requirePubkey, updateCredentialsController); app.get('/api/v1/accounts/search', accountSearchController); app.get('/api/v1/accounts/lookup', accountLookupController); app.get('/api/v1/accounts/relationships', relationshipsController); @@ -104,9 +105,9 @@ app.get('/api/v1/accounts/:pubkey{[0-9a-f]{64}}', accountController); app.get('/api/v1/statuses/:id{[0-9a-f]{64}}/context', contextController); app.get('/api/v1/statuses/:id{[0-9a-f]{64}}', statusController); app.post('/api/v1/statuses/:id{[0-9a-f]{64}}/favourite', favouriteController); -app.post('/api/v1/statuses', requireAuth, createStatusController); +app.post('/api/v1/statuses', requirePubkey, createStatusController); -app.get('/api/v1/timelines/home', requireAuth, homeController); +app.get('/api/v1/timelines/home', requirePubkey, homeController); app.get('/api/v1/timelines/public', publicController); app.get('/api/v1/preferences', preferencesController); @@ -118,8 +119,9 @@ app.get('/api/pleroma/frontend_configurations', frontendConfigController); app.get('/api/v1/trends/tags', trendingTagsController); app.get('/api/v1/trends', trendingTagsController); +app.get('/api/v1/notifications', requirePubkey, notificationsController); + // Not (yet) implemented. -app.get('/api/v1/notifications', emptyArrayController); app.get('/api/v1/bookmarks', emptyArrayController); app.get('/api/v1/custom_emojis', emptyArrayController); app.get('/api/v1/accounts/search', emptyArrayController); diff --git a/src/controllers/api/instance.ts b/src/controllers/api/instance.ts index 6ffb85c..c28e03b 100644 --- a/src/controllers/api/instance.ts +++ b/src/controllers/api/instance.ts @@ -1,8 +1,7 @@ +import { type AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; -import type { Context } from '@/deps.ts'; - -function instanceController(c: Context) { +const instanceController: AppController = (c) => { const { host, protocol } = Conf.url; /** Protocol to use for WebSocket URLs, depending on the protocol of the `LOCAL_DOMAIN`. */ @@ -48,6 +47,6 @@ function instanceController(c: Context) { }, rules: [], }); -} +}; -export default instanceController; +export { instanceController }; diff --git a/src/controllers/api/notifications.ts b/src/controllers/api/notifications.ts new file mode 100644 index 0000000..30c3859 --- /dev/null +++ b/src/controllers/api/notifications.ts @@ -0,0 +1,22 @@ +import { type AppController } from '@/app.ts'; +import * as mixer from '@/mixer.ts'; +import { buildLinkHeader, paginationSchema } from '@/utils/web.ts'; +import { toNotification } from '@/transformers/nostr-to-mastoapi.ts'; +import { Time } from '@/utils.ts'; + +const notificationsController: AppController = async (c) => { + const pubkey = c.get('pubkey')!; + const { since, until } = paginationSchema.parse(c.req.query()); + + const events = await mixer.getFilters( + [{ kinds: [1], '#p': [pubkey], since, until }], + { timeout: Time.seconds(3) }, + ); + + const statuses = await Promise.all(events.map(toNotification)); + + const link = buildLinkHeader(c.req.url, events); + return c.json(statuses, 200, link ? { link } : undefined); +}; + +export { notificationsController }; diff --git a/src/middleware/auth19.ts b/src/middleware/auth19.ts index 45bf5bd..f92f678 100644 --- a/src/middleware/auth19.ts +++ b/src/middleware/auth19.ts @@ -36,7 +36,7 @@ const auth19: AppMiddleware = async (c, next) => { }; /** Throw a 401 if the pubkey isn't set. */ -const requireAuth: AppMiddleware = async (c, next) => { +const requirePubkey: AppMiddleware = async (c, next) => { if (!c.get('pubkey')) { throw new HTTPException(401); } @@ -44,4 +44,4 @@ const requireAuth: AppMiddleware = async (c, next) => { await next(); }; -export { auth19, requireAuth }; +export { auth19, requirePubkey }; diff --git a/src/transformers/nostr-to-mastoapi.ts b/src/transformers/nostr-to-mastoapi.ts index 2c474e1..79d4596 100644 --- a/src/transformers/nostr-to-mastoapi.ts +++ b/src/transformers/nostr-to-mastoapi.ts @@ -276,4 +276,24 @@ async function toRelationship(sourcePubkey: string, targetPubkey: string) { }; } -export { toAccount, toRelationship, toStatus }; +function toNotification(event: Event) { + switch (event.kind) { + case 1: + return toNotificationMention(event as Event<1>); + } +} + +async function toNotificationMention(event: Event<1>) { + const status = await toStatus(event); + if (!status) return; + + return { + id: event.id, + type: 'mention', + created_at: nostrDate(event.created_at).toISOString(), + account: status.account, + status: status, + }; +} + +export { toAccount, toNotification, toRelationship, toStatus };