Skip to content

Commit

Permalink
Gateway updates
Browse files Browse the repository at this point in the history
  • Loading branch information
thostetler committed Oct 17, 2024
1 parent 644519e commit f9afc74
Show file tree
Hide file tree
Showing 25 changed files with 238 additions and 150 deletions.
2 changes: 1 addition & 1 deletion global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ declare module 'iron-session' {
token?: {
access_token: string;
anonymous: boolean;
expire_in: string;
expires_at: string;
username: string;
};
isAuthenticated?: boolean;
Expand Down
10 changes: 9 additions & 1 deletion src/api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ const resolveApiBaseUrl = (defaultBaseUrl = ''): string => {
}

// use a known URL for development
if (process.env.NODE_ENV === 'development' && typeof process.env.NEXT_PUBLIC_API_HOST_CLIENT === 'string') {
if (
typeof window !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
typeof process.env.NEXT_PUBLIC_API_HOST_CLIENT === 'string'
) {
return process.env.NEXT_PUBLIC_API_HOST_CLIENT;
}

Expand All @@ -31,6 +35,10 @@ const resolveApiBaseUrl = (defaultBaseUrl = ''): string => {
export const defaultRequestConfig: AxiosRequestConfig = {
baseURL: resolveApiBaseUrl(),
withCredentials: true,
headers: {
'Content-Type': 'application/json',
},

timeout: typeof window === 'undefined' ? APP_DEFAULTS.SSR_API_TIMEOUT : APP_DEFAULTS.API_TIMEOUT,
paramsSerializer: {
serialize: (params) =>
Expand Down
113 changes: 65 additions & 48 deletions src/api/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,65 +187,82 @@ export const biblibSorts = [
] as const;

export enum ApiTargets {
// Accounts
BOOTSTRAP = '/accounts/bootstrap',
SEARCH = '/search/query',
QTREE = '/search/qtree',
BIGQUERY = '/search/bigquery',
EXPORT = '/export',
SERVICE_AUTHOR_NETWORK = '/vis/author-network',
SERVICE_PAPER_NETWORK = '/vis/paper-network',
SERVICE_WORDCLOUD = '/vis/word-cloud',
SERVICE_METRICS = '/metrics',
SERVICE_OBJECTS = '/objects',
SERVICE_OBJECTS_QUERY = '/objects/query',
SERVICE_CITATION_HELPER = '/citation_helper',
SERVICE_AUTHOR_AFFILIATION_EXPORT = '/authoraff',
MYADS_STORAGE = '/vault',
MYADS_STORAGE_QUERY = '/vault/query',
MYADS_NOTIFICATIONS = '/vault/notifications',
MYADS_NOTIFICATIONS_QUERY = '/vault/notification_query',
LINK_SERVERS = '/vault/configuration/link_servers',
AUTHOR_AFFILIATION_SEARCH = '/author-affiliation/search',
AUTHOR_AFFILIATION_EXPORT = '/author-affiliation/export',
RESOLVER = '/resolver',
CHANGE_EMAIL = '/accounts/user/change-email',
CHANGE_PASSWORD = '/accounts/user/change-password',
CSRF = '/accounts/csrf',
USER = '/accounts/user',
USER_DATA = '/vault/user-data',
SITE_CONFIGURATION = '/vault/configuration',
TOKEN = '/accounts/token',
INFO = '/accounts/info',
LOGIN = '/accounts/login',
LOGOUT = '/accounts/logout',
REGISTER = '/accounts/register',
LOGIN = '/accounts/user/login',
LOGOUT = '/accounts/user/logout',
PROTECTED = '/accounts/protected',
// REGISTER = '/accounts/register',
RESET_PASSWORD = '/accounts/reset-password',
STATUS = '/accounts/status',
TOKEN = '/accounts/user/token',
USER = '/accounts/user',
VERIFY = '/accounts/verify',
DELETE = '/accounts/user/delete',
RESET_PASSWORD = '/accounts/reset-password',
CHANGE_PASSWORD = '/accounts/change-password',
CHANGE_EMAIL = '/accounts/change-email',
RECOMMENDER = '/recommender',
GRAPHICS = '/graphics',
FEEDBACK = '/feedback/userfeedback',

// Author Affiliation
AUTHOR_AFFILIATION_EXPORT = '/author-affiliation/export',
AUTHOR_AFFILIATION_SEARCH = '/author-affiliation/search',

// Library Operations
DOCUMENTS = '/biblib/documents',
LIBRARIES = '/biblib/libraries',
LIBRARY_IMPORT_ADS2_AUTH = '/harbour/auth/twopointoh',
LIBRARY_IMPORT_ADS2_TO_BBB = '/biblib/twopointoh',
LIBRARY_IMPORT_CLASSIC_AUTH = '/harbour/auth/classic',
LIBRARY_IMPORT_CLASSIC_MIRRORS = '/harbour/mirrors',
LIBRARY_IMPORT_CLASSIC_TO_BBB = '/biblib/classic',
LIBRARY_IMPORT_ADS2_AUTH = '/harbour/auth/twopointoh',
LIBRARY_IMPORT_ADS2_TO_BBB = '/biblib/twopointoh',
LIBRARY_IMPORT_ZOTERO = '/harbour/export/twopointoh/zotero',
LIBRARY_IMPORT_MENDELEY = '/harbour/export/twopointoh/mendeley',
LIBRARY_IMPORT_CREDENTIALS = '/harbour/user',
ORCID_PREFERENCES = '/orcid/preferences',
ORCID = '/orcid',
ORCID_NAME = '/orcid/orcid-name',
ORCID_WORKS = 'orcid-works',
ORCID_WORK = 'orcid-work',
ORCID_PROFILE = 'orcid-profile',
ORCID_EXCHANGE_TOKEN = '/orcid/exchangeOAuthCode',
LIBRARIES = '/biblib/libraries',
LIBRARY_TRANSFER = '/biblib/transfer',
LIBRARY_IMPORT_MENDELEY = '/harbour/export/twopointoh/mendeley',
LIBRARY_IMPORT_ZOTERO = '/harbour/export/twopointoh/zotero',
LIBRARY_NOTES = 'biblib/notes',
LIBRARY_OPERATION = 'biblib/libraries/operations',
LIBRARY_QUERY = 'biblib/query',
LIBRARY_NOTES = 'biblib/notes',
DOCUMENTS = '/biblib/documents',
LIBRARY_TRANSFER = '/biblib/transfer',
PERMISSIONS = '/biblib/permissions',

// Orcid Operations
ORCID = '/orcid',
ORCID_EXCHANGE_TOKEN = '/orcid/exchangeOAuthCode',
ORCID_NAME = '/orcid/orcid-name',
ORCID_PREFERENCES = '/orcid/preferences',
ORCID_PROFILE = 'orcid-profile',
ORCID_WORK = 'orcid-work',
ORCID_WORKS = 'orcid-works',

// Vault Operations
MYADS_NOTIFICATIONS = '/vault/notifications',
MYADS_NOTIFICATIONS_QUERY = '/vault/notification_query',
MYADS_STORAGE = '/vault',
MYADS_STORAGE_QUERY = '/vault/query',
SITE_CONFIGURATION = '/vault/configuration',
USER_DATA = '/vault/user-data',
LINK_SERVERS = '/vault/configuration/link_servers',

// Search and Query
BIGQUERY = '/search/bigquery',
QTREE = '/search/qtree',
SEARCH = '/search/query',

// Services
SERVICE_AUTHOR_AFFILIATION_EXPORT = '/authoraff',
SERVICE_AUTHOR_NETWORK = '/vis/author-network',
SERVICE_CITATION_HELPER = '/citation_helper',
SERVICE_METRICS = '/metrics',
SERVICE_OBJECTS = '/objects',
SERVICE_OBJECTS_QUERY = '/objects/query',
SERVICE_PAPER_NETWORK = '/vis/paper-network',
SERVICE_WORDCLOUD = '/vis/word-cloud',

// Miscellaneous
EXPORT = '/export',
FEEDBACK = '/feedback',
GRAPHICS = '/graphics',
RECOMMENDER = '/recommender',
REFERENCE = '/reference/text',
RESOLVER = '/resolver',
}
1 change: 1 addition & 0 deletions src/api/objects/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export const resolveObjectQuerySSR = async (params: IObjectsQueryApiParams, ctx:
method: 'POST',
data: { query: [query] },
headers: {
...defaultRequestConfig.headers,
...pick(TRACING_HEADERS, ctx.req.headers),
Authorization: `Bearer ${token}`,
},
Expand Down
1 change: 1 addition & 0 deletions src/api/search/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ export const fetchSearchSSR = async (
params: finalParams,
signal,
headers: {
...defaultRequestConfig.headers,
Authorization: `Bearer ${token}`,
...pick(TRACING_HEADERS, ctx.req.headers),
},
Expand Down
8 changes: 6 additions & 2 deletions src/api/user/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ export interface IBootstrapPayload {
ratelimit: number;
anonymous: boolean;
client_secret: string;
expire_in: string;
expires_at: string;
refresh_token: string;
given_name: string;
family_name: string;
message?: string;
}

export type IUserData = Pick<IBootstrapPayload, 'username' | 'anonymous' | 'access_token' | 'expire_in'>;
export type IUserData = Pick<IBootstrapPayload, 'username' | 'anonymous' | 'access_token' | 'expires_at'>;

export interface IUserForgotPasswordCredentials {
email: string;
Expand All @@ -59,6 +61,8 @@ export interface IUserCredentials {
}

export interface IUserRegistrationCredentials {
givenName: string;
familyName: string;
email: string;
password: string;
confirmPassword: string;
Expand Down
8 changes: 5 additions & 3 deletions src/api/user/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ const registerUser: MutationFunction<IBasicAccountsResponse, IUserRegistrationCr
const config = await configWithCSRF({
...defaultRequestConfig,
method: 'POST',
url: ApiTargets.REGISTER,
url: ApiTargets.USER,
data: {
given_name: credentials.givenName,
family_name: credentials.familyName,
email: credentials.email,
password1: credentials.password,
password2: credentials.confirmPassword,
Expand Down Expand Up @@ -208,8 +210,8 @@ const resetUserPassword: MutationFunction<IBasicAccountsResponse, IUserResetPass
export const deleteUserAccount: MutationFunction<IBasicAccountsResponse, unknown> = async () => {
const config = await configWithCSRF({
...defaultRequestConfig,
method: 'POST',
url: ApiTargets.DELETE,
method: 'DELETE',
url: ApiTargets.USER,
});

const { data } = await api.request<IBasicAccountsResponse>(config);
Expand Down
46 changes: 29 additions & 17 deletions src/auth-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { ApiRequestConfig, ApiTargets, IBootstrapPayload, ICSRFResponse, IUserData } from '@/api';
import { defaultRequestConfig } from '@/api/config';
import { isNil } from 'ramda';
import { isPast, parseISO } from 'date-fns';
import { APP_DEFAULTS } from '@/config';
import { logger } from '@/logger';

const fetchCSRF = async () =>
await axios.get<ICSRFResponse, AxiosResponse<ICSRFResponse>>(ApiTargets.CSRF, {
const fetchCSRF = async () => {
const config: AxiosRequestConfig = {
...defaultRequestConfig,
url: ApiTargets.CSRF,
timeout: APP_DEFAULTS.API_TIMEOUT,
});
};
logger.debug({ config }, 'Fetching CSRF token');
return axios.request<ICSRFResponse, AxiosResponse<ICSRFResponse>>(config);
};

export const configWithCSRF = async (config: ApiRequestConfig): Promise<ApiRequestConfig> => {
const csrfRes = await fetchCSRF();
Expand Down Expand Up @@ -61,23 +65,31 @@ export const hash = async (str?: string) => {
* Checks if the user data is valid
* @param userData
*/
export const isUserData = (userData?: IUserData): userData is IUserData => {
return (
!isNil(userData) &&
typeof userData.access_token === 'string' &&
typeof userData.expire_in === 'string' &&
userData.access_token.length > 0 &&
userData.expire_in.length > 0
);
export const isUserData = (userData?: IUserData): userData is IUserData =>
!isNil(userData) &&
typeof userData.access_token === 'string' &&
typeof userData.expires_at === 'string' &&
userData.access_token.length > 0 &&
userData.expires_at.length > 0;

/**
* Checks if a token is expired based on the expiration time.
*
* @param {string} expiresAt - The expiration time of the token in seconds since the Unix epoch.
* @returns {boolean} - Returns true if the current time is greater than or equal to the expiration time, false otherwise.
*/
export const isTokenExpired = (expiresAt: string): boolean => {
const currentTime = Math.floor(Date.now() / 1000);
const tokenExpiryTime = parseInt(expiresAt, 10);
return currentTime >= tokenExpiryTime;
};

/**
* Checks if the user data is valid and the token is not expired
* Checks if the token is valid
* @param userData
*/
export const isValidToken = (userData?: IUserData): boolean => {
return isUserData(userData) && !isPast(parseISO(userData.expire_in));
};
export const isValidToken = (userData?: IUserData): boolean =>
isUserData(userData) && !isTokenExpired(userData.expires_at);

/**
* Checks if the user is authenticated
Expand All @@ -96,7 +108,7 @@ export const pickUserData = (userData?: IUserData | IBootstrapPayload) => {
}
return {
access_token: userData.access_token,
expire_in: userData.expire_in,
expires_at: userData.expires_at,
username: userData.username,
anonymous: userData.anonymous,
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/__mocks__/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const mockSession: IronSessionData = {
token: {
access_token: '',
anonymous: true,
expire_in: '9999-01-01T00:00:00',
expires_at: '1950000000',
username: 'anonymous',
},
isAuthenticated: false,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/useGetUserEmail.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isUserData } from '@/api';
import { useStore } from '@/store';
import { isUserData } from '@/auth-utils';

export const useGetUserEmail = () => {
const user = useStore((state) => state.user);
Expand Down
3 changes: 2 additions & 1 deletion src/lib/useSession.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import api, { isAuthenticated } from '@/api';
import api from '@/api';
import axios from 'axios';
import { useEffect } from 'react';
import { useUser } from '@/lib/useUser';
import { useMutation } from '@tanstack/react-query';
import { ILogoutResponse } from '@/pages/api/auth/logout';
import { useRouter } from 'next/router';
import { isAuthenticated } from '@/auth-utils';

/**
* Provides access to the user session and methods to logout
Expand Down
2 changes: 1 addition & 1 deletion src/lib/useUser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useStore } from '@/store';
import { useQueryClient } from '@tanstack/react-query';
import { useEffect } from 'react';
import { isUserData } from '@/api';
import { isUserData } from '@/auth-utils';

/**
* Provides access to the user object and methods to reset it
Expand Down
4 changes: 2 additions & 2 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const logger: Logger = pino({
browser: {
asObject: true,
},
level: process.env.LOG_LEVEL || 'info',
level: process.env.NEXT_PUBLIC_LOG_LEVEL || 'info',
base: {
env: process.env.NODE_ENV || 'development',
},
Expand All @@ -29,7 +29,7 @@ export const edgeLogger: Logger = pino({
}
},
},
level: process.env.LOG_LEVEL || 'info',
level: process.env.NEXT_PUBLIC_LOG_LEVEL || 'info',
base: {
env: process.env.NODE_ENV || 'development',
},
Expand Down
2 changes: 1 addition & 1 deletion src/middlewares/botCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const crawlerCheck = async (req: NextRequest, ip: string, ua: string) => {

const baseToken: IronSessionData['token'] = {
anonymous: true,
expire_in: '9999-01-01T00:00:00',
expires_at: '99999999999999',
username: 'anonymous',
access_token: 'no-token',
};
Expand Down
Loading

0 comments on commit f9afc74

Please sign in to comment.