refactor: restructure JwtPayload and MeQuery (#994)
* extract schema::User as format for MeQuery * refactor: restructure JWTPayload * feat: update frontend to adapt JWT token format change * delete generated filesr0.7
parent
b26a7f7cf3
commit
8dc09ea4e7
|
|
@ -1,87 +0,0 @@
|
|||
/* eslint-disable */
|
||||
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'
|
||||
|
||||
import * as types from './graphql'
|
||||
|
||||
/**
|
||||
* Map of all GraphQL operations in the project.
|
||||
*
|
||||
* This map has several performance disadvantages:
|
||||
* 1. It is not tree-shakeable, so it will include all operations in the project.
|
||||
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
|
||||
* 3. It does not support dead code elimination, so it will add unused operations.
|
||||
*
|
||||
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
||||
*/
|
||||
const documents = {
|
||||
'\n query GetRegistrationToken {\n registrationToken\n }\n':
|
||||
types.GetRegistrationTokenDocument,
|
||||
'\n mutation tokenAuth($email: String!, $password: String!) {\n tokenAuth(email: $email, password: $password) {\n accessToken\n refreshToken\n }\n }\n':
|
||||
types.TokenAuthDocument,
|
||||
'\n mutation register(\n $email: String!\n $password1: String!\n $password2: String!\n $invitationCode: String\n ) {\n register(\n email: $email\n password1: $password1\n password2: $password2\n invitationCode: $invitationCode\n ) {\n accessToken\n refreshToken\n }\n }\n':
|
||||
types.RegisterDocument,
|
||||
'\n query GetWorkers {\n workers {\n kind\n name\n addr\n device\n arch\n cpuInfo\n cpuCount\n cudaDevices\n }\n }\n':
|
||||
types.GetWorkersDocument,
|
||||
'\n mutation refreshToken($refreshToken: String!) {\n refreshToken(refreshToken: $refreshToken) {\n accessToken\n refreshToken\n }\n }\n':
|
||||
types.RefreshTokenDocument,
|
||||
'\n query GetIsAdminInitialized {\n isAdminInitialized\n }\n':
|
||||
types.GetIsAdminInitializedDocument
|
||||
}
|
||||
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
|
||||
* ```
|
||||
*
|
||||
* The query argument is unknown!
|
||||
* Please regenerate the types.
|
||||
*/
|
||||
export function graphql(source: string): unknown
|
||||
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(
|
||||
source: '\n query GetRegistrationToken {\n registrationToken\n }\n'
|
||||
): (typeof documents)['\n query GetRegistrationToken {\n registrationToken\n }\n']
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(
|
||||
source: '\n mutation tokenAuth($email: String!, $password: String!) {\n tokenAuth(email: $email, password: $password) {\n accessToken\n refreshToken\n }\n }\n'
|
||||
): (typeof documents)['\n mutation tokenAuth($email: String!, $password: String!) {\n tokenAuth(email: $email, password: $password) {\n accessToken\n refreshToken\n }\n }\n']
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(
|
||||
source: '\n mutation register(\n $email: String!\n $password1: String!\n $password2: String!\n $invitationCode: String\n ) {\n register(\n email: $email\n password1: $password1\n password2: $password2\n invitationCode: $invitationCode\n ) {\n accessToken\n refreshToken\n }\n }\n'
|
||||
): (typeof documents)['\n mutation register(\n $email: String!\n $password1: String!\n $password2: String!\n $invitationCode: String\n ) {\n register(\n email: $email\n password1: $password1\n password2: $password2\n invitationCode: $invitationCode\n ) {\n accessToken\n refreshToken\n }\n }\n']
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(
|
||||
source: '\n query GetWorkers {\n workers {\n kind\n name\n addr\n device\n arch\n cpuInfo\n cpuCount\n cudaDevices\n }\n }\n'
|
||||
): (typeof documents)['\n query GetWorkers {\n workers {\n kind\n name\n addr\n device\n arch\n cpuInfo\n cpuCount\n cudaDevices\n }\n }\n']
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(
|
||||
source: '\n mutation refreshToken($refreshToken: String!) {\n refreshToken(refreshToken: $refreshToken) {\n accessToken\n refreshToken\n }\n }\n'
|
||||
): (typeof documents)['\n mutation refreshToken($refreshToken: String!) {\n refreshToken(refreshToken: $refreshToken) {\n accessToken\n refreshToken\n }\n }\n']
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(
|
||||
source: '\n query GetIsAdminInitialized {\n isAdminInitialized\n }\n'
|
||||
): (typeof documents)['\n query GetIsAdminInitialized {\n isAdminInitialized\n }\n']
|
||||
|
||||
export function graphql(source: string) {
|
||||
return (documents as any)[source] ?? {}
|
||||
}
|
||||
|
||||
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> =
|
||||
TDocumentNode extends DocumentNode<infer TType, any> ? TType : never
|
||||
|
|
@ -1,511 +0,0 @@
|
|||
/* eslint-disable */
|
||||
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'
|
||||
|
||||
export type Maybe<T> = T | null
|
||||
export type InputMaybe<T> = Maybe<T>
|
||||
export type Exact<T extends { [key: string]: unknown }> = {
|
||||
[K in keyof T]: T[K]
|
||||
}
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
|
||||
[SubKey in K]?: Maybe<T[SubKey]>
|
||||
}
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
|
||||
[SubKey in K]: Maybe<T[SubKey]>
|
||||
}
|
||||
export type MakeEmpty<
|
||||
T extends { [key: string]: unknown },
|
||||
K extends keyof T
|
||||
> = { [_ in K]?: never }
|
||||
export type Incremental<T> =
|
||||
| T
|
||||
| {
|
||||
[P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never
|
||||
}
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
ID: { input: string; output: string }
|
||||
String: { input: string; output: string }
|
||||
Boolean: { input: boolean; output: boolean }
|
||||
Int: { input: number; output: number }
|
||||
Float: { input: number; output: number }
|
||||
}
|
||||
|
||||
export type Claims = {
|
||||
__typename?: 'Claims'
|
||||
exp: Scalars['Float']['output']
|
||||
iat: Scalars['Float']['output']
|
||||
user: UserInfo
|
||||
}
|
||||
|
||||
export type Invitation = {
|
||||
__typename?: 'Invitation'
|
||||
code: Scalars['String']['output']
|
||||
createdAt: Scalars['String']['output']
|
||||
email: Scalars['String']['output']
|
||||
id: Scalars['Int']['output']
|
||||
}
|
||||
|
||||
export type Mutation = {
|
||||
__typename?: 'Mutation'
|
||||
createInvitation: Scalars['Int']['output']
|
||||
deleteInvitation: Scalars['Int']['output']
|
||||
refreshToken: RefreshTokenResponse
|
||||
register: RegisterResponse
|
||||
resetRegistrationToken: Scalars['String']['output']
|
||||
tokenAuth: TokenAuthResponse
|
||||
verifyToken: VerifyTokenResponse
|
||||
}
|
||||
|
||||
export type MutationCreateInvitationArgs = {
|
||||
email: Scalars['String']['input']
|
||||
}
|
||||
|
||||
export type MutationDeleteInvitationArgs = {
|
||||
id: Scalars['Int']['input']
|
||||
}
|
||||
|
||||
export type MutationRefreshTokenArgs = {
|
||||
refreshToken: Scalars['String']['input']
|
||||
}
|
||||
|
||||
export type MutationRegisterArgs = {
|
||||
email: Scalars['String']['input']
|
||||
invitationCode?: InputMaybe<Scalars['String']['input']>
|
||||
password1: Scalars['String']['input']
|
||||
password2: Scalars['String']['input']
|
||||
}
|
||||
|
||||
export type MutationTokenAuthArgs = {
|
||||
email: Scalars['String']['input']
|
||||
password: Scalars['String']['input']
|
||||
}
|
||||
|
||||
export type MutationVerifyTokenArgs = {
|
||||
token: Scalars['String']['input']
|
||||
}
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query'
|
||||
invitations: Array<Invitation>
|
||||
isAdminInitialized: Scalars['Boolean']['output']
|
||||
me: UserInfo
|
||||
registrationToken: Scalars['String']['output']
|
||||
workers: Array<Worker>
|
||||
}
|
||||
|
||||
export type RefreshTokenResponse = {
|
||||
__typename?: 'RefreshTokenResponse'
|
||||
accessToken: Scalars['String']['output']
|
||||
refreshExpiresAt: Scalars['Float']['output']
|
||||
refreshToken: Scalars['String']['output']
|
||||
}
|
||||
|
||||
export type RegisterResponse = {
|
||||
__typename?: 'RegisterResponse'
|
||||
accessToken: Scalars['String']['output']
|
||||
refreshToken: Scalars['String']['output']
|
||||
}
|
||||
|
||||
export type TokenAuthResponse = {
|
||||
__typename?: 'TokenAuthResponse'
|
||||
accessToken: Scalars['String']['output']
|
||||
refreshToken: Scalars['String']['output']
|
||||
}
|
||||
|
||||
export type UserInfo = {
|
||||
__typename?: 'UserInfo'
|
||||
email: Scalars['String']['output']
|
||||
isAdmin: Scalars['Boolean']['output']
|
||||
}
|
||||
|
||||
export type VerifyTokenResponse = {
|
||||
__typename?: 'VerifyTokenResponse'
|
||||
claims: Claims
|
||||
}
|
||||
|
||||
export type Worker = {
|
||||
__typename?: 'Worker'
|
||||
addr: Scalars['String']['output']
|
||||
arch: Scalars['String']['output']
|
||||
cpuCount: Scalars['Int']['output']
|
||||
cpuInfo: Scalars['String']['output']
|
||||
cudaDevices: Array<Scalars['String']['output']>
|
||||
device: Scalars['String']['output']
|
||||
kind: WorkerKind
|
||||
name: Scalars['String']['output']
|
||||
}
|
||||
|
||||
export enum WorkerKind {
|
||||
Chat = 'CHAT',
|
||||
Completion = 'COMPLETION'
|
||||
}
|
||||
|
||||
export type GetRegistrationTokenQueryVariables = Exact<{ [key: string]: never }>
|
||||
|
||||
export type GetRegistrationTokenQuery = {
|
||||
__typename?: 'Query'
|
||||
registrationToken: string
|
||||
}
|
||||
|
||||
export type TokenAuthMutationVariables = Exact<{
|
||||
email: Scalars['String']['input']
|
||||
password: Scalars['String']['input']
|
||||
}>
|
||||
|
||||
export type TokenAuthMutation = {
|
||||
__typename?: 'Mutation'
|
||||
tokenAuth: {
|
||||
__typename?: 'TokenAuthResponse'
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
}
|
||||
}
|
||||
|
||||
export type RegisterMutationVariables = Exact<{
|
||||
email: Scalars['String']['input']
|
||||
password1: Scalars['String']['input']
|
||||
password2: Scalars['String']['input']
|
||||
invitationCode?: InputMaybe<Scalars['String']['input']>
|
||||
}>
|
||||
|
||||
export type RegisterMutation = {
|
||||
__typename?: 'Mutation'
|
||||
register: {
|
||||
__typename?: 'RegisterResponse'
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
}
|
||||
}
|
||||
|
||||
export type GetWorkersQueryVariables = Exact<{ [key: string]: never }>
|
||||
|
||||
export type GetWorkersQuery = {
|
||||
__typename?: 'Query'
|
||||
workers: Array<{
|
||||
__typename?: 'Worker'
|
||||
kind: WorkerKind
|
||||
name: string
|
||||
addr: string
|
||||
device: string
|
||||
arch: string
|
||||
cpuInfo: string
|
||||
cpuCount: number
|
||||
cudaDevices: Array<string>
|
||||
}>
|
||||
}
|
||||
|
||||
export type RefreshTokenMutationVariables = Exact<{
|
||||
refreshToken: Scalars['String']['input']
|
||||
}>
|
||||
|
||||
export type RefreshTokenMutation = {
|
||||
__typename?: 'Mutation'
|
||||
refreshToken: {
|
||||
__typename?: 'RefreshTokenResponse'
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
}
|
||||
}
|
||||
|
||||
export type GetIsAdminInitializedQueryVariables = Exact<{
|
||||
[key: string]: never
|
||||
}>
|
||||
|
||||
export type GetIsAdminInitializedQuery = {
|
||||
__typename?: 'Query'
|
||||
isAdminInitialized: boolean
|
||||
}
|
||||
|
||||
export const GetRegistrationTokenDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'query',
|
||||
name: { kind: 'Name', value: 'GetRegistrationToken' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'registrationToken' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
} as unknown as DocumentNode<
|
||||
GetRegistrationTokenQuery,
|
||||
GetRegistrationTokenQueryVariables
|
||||
>
|
||||
export const TokenAuthDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'mutation',
|
||||
name: { kind: 'Name', value: 'tokenAuth' },
|
||||
variableDefinitions: [
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'email' }
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'password' }
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }
|
||||
}
|
||||
}
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'tokenAuth' },
|
||||
arguments: [
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'email' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'email' }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'password' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'password' }
|
||||
}
|
||||
}
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'accessToken' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'refreshToken' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
} as unknown as DocumentNode<TokenAuthMutation, TokenAuthMutationVariables>
|
||||
export const RegisterDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'mutation',
|
||||
name: { kind: 'Name', value: 'register' },
|
||||
variableDefinitions: [
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'email' }
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'password1' }
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'password2' }
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'invitationCode' }
|
||||
},
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }
|
||||
}
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'register' },
|
||||
arguments: [
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'email' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'email' }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'password1' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'password1' }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'password2' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'password2' }
|
||||
}
|
||||
},
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'invitationCode' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'invitationCode' }
|
||||
}
|
||||
}
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'accessToken' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'refreshToken' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
} as unknown as DocumentNode<RegisterMutation, RegisterMutationVariables>
|
||||
export const GetWorkersDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'query',
|
||||
name: { kind: 'Name', value: 'GetWorkers' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'workers' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'kind' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'name' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'addr' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'device' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'arch' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'cpuInfo' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'cpuCount' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'cudaDevices' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
} as unknown as DocumentNode<GetWorkersQuery, GetWorkersQueryVariables>
|
||||
export const RefreshTokenDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'mutation',
|
||||
name: { kind: 'Name', value: 'refreshToken' },
|
||||
variableDefinitions: [
|
||||
{
|
||||
kind: 'VariableDefinition',
|
||||
variable: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'refreshToken' }
|
||||
},
|
||||
type: {
|
||||
kind: 'NonNullType',
|
||||
type: { kind: 'NamedType', name: { kind: 'Name', value: 'String' } }
|
||||
}
|
||||
}
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{
|
||||
kind: 'Field',
|
||||
name: { kind: 'Name', value: 'refreshToken' },
|
||||
arguments: [
|
||||
{
|
||||
kind: 'Argument',
|
||||
name: { kind: 'Name', value: 'refreshToken' },
|
||||
value: {
|
||||
kind: 'Variable',
|
||||
name: { kind: 'Name', value: 'refreshToken' }
|
||||
}
|
||||
}
|
||||
],
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'accessToken' } },
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'refreshToken' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
} as unknown as DocumentNode<
|
||||
RefreshTokenMutation,
|
||||
RefreshTokenMutationVariables
|
||||
>
|
||||
export const GetIsAdminInitializedDocument = {
|
||||
kind: 'Document',
|
||||
definitions: [
|
||||
{
|
||||
kind: 'OperationDefinition',
|
||||
operation: 'query',
|
||||
name: { kind: 'Name', value: 'GetIsAdminInitialized' },
|
||||
selectionSet: {
|
||||
kind: 'SelectionSet',
|
||||
selections: [
|
||||
{ kind: 'Field', name: { kind: 'Name', value: 'isAdminInitialized' } }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
} as unknown as DocumentNode<
|
||||
GetIsAdminInitializedQuery,
|
||||
GetIsAdminInitializedQueryVariables
|
||||
>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { jwtDecode } from 'jwt-decode'
|
||||
import { jwtDecode, JwtPayload } from 'jwt-decode'
|
||||
|
||||
import { graphql } from '@/lib/gql/generates'
|
||||
import useInterval from '@/lib/hooks/use-interval'
|
||||
|
|
@ -226,13 +226,13 @@ type Session =
|
|||
function useSession(): Session {
|
||||
const { authState } = useAuthStore()
|
||||
if (authState?.status == 'authenticated') {
|
||||
const { user } = jwtDecode<{ user: { email: string; is_admin: boolean } }>(
|
||||
const { sub, is_admin } = jwtDecode<JwtPayload & { is_admin: boolean }>(
|
||||
authState.data.accessToken
|
||||
)
|
||||
return {
|
||||
data: {
|
||||
email: user.email,
|
||||
isAdmin: user.is_admin,
|
||||
email: sub!,
|
||||
isAdmin: is_admin,
|
||||
accessToken: authState.data.accessToken
|
||||
},
|
||||
status: authState.status
|
||||
|
|
|
|||
|
|
@ -18,19 +18,22 @@ type Mutation {
|
|||
deleteInvitation(id: Int!): Int!
|
||||
}
|
||||
|
||||
"DateTime"
|
||||
scalar DateTimeUtc
|
||||
|
||||
type VerifyTokenResponse {
|
||||
claims: Claims!
|
||||
claims: JWTPayload!
|
||||
}
|
||||
|
||||
type UserInfo {
|
||||
email: String!
|
||||
isAdmin: Boolean!
|
||||
}
|
||||
|
||||
type Claims {
|
||||
type JWTPayload {
|
||||
"Expiration time (as UTC timestamp)"
|
||||
exp: Float!
|
||||
"Issued at (as UTC timestamp)"
|
||||
iat: Float!
|
||||
user: UserInfo!
|
||||
"User email address"
|
||||
sub: String!
|
||||
"Whether the user is admin."
|
||||
isAdmin: Boolean!
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
|
@ -38,7 +41,7 @@ type Query {
|
|||
registrationToken: String!
|
||||
isAdminInitialized: Boolean!
|
||||
invitations: [Invitation!]!
|
||||
me: UserInfo!
|
||||
me: User!
|
||||
}
|
||||
|
||||
type Invitation {
|
||||
|
|
@ -48,6 +51,12 @@ type Invitation {
|
|||
createdAt: String!
|
||||
}
|
||||
|
||||
type User {
|
||||
email: String!
|
||||
isAdmin: Boolean!
|
||||
authToken: String!
|
||||
}
|
||||
|
||||
type Worker {
|
||||
kind: WorkerKind!
|
||||
name: String!
|
||||
|
|
@ -67,7 +76,7 @@ type TokenAuthResponse {
|
|||
type RefreshTokenResponse {
|
||||
accessToken: String!
|
||||
refreshToken: String!
|
||||
refreshExpiresAt: Float!
|
||||
refreshExpiresAt: DateTimeUtc!
|
||||
}
|
||||
|
||||
schema {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use tracing::{error, warn};
|
|||
use uuid::Uuid;
|
||||
use validator::ValidationErrors;
|
||||
|
||||
use super::from_validation_errors;
|
||||
use super::{from_validation_errors, User};
|
||||
|
||||
lazy_static! {
|
||||
static ref JWT_TOKEN_SECRET: String = jwt_token_secret();
|
||||
|
|
@ -26,15 +26,15 @@ lazy_static! {
|
|||
static ref JWT_DEFAULT_EXP: u64 = 30 * 60; // 30 minutes
|
||||
}
|
||||
|
||||
pub fn generate_jwt(claims: Claims) -> jwt::errors::Result<String> {
|
||||
pub fn generate_jwt(claims: JWTPayload) -> jwt::errors::Result<String> {
|
||||
let header = jwt::Header::default();
|
||||
let token = jwt::encode(&header, &claims, &JWT_ENCODING_KEY)?;
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
pub fn validate_jwt(token: &str) -> jwt::errors::Result<Claims> {
|
||||
pub fn validate_jwt(token: &str) -> jwt::errors::Result<JWTPayload> {
|
||||
let validation = jwt::Validation::default();
|
||||
let data = jwt::decode::<Claims>(token, &JWT_DECODING_KEY, &validation)?;
|
||||
let data = jwt::decode::<JWTPayload>(token, &JWT_DECODING_KEY, &validation)?;
|
||||
Ok(data.claims)
|
||||
}
|
||||
|
||||
|
|
@ -202,58 +202,40 @@ impl RefreshTokenResponse {
|
|||
|
||||
#[derive(Debug, GraphQLObject)]
|
||||
pub struct VerifyTokenResponse {
|
||||
claims: Claims,
|
||||
claims: JWTPayload,
|
||||
}
|
||||
|
||||
impl VerifyTokenResponse {
|
||||
pub fn new(claims: Claims) -> Self {
|
||||
pub fn new(claims: JWTPayload) -> Self {
|
||||
Self { claims }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, GraphQLObject)]
|
||||
pub struct UserInfo {
|
||||
email: String,
|
||||
is_admin: bool,
|
||||
}
|
||||
|
||||
impl UserInfo {
|
||||
pub fn new(email: String, is_admin: bool) -> Self {
|
||||
Self { email, is_admin }
|
||||
}
|
||||
|
||||
pub fn is_admin(&self) -> bool {
|
||||
self.is_admin
|
||||
}
|
||||
|
||||
pub fn email(&self) -> &str {
|
||||
&self.email
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, GraphQLObject)]
|
||||
pub struct Claims {
|
||||
// Required. Expiration time (as UTC timestamp)
|
||||
pub struct JWTPayload {
|
||||
/// Expiration time (as UTC timestamp)
|
||||
exp: f64,
|
||||
// Optional. Issued at (as UTC timestamp)
|
||||
|
||||
/// Issued at (as UTC timestamp)
|
||||
iat: f64,
|
||||
// Customized. user info
|
||||
user: UserInfo,
|
||||
|
||||
/// User email address
|
||||
pub sub: String,
|
||||
|
||||
/// Whether the user is admin.
|
||||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
impl Claims {
|
||||
pub fn new(user: UserInfo) -> Self {
|
||||
impl JWTPayload {
|
||||
pub fn new(email: String, is_admin: bool) -> Self {
|
||||
let now = jwt::get_current_timestamp();
|
||||
Self {
|
||||
iat: now as f64,
|
||||
exp: (now + *JWT_DEFAULT_EXP) as f64,
|
||||
user,
|
||||
sub: email,
|
||||
is_admin,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_info(&self) -> &UserInfo {
|
||||
&self.user
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, GraphQLObject)]
|
||||
|
|
@ -287,6 +269,7 @@ pub trait AuthenticationService: Send + Sync {
|
|||
) -> std::result::Result<RefreshTokenResponse, RefreshTokenError>;
|
||||
async fn verify_access_token(&self, access_token: &str) -> Result<VerifyTokenResponse>;
|
||||
async fn is_admin_initialized(&self) -> Result<bool>;
|
||||
async fn get_user_by_email(&self, email: &str) -> Result<User>;
|
||||
|
||||
async fn create_invitation(&self, email: String) -> Result<i32>;
|
||||
async fn list_invitations(&self) -> Result<Vec<Invitation>>;
|
||||
|
|
@ -298,7 +281,7 @@ mod tests {
|
|||
use super::*;
|
||||
#[test]
|
||||
fn test_generate_jwt() {
|
||||
let claims = Claims::new(UserInfo::new("test".to_string(), false));
|
||||
let claims = JWTPayload::new("test".to_string(), false);
|
||||
let token = generate_jwt(claims).unwrap();
|
||||
|
||||
assert!(!token.is_empty())
|
||||
|
|
@ -306,13 +289,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_validate_jwt() {
|
||||
let claims = Claims::new(UserInfo::new("test".to_string(), false));
|
||||
let claims = JWTPayload::new("test".to_string(), false);
|
||||
let token = generate_jwt(claims).unwrap();
|
||||
let claims = validate_jwt(&token).unwrap();
|
||||
assert_eq!(
|
||||
claims.user_info(),
|
||||
&UserInfo::new("test".to_string(), false)
|
||||
);
|
||||
assert_eq!(claims.sub, "test");
|
||||
assert!(!claims.is_admin);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ use std::sync::Arc;
|
|||
|
||||
use auth::AuthenticationService;
|
||||
use juniper::{
|
||||
graphql_object, graphql_value, EmptySubscription, FieldError, IntoFieldError, Object, RootNode,
|
||||
ScalarValue, Value,
|
||||
graphql_object, graphql_value, EmptySubscription, FieldError, GraphQLObject, IntoFieldError,
|
||||
Object, RootNode, ScalarValue, Value,
|
||||
};
|
||||
use juniper_axum::FromAuth;
|
||||
use tabby_common::api::{code::CodeSearch, event::RawEventLogger};
|
||||
|
|
@ -18,7 +18,7 @@ use self::{
|
|||
};
|
||||
use crate::schema::{
|
||||
auth::{
|
||||
RefreshTokenError, RefreshTokenResponse, RegisterResponse, TokenAuthResponse, UserInfo,
|
||||
RefreshTokenError, RefreshTokenResponse, RegisterResponse, TokenAuthResponse,
|
||||
VerifyTokenResponse,
|
||||
},
|
||||
worker::Worker,
|
||||
|
|
@ -32,7 +32,7 @@ pub trait ServiceLocator: Send + Sync {
|
|||
}
|
||||
|
||||
pub struct Context {
|
||||
claims: Option<auth::Claims>,
|
||||
claims: Option<auth::JWTPayload>,
|
||||
locator: Arc<dyn ServiceLocator>,
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ pub struct Query;
|
|||
impl Query {
|
||||
async fn workers(ctx: &Context) -> Result<Vec<Worker>> {
|
||||
if let Some(claims) = &ctx.claims {
|
||||
if claims.user_info().is_admin() {
|
||||
if claims.is_admin {
|
||||
let workers = ctx.locator.worker().list_workers().await;
|
||||
return Ok(workers);
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ impl Query {
|
|||
|
||||
async fn registration_token(ctx: &Context) -> Result<String> {
|
||||
if let Some(claims) = &ctx.claims {
|
||||
if claims.user_info().is_admin() {
|
||||
if claims.is_admin {
|
||||
let token = ctx.locator.worker().read_registration_token().await?;
|
||||
return Ok(token);
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ impl Query {
|
|||
|
||||
async fn invitations(ctx: &Context) -> Result<Vec<Invitation>> {
|
||||
if let Some(claims) = &ctx.claims {
|
||||
if claims.user_info().is_admin() {
|
||||
if claims.is_admin {
|
||||
return Ok(ctx.locator.auth().list_invitations().await?);
|
||||
}
|
||||
}
|
||||
|
|
@ -110,14 +110,23 @@ impl Query {
|
|||
))
|
||||
}
|
||||
|
||||
async fn me(ctx: &Context) -> Result<UserInfo> {
|
||||
async fn me(ctx: &Context) -> Result<User> {
|
||||
if let Some(claims) = &ctx.claims {
|
||||
return Ok(claims.user_info().to_owned());
|
||||
let user = ctx.locator.auth().get_user_by_email(&claims.sub).await?;
|
||||
Ok(user)
|
||||
} else {
|
||||
Err(CoreError::Unauthorized("Not logged in"))
|
||||
}
|
||||
Err(CoreError::Unauthorized("Not logged in"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, GraphQLObject)]
|
||||
pub struct User {
|
||||
pub email: String,
|
||||
pub is_admin: bool,
|
||||
pub auth_token: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mutation;
|
||||
|
||||
|
|
@ -125,7 +134,7 @@ pub struct Mutation;
|
|||
impl Mutation {
|
||||
async fn reset_registration_token(ctx: &Context) -> Result<String> {
|
||||
if let Some(claims) = &ctx.claims {
|
||||
if claims.user_info().is_admin() {
|
||||
if claims.is_admin {
|
||||
let reg_token = ctx.locator.worker().reset_registration_token().await?;
|
||||
return Ok(reg_token);
|
||||
}
|
||||
|
|
@ -169,7 +178,7 @@ impl Mutation {
|
|||
|
||||
async fn create_invitation(ctx: &Context, email: String) -> Result<i32> {
|
||||
if let Some(claims) = &ctx.claims {
|
||||
if claims.user_info().is_admin() {
|
||||
if claims.is_admin {
|
||||
return Ok(ctx.locator.auth().create_invitation(email).await?);
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +189,7 @@ impl Mutation {
|
|||
|
||||
async fn delete_invitation(ctx: &Context, id: i32) -> Result<i32> {
|
||||
if let Some(claims) = &ctx.claims {
|
||||
if claims.user_info().is_admin() {
|
||||
if claims.is_admin {
|
||||
return Ok(ctx.locator.auth().delete_invitation(id).await?);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use argon2::{
|
||||
password_hash,
|
||||
password_hash::{rand_core::OsRng, SaltString},
|
||||
|
|
@ -8,10 +8,13 @@ use async_trait::async_trait;
|
|||
use validator::Validate;
|
||||
|
||||
use super::db::DbConn;
|
||||
use crate::schema::auth::{
|
||||
generate_jwt, generate_refresh_token, validate_jwt, AuthenticationService, Claims, Invitation,
|
||||
RefreshTokenError, RefreshTokenResponse, RegisterError, RegisterResponse, TokenAuthError,
|
||||
TokenAuthResponse, UserInfo, VerifyTokenResponse,
|
||||
use crate::schema::{
|
||||
auth::{
|
||||
generate_jwt, generate_refresh_token, validate_jwt, AuthenticationService, Invitation,
|
||||
JWTPayload, RefreshTokenError, RefreshTokenResponse, RegisterError, RegisterResponse,
|
||||
TokenAuthError, TokenAuthResponse, VerifyTokenResponse,
|
||||
},
|
||||
User,
|
||||
};
|
||||
|
||||
/// Input parameters for register mutation
|
||||
|
|
@ -149,10 +152,8 @@ impl AuthenticationService for DbConn {
|
|||
let refresh_token = generate_refresh_token();
|
||||
self.create_refresh_token(id, &refresh_token).await?;
|
||||
|
||||
let Ok(access_token) = generate_jwt(Claims::new(UserInfo::new(
|
||||
user.email.clone(),
|
||||
user.is_admin,
|
||||
))) else {
|
||||
let Ok(access_token) = generate_jwt(JWTPayload::new(user.email.clone(), user.is_admin))
|
||||
else {
|
||||
return Err(RegisterError::Unknown);
|
||||
};
|
||||
|
||||
|
|
@ -179,10 +180,8 @@ impl AuthenticationService for DbConn {
|
|||
let refresh_token = generate_refresh_token();
|
||||
self.create_refresh_token(user.id, &refresh_token).await?;
|
||||
|
||||
let Ok(access_token) = generate_jwt(Claims::new(UserInfo::new(
|
||||
user.email.clone(),
|
||||
user.is_admin,
|
||||
))) else {
|
||||
let Ok(access_token) = generate_jwt(JWTPayload::new(user.email.clone(), user.is_admin))
|
||||
else {
|
||||
return Err(TokenAuthError::Unknown);
|
||||
};
|
||||
|
||||
|
|
@ -208,10 +207,8 @@ impl AuthenticationService for DbConn {
|
|||
self.replace_refresh_token(&token, &new_token).await?;
|
||||
|
||||
// refresh token update is done, generate new access token based on user info
|
||||
let Ok(access_token) = generate_jwt(Claims::new(UserInfo::new(
|
||||
user.email.clone(),
|
||||
user.is_admin,
|
||||
))) else {
|
||||
let Ok(access_token) = generate_jwt(JWTPayload::new(user.email.clone(), user.is_admin))
|
||||
else {
|
||||
return Err(RefreshTokenError::Unknown);
|
||||
};
|
||||
|
||||
|
|
@ -231,6 +228,15 @@ impl AuthenticationService for DbConn {
|
|||
Ok(!admin.is_empty())
|
||||
}
|
||||
|
||||
async fn get_user_by_email(&self, email: &str) -> Result<User> {
|
||||
let user = self.get_user_by_email(email).await?;
|
||||
if let Some(user) = user {
|
||||
Ok(user.into())
|
||||
} else {
|
||||
Err(anyhow!("User not found {}", email))
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_invitation(&self, email: String) -> Result<i32> {
|
||||
self.create_invitation(email).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use rusqlite::{params, OptionalExtension, Row};
|
|||
use uuid::Uuid;
|
||||
|
||||
use super::DbConn;
|
||||
use crate::schema;
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct User {
|
||||
|
|
@ -41,6 +42,16 @@ impl User {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<User> for schema::User {
|
||||
fn from(val: User) -> Self {
|
||||
schema::User {
|
||||
email: val.email,
|
||||
is_admin: val.is_admin,
|
||||
auth_token: val.auth_token,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DbConn {
|
||||
pub async fn create_user(
|
||||
&self,
|
||||
|
|
|
|||
Loading…
Reference in New Issue