Add website api graphql endpoint

This commit is contained in:
Dominik Korsa 2021-01-17 14:12:40 +01:00
parent 2e4357f7f8
commit 3e1f7e01f0
No known key found for this signature in database
GPG key ID: C6697282ADE724F2
13 changed files with 918 additions and 33 deletions

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/backend/backend.iml" filepath="$PROJECT_DIR$/backend/backend.iml" />
</modules>
</component>
</project>

9
backend/backend.iml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,7 @@
"@types/express": "^4.17.10",
"@types/lodash": "^4.14.167",
"@types/node": "^14.14.21",
"apollo-server-fastify": "^2.19.2",
"dotenv": "^8.2.0",
"fastify": "^3.10.1",
"fastify-cookie": "^5.1.0",
@ -28,6 +29,7 @@
"pino-pretty": "^4.3.0",
"reflect-metadata": "^0.1.13",
"ts-node": "^9.1.1",
"type-graphql": "^1.1.1",
"typeorm": "^0.2.30",
"typescript": "^4.1.3"
},

View file

@ -1,10 +1,11 @@
import { nanoid } from 'nanoid';
import {
BaseEntity,
Column, Entity, ObjectID, ObjectIdColumn,
} from 'typeorm';
@Entity()
export default class Application {
export default class Application extends BaseEntity {
@ObjectIdColumn()
public _id!: ObjectID;
@ -23,9 +24,6 @@ export default class Application {
@Column()
public redirectUris!: string[];
@Column()
public public!: string[];
public static generateClientId(): string {
return nanoid(12);
}

View file

@ -9,6 +9,7 @@ import FastifySensible from 'fastify-sensible';
import FastifySession from 'fastify-session';
import database from './database/database';
import registerOAuth from './routes/oauth2';
import registerWebsiteApi from './routes/website-api';
import { parseIntStrict, requireEnv } from './utils';
const server = Fastify({
@ -28,6 +29,7 @@ async function start() {
},
});
await server.register(registerOAuth, { prefix: '/api/oauth', logLevel: 'info' });
await server.register(registerWebsiteApi, { prefix: '/api/website', logLevel: 'info' });
await database.connect();
server.log.info('Connected to database');

View file

@ -13,12 +13,10 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
request,
reply,
) => {
console.log(request.query);
if (!isObject(request.query)) {
server.log.warn('Request query is not an object');
throw server.httpErrors.badRequest();
}
server.log.info(JSON.stringify(request.body));
try {
validateParam('client_id', request.query.client_id);
validateParam('redirect_uri', request.query.redirect_uri);
@ -63,7 +61,7 @@ export default function registerAuthorize(server: MyFastifyInstance): void {
sessionData.prompts.set(promptId, {
clientId: request.query.client_id,
redirectUri: request.query.redirect_uri,
scopes,
scopes: requestedScopes,
state: request.query.state,
codeChallenge: request.query.code_challenge === undefined ? undefined : {
method: codeChallengeMethod,

View file

@ -0,0 +1,26 @@
import { ApolloServer } from 'apollo-server-fastify';
import { buildSchema } from 'type-graphql';
import { ApolloContext, MyFastifyInstance } from '../../types';
import { getSessionData } from '../../utils';
import PromptInfoResolver from './resolvers/prompt-info-resolver';
import { WebsiteAPIContext } from './types';
export default async function registerWebsiteApi(server: MyFastifyInstance): Promise<void> {
const schema = await buildSchema({
authMode: 'error',
resolvers: [PromptInfoResolver],
});
const apolloServer = new ApolloServer({
schema,
context: ((context: ApolloContext): WebsiteAPIContext => ({
...context,
sessionData: getSessionData(context.request.session),
})),
});
await server.register(apolloServer.createHandler({
cors: {
origin: 'https://google.com',
},
}));
console.log(apolloServer.graphqlPath);
}

View file

@ -0,0 +1,15 @@
import { Field, ObjectType } from 'type-graphql';
@ObjectType()
export default class PromptInfoApplication {
@Field(() => String)
public name!: string;
@Field(() => String, {
nullable: true,
})
public iconUrl!: string | null;
@Field(() => Boolean)
public verified!: boolean;
}

View file

@ -0,0 +1,17 @@
import { Field, ObjectType } from 'type-graphql';
import PromptInfoApplication from './prompt-info-application';
@ObjectType()
export default class PromptInfo {
@Field(() => String)
public id!: string;
@Field(() => [String])
public scopes!: string[];
@Field(() => String)
public clientId!: string;
@Field(() => PromptInfoApplication)
public application!: PromptInfoApplication;
}

View file

@ -0,0 +1,40 @@
/* eslint-disable class-methods-use-this */
import {
Arg, Ctx, FieldResolver, Query, Resolver, ResolverInterface, Root,
} from 'type-graphql';
import database from '../../../database/database';
import PromptInfo from '../models/prompt-info';
import PromptInfoApplication from '../models/prompt-info-application';
import { WebsiteAPIContext } from '../types';
@Resolver(PromptInfo)
export default class PromptInfoResolver implements ResolverInterface<PromptInfo> {
@Query(() => PromptInfo)
public promptInfo(
@Arg('id') id: string,
@Ctx() { sessionData }: WebsiteAPIContext,
): Partial<PromptInfo> {
const prompt = sessionData.prompts.get(id);
if (!prompt) throw new Error('Prompt data not found');
return {
id,
clientId: prompt.clientId,
scopes: prompt.scopes,
};
}
@FieldResolver()
public async application(@Root() prompt: PromptInfo): Promise<PromptInfoApplication> {
const application = await database.applicationRepo.findOne({
where: {
clientId: prompt.clientId,
},
});
if (!application) throw new Error('Prompt data not found');
return {
name: application.name,
iconUrl: application.iconUrl,
verified: application.verified,
};
}
}

View file

@ -0,0 +1,5 @@
import { ApolloContext, SessionData } from '../../types';
export interface WebsiteAPIContext extends ApolloContext {
sessionData: SessionData;
}

View file

@ -1,5 +1,5 @@
import {
FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault,
FastifyInstance, FastifyRequest, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault,
} from 'fastify';
export interface Prompt {
@ -26,3 +26,7 @@ export interface Session {
}
export type MyFastifyInstance = FastifyInstance<RawServerDefault, RawRequestDefaultExpression<RawServerDefault>, RawReplyDefaultExpression<RawServerDefault>>;
export interface ApolloContext {
request: FastifyRequest;
}