diff --git a/backend/Dockerfile b/backend/Dockerfile index b5084874..6f200afb 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -8,7 +8,6 @@ WORKDIR /usr/src/app USER node COPY --chown=node frontend/src/shared /usr/src/app/frontend/src/shared/ -COPY --chown=node frontend/src/types.ts /usr/src/app/frontend/src/types.ts COPY --chown=node backend/package.json backend/ COPY --chown=node backend/package-lock.json backend/ COPY --chown=node backend/prisma/schema.prisma backend @@ -39,8 +38,7 @@ RUN chown node:node /usr/src/app -R WORKDIR /usr/src/app USER node -COPY --from=backend-build --chown=node /usr/src/app/frontend/src/shared/types.d.ts ../frontend/src/shared/types.d.ts -COPY --from=backend-build --chown=node /usr/src/app/frontend/src/types.ts ../frontend/src/types.ts +COPY --from=backend-build --chown=node /usr/src/app/frontend/src/shared/ ../frontend/src/shared/ COPY --from=backend-build --chown=node /usr/src/app/backend/build . COPY --from=backend-build --chown=node /usr/src/app/backend/node_modules ./backend/node_modules COPY --from=backend-build --chown=node /usr/src/app/backend/prisma ./backend/prisma diff --git a/backend/src/api-tests/user/login.test.ts b/backend/src/api-tests/user/login.test.ts index dc129dcc..b07ef5b1 100644 --- a/backend/src/api-tests/user/login.test.ts +++ b/backend/src/api-tests/user/login.test.ts @@ -16,7 +16,7 @@ describe('Logging in', () => { expect(result.status).toEqual(200) expect(result.body.username).toEqual('testSu') - expect(result.body.role).toEqual(1) // frontend/src/types: Role enum + expect(result.body.role).toEqual(1) // frontend/src/shared/types: Role enum expect(result.body.isFirstLogin).toEqual(undefined) }) diff --git a/backend/src/app.ts b/backend/src/app.ts index 0806ade2..f29c0aa7 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -24,7 +24,7 @@ import { ENABLE_WRITE, RUNNING_ENV } from './utils/config' import { tokenExtractor, userExtractor } from './middlewares/authenticator' import { errorHandler } from './middlewares/errorHandler' import { requireOneOf } from './middlewares/authorizer' -import { Role } from './../../frontend/src/types' +import { Role } from './../../frontend/src/shared/types' import { blockWriteRequests } from './middlewares/misc' import testRouter from './routes/test' diff --git a/backend/src/middlewares/authenticator.ts b/backend/src/middlewares/authenticator.ts index 8449fb82..4ee8eb58 100644 --- a/backend/src/middlewares/authenticator.ts +++ b/backend/src/middlewares/authenticator.ts @@ -2,8 +2,7 @@ import { Middleware } from '../types' import { SECRET } from '../utils/config' import jwt, { Secret, TokenExpiredError } from 'jsonwebtoken' import { nowDb } from '../utils/db' -import { User } from '../../../frontend/src/shared/types' -import { Role } from '../../../frontend/src/types' +import { User, Role } from '../../../frontend/src/shared/types' /* This allows extractors to add user and token to request diff --git a/backend/src/middlewares/authorizer.ts b/backend/src/middlewares/authorizer.ts index 6ebad55e..a5bfd1ea 100644 --- a/backend/src/middlewares/authorizer.ts +++ b/backend/src/middlewares/authorizer.ts @@ -1,4 +1,4 @@ -import { Role } from '../../../frontend/src/types' +import { Role } from '../../../frontend/src/shared/types' import { Middleware } from '../types' export class AccessError extends Error { diff --git a/backend/src/routes/locality.ts b/backend/src/routes/locality.ts index 515b1b67..be562ddc 100644 --- a/backend/src/routes/locality.ts +++ b/backend/src/routes/locality.ts @@ -1,9 +1,8 @@ import { Request, Router } from 'express' import { getAllLocalities, getLocalityDetails, validateEntireLocality } from '../services/locality' import { fixBigInt } from '../utils/common' -import { EditDataType, EditMetaData, LocalityDetailsType } from '../../../frontend/src/shared/types' +import { EditDataType, EditMetaData, LocalityDetailsType, Role } from '../../../frontend/src/shared/types' import { requireOneOf } from '../middlewares/authorizer' -import { Role } from '../../../frontend/src/types' import { deleteLocality, writeLocality } from '../services/write/locality' const router = Router() diff --git a/backend/src/routes/person.ts b/backend/src/routes/person.ts index fa3436e8..6c73299e 100644 --- a/backend/src/routes/person.ts +++ b/backend/src/routes/person.ts @@ -1,6 +1,6 @@ import { Router } from 'express' import { getAllPersons, getPersonDetails } from '../services/person' -import { Role } from '../../../frontend/src/types' +import { Role } from '../../../frontend/src/shared/types' import { requireOneOf } from '../middlewares/authorizer' const router = Router() diff --git a/backend/src/routes/project.ts b/backend/src/routes/project.ts index 26b520d4..98c24aa7 100644 --- a/backend/src/routes/project.ts +++ b/backend/src/routes/project.ts @@ -1,7 +1,7 @@ import { Router } from 'express' import { getAllProjects, getProjectDetails } from '../services/project' import { requireOneOf } from '../middlewares/authorizer' -import { Role } from '../../../frontend/src/types' +import { Role } from '../../../frontend/src/shared/types' const router = Router() diff --git a/backend/src/routes/reference.ts b/backend/src/routes/reference.ts index d695f528..ef55b930 100644 --- a/backend/src/routes/reference.ts +++ b/backend/src/routes/reference.ts @@ -10,8 +10,7 @@ import { getJournalById, } from '../services/reference' import { requireOneOf } from '../middlewares/authorizer' -import { Role } from '../../../frontend/src/types' -import { EditMetaData, ReferenceDetailsType, EditDataType } from '../../../frontend/src/shared/types' +import { Role, EditMetaData, ReferenceDetailsType, EditDataType } from '../../../frontend/src/shared/types' import { deleteReference, writeReference } from '../services/write/reference' const router = Router() diff --git a/backend/src/routes/region.ts b/backend/src/routes/region.ts index 2cada5b1..3c59e84f 100644 --- a/backend/src/routes/region.ts +++ b/backend/src/routes/region.ts @@ -1,7 +1,7 @@ import { Router } from 'express' import { getAllRegions, getRegionDetails } from '../services/region' import { requireOneOf } from '../middlewares/authorizer' -import { Role } from '../../../frontend/src/types' +import { Role } from '../../../frontend/src/shared/types' const router = Router() diff --git a/backend/src/routes/species.ts b/backend/src/routes/species.ts index 7bff23a4..ff63f48a 100644 --- a/backend/src/routes/species.ts +++ b/backend/src/routes/species.ts @@ -1,10 +1,9 @@ import { Request, Router } from 'express' import { getAllSpecies, getSpeciesDetails, validateEntireSpecies } from '../services/species' import { fixBigInt } from '../utils/common' -import { EditMetaData, SpeciesDetailsType } from '../../../frontend/src/shared/types' +import { EditMetaData, SpeciesDetailsType, Role } from '../../../frontend/src/shared/types' import { deleteSpecies, writeSpecies } from '../services/write/species' import { requireOneOf } from '../middlewares/authorizer' -import { Role } from '../../../frontend/src/types' const router = Router() diff --git a/backend/src/routes/timeBound.ts b/backend/src/routes/timeBound.ts index 0d62a638..a3f39c2e 100644 --- a/backend/src/routes/timeBound.ts +++ b/backend/src/routes/timeBound.ts @@ -1,6 +1,5 @@ import { Request, Router } from 'express' -import { EditDataType, EditMetaData, TimeBoundDetailsType } from '../../../frontend/src/shared/types' -import { Role } from '../../../frontend/src/types' +import { EditDataType, EditMetaData, TimeBoundDetailsType, Role } from '../../../frontend/src/shared/types' import { requireOneOf } from '../middlewares/authorizer' import { getAllTimeBounds, diff --git a/backend/src/routes/timeUnit.ts b/backend/src/routes/timeUnit.ts index 19ec55bc..30aa4d64 100644 --- a/backend/src/routes/timeUnit.ts +++ b/backend/src/routes/timeUnit.ts @@ -1,6 +1,5 @@ import { Request, Router } from 'express' -import { EditDataType, EditMetaData, TimeUnitDetailsType } from '../../../frontend/src/shared/types' -import { Role } from '../../../frontend/src/types' +import { EditDataType, EditMetaData, TimeUnitDetailsType, Role } from '../../../frontend/src/shared/types' import { requireOneOf } from '../middlewares/authorizer' import { getAllSequences } from '../services/sequence' import { diff --git a/backend/src/routes/user.ts b/backend/src/routes/user.ts index 454295ac..b6772d1c 100644 --- a/backend/src/routes/user.ts +++ b/backend/src/routes/user.ts @@ -6,7 +6,7 @@ import * as bcrypt from 'bcrypt' import { nowDb } from '../utils/db' import { getRole } from '../middlewares/authenticator' import { AccessError } from '../middlewares/authorizer' -import { Role } from '../../../frontend/src/types' +import { Role } from '../../../frontend/src/shared/types' import md5 from 'md5' const router = Router() diff --git a/backend/src/services/crossSearch.ts b/backend/src/services/crossSearch.ts index 41a7a52e..03a9f5e0 100644 --- a/backend/src/services/crossSearch.ts +++ b/backend/src/services/crossSearch.ts @@ -1,5 +1,4 @@ -import { User, CrossSearch } from '../../../frontend/src/shared/types' -import { Role } from '../../../frontend/src/types' +import { User, CrossSearch, Role } from '../../../frontend/src/shared/types' import { nowDb } from '../utils/db' import { generateFilteredCrossSearchSql, diff --git a/backend/src/services/locality.ts b/backend/src/services/locality.ts index 9c45d205..fdc8e065 100644 --- a/backend/src/services/locality.ts +++ b/backend/src/services/locality.ts @@ -1,5 +1,4 @@ -import { EditDataType, EditMetaData, LocalityDetailsType, User } from '../../../frontend/src/shared/types' -import { Role } from '../../../frontend/src/types' +import { EditDataType, EditMetaData, LocalityDetailsType, User, Role } from '../../../frontend/src/shared/types' import { validateLocality } from '../../../frontend/src/shared/validators/locality' import { ValidationObject, referenceValidator } from '../../../frontend/src/shared/validators/validator' import Prisma from '../../prisma/generated/now_test_client' diff --git a/backend/src/services/localitySpeciesExport.ts b/backend/src/services/localitySpeciesExport.ts index 67cc6627..a98e416f 100644 --- a/backend/src/services/localitySpeciesExport.ts +++ b/backend/src/services/localitySpeciesExport.ts @@ -1,7 +1,6 @@ import { PoolConnection } from 'mariadb' -import { User } from '../../../frontend/src/shared/types' +import { User, Role } from '../../../frontend/src/shared/types' import { NOW_DB_NAME } from '../utils/config' -import { Role } from '../../../frontend/src/types' import { pool } from '../utils/db' const getExportList = async ( diff --git a/backend/src/services/write/locality.ts b/backend/src/services/write/locality.ts index 2c717fe0..9e686254 100644 --- a/backend/src/services/write/locality.ts +++ b/backend/src/services/write/locality.ts @@ -1,8 +1,8 @@ import { EditDataType, LocalityDetailsType, Reference, User } from '../../../../frontend/src/shared/types' +import { getHomininSkeletalRemains } from '../../../../frontend/src/types' import { NOW_DB_NAME } from '../../utils/config' import { WriteHandler } from './writeOperations/writeHandler' import { getFieldsOfTables } from '../../utils/db' -import { getHomininSkeletalRemains } from '../../../../frontend/src/types' import { getLocalityDetails, filterDuplicateLocalitySpecies } from '../locality' import { ActionType } from './writeOperations/types' import { makeListRemoved, fixRadioSelection } from './writeOperations/utils' diff --git a/frontend/src/components/DetailView/common/EditingForm.tsx b/frontend/src/components/DetailView/common/EditingForm.tsx index 2fb7ebba..1378f18b 100644 --- a/frontend/src/components/DetailView/common/EditingForm.tsx +++ b/frontend/src/components/DetailView/common/EditingForm.tsx @@ -1,7 +1,7 @@ import { Box, TextField } from '@mui/material' import { EditingModal } from './EditingModal' import { useForm } from 'react-hook-form' -import { EditDataType, Editable } from '@/shared/types' +import { Editable, EditDataType } from '@/shared/types' import { useDetailContext } from '../Context/DetailContext' export type EditingFormField = { name: string; label: string; required?: boolean } diff --git a/frontend/src/components/EmailPage.tsx b/frontend/src/components/EmailPage.tsx index 93785724..fb60dc7b 100644 --- a/frontend/src/components/EmailPage.tsx +++ b/frontend/src/components/EmailPage.tsx @@ -1,6 +1,6 @@ import { useUser } from '@/hooks/user' import { useEmailMutation } from '@/redux/emailReducer' -import { Role } from '@/types' +import { Role } from '@/shared/types' import { Box, Button, Divider, Stack, TextField, Typography } from '@mui/material' import { useState } from 'react' diff --git a/frontend/src/components/NavBar.tsx b/frontend/src/components/NavBar.tsx index 1ebd46b6..13f0bdbc 100644 --- a/frontend/src/components/NavBar.tsx +++ b/frontend/src/components/NavBar.tsx @@ -4,7 +4,7 @@ import { useDispatch } from 'react-redux' import { clearUser } from '../redux/userReducer' import { api } from '../redux/api' import { MouseEvent, useState } from 'react' -import { Role } from '@/types' +import { Role } from '@/shared/types' import { useUser } from '@/hooks/user' import { ENV } from '@/util/config' import { useNotify } from '@/hooks/notification' diff --git a/frontend/src/components/Page.tsx b/frontend/src/components/Page.tsx index 70e68110..4afa956a 100644 --- a/frontend/src/components/Page.tsx +++ b/frontend/src/components/Page.tsx @@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom' import { ReactNode, createContext, useContext, useState, Context } from 'react' import { useUser } from '@/hooks/user' import { Box } from '@mui/material' -import { Role } from '@/types' +import { Role } from '@/shared/types' import { UserState } from '@/redux/userReducer' import { ENABLE_WRITE } from '@/util/config' diff --git a/frontend/src/components/pages.tsx b/frontend/src/components/pages.tsx index 3993a9c7..97e047ad 100644 --- a/frontend/src/components/pages.tsx +++ b/frontend/src/components/pages.tsx @@ -7,6 +7,7 @@ import { PersonDetailsType, ProjectDetailsType, RegionDetails as RegionDetailsType, + Role, } from '@/shared/types' import { CrossSearchTable } from './CrossSearch/CrossSearchTable' import { LocalityDetails } from './Locality/LocalityDetails' @@ -26,7 +27,6 @@ import { ProjectTable } from './Project/ProjectTable' import { ProjectDetails } from './Project/ProjectDetails' import { RegionTable } from './Region/RegionTable' import { RegionDetails } from './Region/RegionDetails' -import { Role } from '@/types' import { UserState } from '@/redux/userReducer' const noRights: EditRights = {} diff --git a/frontend/src/redux/userReducer.ts b/frontend/src/redux/userReducer.ts index af1c2752..7a3cd266 100644 --- a/frontend/src/redux/userReducer.ts +++ b/frontend/src/redux/userReducer.ts @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { api } from '@/redux/api' -import { Role } from '@/types' +import { Role } from '@/shared/types' export type LoginResponse = { token: string diff --git a/frontend/src/shared/types.ts b/frontend/src/shared/types/data.ts similarity index 82% rename from frontend/src/shared/types.ts rename to frontend/src/shared/types/data.ts index 0f56edb5..5d8f8d13 100644 --- a/frontend/src/shared/types.ts +++ b/frontend/src/shared/types/data.ts @@ -1,44 +1,8 @@ -import * as Prisma from '../../../backend/prisma/generated/now_test_client/default' -import * as LogPrisma from '../../../backend/prisma/generated/now_log_test_client/default' -import { Role } from '../types' - -export type User = { - username: string - role: Role - userId: number - initials: string -} - -/* This makes all fields optional, except for array-type fields. - Also wraps the objects inside arrays as Editable, adding rowState-field in them. - Applies itself recursively to all nested objects even inside arrays. */ -export type EditDataType = T extends object - ? T extends readonly unknown[] - ? { [I in keyof T]: Editable> } - : { [K in keyof T as T[K] extends readonly unknown[] ? K : never]: EditDataType } & { - [K in keyof T as T[K] extends readonly unknown[] ? never : K]?: EditDataType - } extends infer U - ? { [K in keyof U]: U[K] } - : never - : T - -// Changes all bigints to number type including in nested objects -export type FixBigInt = { - [K in keyof T]: T[K] extends bigint | null - ? number | null - : T[K] extends (infer U)[] - ? FixBigInt[] - : T[K] extends object - ? FixBigInt - : T[K] -} +import * as Prisma from '../../../../backend/prisma/generated/now_test_client/default' +import * as LogPrisma from '../../../../backend/prisma/generated/now_log_test_client/default' +import { Editable, FixBigInt, RowState } from './util' export type SpeciesType = FixBigInt -export type RowState = 'new' | 'removed' | 'cancelled' | 'clean' - -// Use this for fields that include array that has to be edited by EditableTable. -// For example see LocalityDetails: museums field -export type Editable = T & { rowState?: RowState } export type SedimentaryStructureValues = Prisma.now_ss_values export type CollectingMethod = Prisma.now_coll_meth export type LocalityProject = Prisma.now_plr & { now_proj: Prisma.now_proj } @@ -291,16 +255,6 @@ export type ReferenceAuthorType = { data_id?: number } -export type ValidationErrors = { - status: string - data: ValidationErrorItem[] -} - -type ValidationErrorItem = { - name: string - error: string -} - type Geoname = { adminCode01: string lng: number diff --git a/frontend/src/shared/types/index.ts b/frontend/src/shared/types/index.ts new file mode 100644 index 00000000..a1dc5706 --- /dev/null +++ b/frontend/src/shared/types/index.ts @@ -0,0 +1,3 @@ +export * from './data' +export * from './misc' +export * from './util' diff --git a/frontend/src/shared/types/misc.ts b/frontend/src/shared/types/misc.ts new file mode 100644 index 00000000..bf811b23 --- /dev/null +++ b/frontend/src/shared/types/misc.ts @@ -0,0 +1,26 @@ +export enum Role { + Admin = 1, + EditUnrestricted = 2, + EditRestricted = 3, + NowOffice = 4, + Project = 5, + ProjectPrivate = 6, + ReadOnly = 7, +} + +export type User = { + username: string + role: Role + userId: number + initials: string +} + +export type ValidationErrors = { + status: string + data: ValidationErrorItem[] +} + +type ValidationErrorItem = { + name: string + error: string +} diff --git a/frontend/src/shared/types/util.ts b/frontend/src/shared/types/util.ts new file mode 100644 index 00000000..cdd80e67 --- /dev/null +++ b/frontend/src/shared/types/util.ts @@ -0,0 +1,29 @@ +/* This makes all fields optional, except for array-type fields. + Also wraps the objects inside arrays as Editable, adding rowState-field in them. + Applies itself recursively to all nested objects even inside arrays. */ +export type EditDataType = T extends object + ? T extends readonly unknown[] + ? { [I in keyof T]: Editable> } + : { [K in keyof T as T[K] extends readonly unknown[] ? K : never]: EditDataType } & { + [K in keyof T as T[K] extends readonly unknown[] ? never : K]?: EditDataType + } extends infer U + ? { [K in keyof U]: U[K] } + : never + : T + +// Changes all bigints to number type including in nested objects +export type FixBigInt = { + [K in keyof T]: T[K] extends bigint | null + ? number | null + : T[K] extends (infer U)[] + ? FixBigInt[] + : T[K] extends object + ? FixBigInt + : T[K] +} + +export type RowState = 'new' | 'removed' | 'cancelled' | 'clean' + +// Use this for fields that include array that has to be edited by EditableTable. +// For example see LocalityDetails: museums field +export type Editable = T & { rowState?: RowState } diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 2440ca8e..89a90b41 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -1,13 +1,3 @@ -export enum Role { - Admin = 1, - EditUnrestricted = 2, - EditRestricted = 3, - NowOffice = 4, - Project = 5, - ProjectPrivate = 6, - ReadOnly = 7, -} - import { EditDataType, LocalityDetailsType } from './shared/types' export const calculateMeanHypsodonty = (locality: LocalityDetailsType) => {