Sharkey/packages/backend/src/server/api/ApiServerService.ts

175 lines
5.1 KiB
TypeScript

import { Inject, Injectable } from '@nestjs/common';
import cors from '@fastify/cors';
import multipart from '@fastify/multipart';
import fastifyCookie from '@fastify/cookie';
import { ModuleRef, repl } from '@nestjs/core';
import type { Config } from '@/config.js';
import type { UsersRepository, InstancesRepository, AccessTokensRepository } from '@/models/index.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
import endpoints, { IEndpoint } from './endpoints.js';
import { ApiCallService } from './ApiCallService.js';
import { SignupApiService } from './SignupApiService.js';
import { SigninApiService } from './SigninApiService.js';
import { GithubServerService } from './integration/GithubServerService.js';
import { DiscordServerService } from './integration/DiscordServerService.js';
import { TwitterServerService } from './integration/TwitterServerService.js';
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
@Injectable()
export class ApiServerService {
constructor(
private moduleRef: ModuleRef,
@Inject(DI.config)
private config: Config,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,
@Inject(DI.accessTokensRepository)
private accessTokensRepository: AccessTokensRepository,
private userEntityService: UserEntityService,
private apiCallService: ApiCallService,
private signupApiServiceService: SignupApiService,
private signinApiServiceService: SigninApiService,
private githubServerService: GithubServerService,
private discordServerService: DiscordServerService,
private twitterServerService: TwitterServerService,
) {
//this.createServer = this.createServer.bind(this);
}
@bindThis
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
fastify.register(cors, {
origin: '*',
});
fastify.register(multipart, {
limits: {
fileSize: this.config.maxFileSize ?? 262144000,
files: 1,
},
});
fastify.register(fastifyCookie, {});
// Prevent cache
fastify.addHook('onRequest', (request, reply, done) => {
reply.header('Cache-Control', 'private, max-age=0, must-revalidate');
done();
});
for (const endpoint of endpoints) {
const ep = {
name: endpoint.name,
meta: endpoint.meta,
params: endpoint.params,
exec: this.moduleRef.get('ep:' + endpoint.name, { strict: false }).exec,
};
if (endpoint.meta.requireFile) {
fastify.all<{
Params: { endpoint: string; },
Body: Record<string, unknown>,
Querystring: Record<string, unknown>,
}>('/' + endpoint.name, (request, reply) => {
if (request.method === 'GET' && !endpoint.meta.allowGet) {
reply.code(405);
reply.send();
return;
}
this.apiCallService.handleMultipartRequest(ep, request, reply);
});
} else {
fastify.all<{
Params: { endpoint: string; },
Body: Record<string, unknown>,
Querystring: Record<string, unknown>,
}>('/' + endpoint.name, (request, reply) => {
if (request.method === 'GET' && !endpoint.meta.allowGet) {
reply.code(405);
reply.send();
return;
}
this.apiCallService.handleRequest(ep, request, reply);
});
}
}
fastify.post<{
Body: {
username: string;
password: string;
host?: string;
invitationCode?: string;
emailAddress?: string;
'hcaptcha-response'?: string;
'g-recaptcha-response'?: string;
'turnstile-response'?: string;
}
}>('/signup', (request, reply) => this.signupApiServiceService.signup(request, reply));
fastify.post<{
Body: {
username: string;
password: string;
token?: string;
signature?: string;
authenticatorData?: string;
clientDataJSON?: string;
credentialId?: string;
challengeId?: string;
};
}>('/signin', (request, reply) => this.signinApiServiceService.signin(request, reply));
fastify.post<{ Body: { code: string; } }>('/signup-pending', (request, reply) => this.signupApiServiceService.signupPending(request, reply));
fastify.register(this.discordServerService.create);
fastify.register(this.githubServerService.create);
fastify.register(this.twitterServerService.create);
fastify.get('/v1/instance/peers', async (request, reply) => {
const instances = await this.instancesRepository.find({
select: ['host'],
where: {
isSuspended: false,
},
});
return instances.map(instance => instance.host);
});
fastify.post<{ Params: { session: string; } }>('/miauth/:session/check', async (request, reply) => {
const token = await this.accessTokensRepository.findOneBy({
session: request.params.session,
});
if (token && token.session != null && !token.fetched) {
this.accessTokensRepository.update(token.id, {
fetched: true,
});
return {
ok: true,
token: token.token,
user: await this.userEntityService.pack(token.userId, null, { detail: true }),
};
} else {
return {
ok: false,
};
}
});
done();
}
}