diff --git a/src/app.ts b/src/app.ts index 16e74703..be114718 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,15 +14,16 @@ import { homeController } from './controllers/api/timelines.ts'; import instanceController from './controllers/api/instance.ts'; import { createTokenController, oauthAuthorizeController, oauthController } from './controllers/api/oauth.ts'; import { preferencesController } from './controllers/api/preferences.ts'; +import { searchController } from './controllers/api/search.ts'; import { contextController, createStatusController, favouriteController, statusController, } from './controllers/api/statuses.ts'; -import { requireAuth, setAuth } from './middleware/auth.ts'; +import { streamingController } from './controllers/api/streaming.ts'; import { indexController } from './controllers/site.ts'; -import { searchController } from './controllers/api/search.ts'; +import { requireAuth, setAuth } from './middleware/auth.ts'; interface AppEnv extends HonoEnv { Variables: { @@ -37,7 +38,12 @@ type AppController = Handler; const app = new Hono(); -app.use('/*', logger(), cors({ origin: '*', exposeHeaders: ['link'] }), setAuth); +app.use('*', logger()); + +app.get('/api/v1/streaming', streamingController); +app.get('/api/v1/streaming/', streamingController); + +app.use('*', cors({ origin: '*', exposeHeaders: ['link'] }), setAuth); app.get('/api/v1/instance', instanceController); diff --git a/src/controllers/api/instance.ts b/src/controllers/api/instance.ts index 1cf0fd25..ec8a3d3c 100644 --- a/src/controllers/api/instance.ts +++ b/src/controllers/api/instance.ts @@ -3,7 +3,7 @@ import { ADMIN_EMAIL, LOCAL_DOMAIN, POST_CHAR_LIMIT } from '@/config.ts'; import type { Context } from '@/deps.ts'; function instanceController(c: Context) { - const { host } = new URL(LOCAL_DOMAIN); + const { host, protocol } = new URL(LOCAL_DOMAIN); return c.json({ uri: host, @@ -35,7 +35,7 @@ function instanceController(c: Context) { user_count: 0, }, urls: { - streaming_api: `wss://${host}`, + streaming_api: `${protocol === 'http:' ? 'ws:' : 'wss:'}//${host}`, }, version: '0.0.0 (compatible; Ditto 0.0.1)', email: ADMIN_EMAIL, diff --git a/src/controllers/api/streaming.ts b/src/controllers/api/streaming.ts new file mode 100644 index 00000000..d3c3b8a2 --- /dev/null +++ b/src/controllers/api/streaming.ts @@ -0,0 +1,29 @@ +import { AppController } from '@/app.ts'; +import { nip21 } from '@/deps.ts'; + +const streamingController: AppController = (c) => { + const upgrade = c.req.headers.get('upgrade'); + const token = c.req.headers.get('sec-websocket-protocol'); + + if (upgrade?.toLowerCase() !== 'websocket') { + return c.text('Please use websocket protocol', 400); + } + + if (!token) { + return c.json({ error: 'Missing access token' }, 401); + } + + if (!nip21.BECH32_REGEX.test(token)) { + return c.json({ error: 'Invalid access token' }, 401); + } + + const { socket, response } = Deno.upgradeWebSocket(c.req.raw, { protocol: token }); + + socket.addEventListener('open', () => console.log('websocket: connection opened')); + socket.addEventListener('close', () => console.log('websocket: connection closed')); + socket.addEventListener('message', (e) => console.log('websocket message: ', e.data)); + + return response; +}; + +export { streamingController }; diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index 6b8e16e0..6526ff13 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -3,7 +3,7 @@ import { getPublicKey, HTTPException, nip19 } from '@/deps.ts'; /** NIP-19 auth middleware. */ const setAuth: AppMiddleware = async (c, next) => { - const authHeader = c.req.headers.get('Authorization'); + const authHeader = c.req.headers.get('authorization'); if (authHeader?.startsWith('Bearer ')) { const bech32 = authHeader.replace(/^Bearer /, '');