chore: migrating to nodejs 22

This commit is contained in:
Rafael Taranto 2025-03-08 12:28:23 +00:00
parent 0296f86060
commit 2e31ab391f
47 changed files with 16384 additions and 11137 deletions

View file

@ -1 +1 @@
nodejs 14 nodejs 22

View file

@ -3,7 +3,7 @@
require('../lib/environment-helper') require('../lib/environment-helper')
const { asyncLocalStorage, defaultStore } = require('../lib/async-storage') const { asyncLocalStorage, defaultStore } = require('../lib/async-storage')
const userManagement = require('../lib/new-admin/graphql/modules/userManagement') const userManagement = require('../lib/new-admin/graphql/modules/userManagement')
const authErrors = require('../lib/new-admin/graphql/errors/authentication') const authErrors = require('../lib/new-admin/graphql/errors')
const name = process.argv[2] const name = process.argv[2]
const role = process.argv[3] const role = process.argv[3]

View file

@ -1,5 +0,0 @@
#!/usr/bin/env node
const adminServer = require('../lib/new-admin/graphql-dev-insecure')
adminServer.run()

View file

@ -1,11 +1,10 @@
const fs = require('fs') const fs = require('fs')
const http = require('http')
const https = require('https') const https = require('https')
const argv = require('minimist')(process.argv.slice(2)) const argv = require('minimist')(process.argv.slice(2))
require('./environment-helper') require('./environment-helper')
const { asyncLocalStorage, defaultStore } = require('./async-storage') const { asyncLocalStorage, defaultStore } = require('./async-storage')
const routes = require('./routes') const { loadRoutes } = require('./routes')
const logger = require('./logger') const logger = require('./logger')
const poller = require('./poller') const poller = require('./poller')
const settingsLoader = require('./new-settings-loader') const settingsLoader = require('./new-settings-loader')
@ -16,8 +15,7 @@ const ofacUpdate = require('./ofac/update')
const KEY_PATH = process.env.KEY_PATH const KEY_PATH = process.env.KEY_PATH
const CERT_PATH = process.env.CERT_PATH const CERT_PATH = process.env.CERT_PATH
const CA_PATH = process.env.CA_PATH
const devMode = argv.dev
const version = require('../package.json').version const version = require('../package.json').version
logger.info('Version: %s', version) logger.info('Version: %s', version)
@ -40,7 +38,7 @@ function run () {
.then(settings => { .then(settings => {
clearInterval(handler) clearInterval(handler)
return loadSanctions(settings) return loadSanctions(settings)
.then(() => startServer(settings)) .then(startServer)
.then(resolve) .then(resolve)
}) })
.catch(errorHandler) .catch(errorHandler)
@ -68,30 +66,27 @@ function loadSanctions (settings) {
}) })
} }
function startServer (settings) { async function startServer () {
return Promise.resolve() const app = await loadRoutes()
.then(() => {
poller.setup(['public']) poller.setup(['public'])
const httpsServerOptions = { const httpsServerOptions = {
key: fs.readFileSync(KEY_PATH), key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(CERT_PATH), cert: fs.readFileSync(CERT_PATH),
ca: fs.readFileSync(CA_PATH),
requestCert: true, requestCert: true,
rejectUnauthorized: false rejectUnauthorized: false
} }
const server = devMode const server = https.createServer(httpsServerOptions, app)
? http.createServer(routes.app)
: https.createServer(httpsServerOptions, routes.app)
const port = argv.port || 3000 const port = argv.port || 3000
if (devMode) logger.info('In dev mode') await new Promise((resolve) =>
server.listen({ port }, resolve),
server.listen(port, () => { )
logger.info('lamassu-server listening on port ' + logger.info(`lamassu-server listening on port ${port}`)
port + ' ' + (devMode ? '(http)' : '(https)'))
})
})
} }
module.exports = { run } module.exports = { run }

View file

@ -276,6 +276,7 @@ function deleteEditedData (id, data) {
async function updateEditedPhoto (id, photo, photoType) { async function updateEditedPhoto (id, photo, photoType) {
const newPatch = {} const newPatch = {}
const baseDir = photoType === 'frontCamera' ? FRONT_CAMERA_DIR : ID_PHOTO_CARD_DIR const baseDir = photoType === 'frontCamera' ? FRONT_CAMERA_DIR : ID_PHOTO_CARD_DIR
console.log('photo', photo)
const { createReadStream, filename } = photo const { createReadStream, filename } = photo
const stream = createReadStream() const stream = createReadStream()

View file

@ -1,27 +1,27 @@
const logger = require('../logger') const logger = require('../logger')
const https = require('https') const { ApolloServer } = require('@apollo/server')
const { ApolloServer } = require('apollo-server-express')
const devMode = !!require('minimist')(process.argv.slice(2)).dev const devMode = !!require('minimist')(process.argv.slice(2)).dev
module.exports = new ApolloServer({ const context = ({ req, res }) => ({
typeDefs: require('./types'),
resolvers: require('./resolvers'),
context: ({ req, res }) => ({
deviceId: req.deviceId, /* lib/middlewares/populateDeviceId.js */ deviceId: req.deviceId, /* lib/middlewares/populateDeviceId.js */
deviceName: req.deviceName, /* lib/middlewares/authorize.js */ deviceName: req.deviceName, /* lib/middlewares/authorize.js */
operatorId: res.locals.operatorId, /* lib/middlewares/operatorId.js */ operatorId: res.locals.operatorId, /* lib/middlewares/operatorId.js */
pid: req.query.pid, pid: req.query.pid,
settings: req.settings, /* lib/middlewares/populateSettings.js */ settings: req.settings, /* lib/middlewares/populateSettings.js */
}), })
uploads: false,
playground: false, const graphQLServer = new ApolloServer({
typeDefs: require('./types'),
resolvers: require('./resolvers'),
introspection: false, introspection: false,
formatError: error => { formatError: error => {
logger.error(error) logger.error(error)
return error return error
}, },
debug: devMode, includeStacktraceInErrorResponses: devMode,
logger logger
}) })
module.exports = { graphQLServer, context }

View file

@ -1,4 +1,5 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
module.exports = gql` module.exports = gql`
type Coin { type Coin {
cryptoCode: String! cryptoCode: String!

View file

@ -13,7 +13,7 @@ const dbm = require('./postgresql_interface')
const configManager = require('./new-config-manager') const configManager = require('./new-config-manager')
const notifierUtils = require('./notifier/utils') const notifierUtils = require('./notifier/utils')
const notifierQueries = require('./notifier/queries') const notifierQueries = require('./notifier/queries')
const { ApolloError } = require('apollo-server-errors'); const { GraphQLError } = require('graphql');
const { loadLatestConfig } = require('./new-settings-loader') const { loadLatestConfig } = require('./new-settings-loader')
const logger = require('./logger') const logger = require('./logger')
@ -154,7 +154,7 @@ function getMachine (machineId, config) {
const sql = `${MACHINE_WITH_CALCULATED_FIELD_SQL} WHERE d.device_id = $1` const sql = `${MACHINE_WITH_CALCULATED_FIELD_SQL} WHERE d.device_id = $1`
const queryMachine = db.oneOrNone(sql, [machineId]).then(r => { const queryMachine = db.oneOrNone(sql, [machineId]).then(r => {
if (r === null) throw new ApolloError('Resource doesn\'t exist', 'NOT_FOUND') if (r === null) throw new GraphQLError('Resource doesn\'t exist', { extensions: { code: 'NOT_FOUND' } })
else return toMachineObject(r) else return toMachineObject(r)
}) })

View file

@ -1,8 +1,5 @@
const _ = require('lodash/fp')
const crypto = require('crypto') const crypto = require('crypto')
const logger = require('../logger')
function sha256 (buf) { function sha256 (buf) {
if (!buf) return null if (!buf) return null
const hash = crypto.createHash('sha256') const hash = crypto.createHash('sha256')
@ -12,9 +9,8 @@ function sha256 (buf) {
} }
const populateDeviceId = function (req, res, next) { const populateDeviceId = function (req, res, next) {
const deviceId = _.isFunction(req.connection.getPeerCertificate) const peerCert = req.socket.getPeerCertificate ? req.socket.getPeerCertificate() : null
? sha256(req.connection.getPeerCertificate()?.raw) const deviceId = peerCert?.raw ? sha256(peerCert.raw) : null
: null
if (!deviceId) return res.status(500).json({ error: 'Unable to find certificate' }) if (!deviceId) return res.status(500).json({ error: 'Unable to find certificate' })
req.deviceId = deviceId req.deviceId = deviceId

View file

@ -4,19 +4,23 @@ const path = require('path')
const express = require('express') const express = require('express')
const https = require('https') const https = require('https')
const serveStatic = require('serve-static') const serveStatic = require('serve-static')
const cors = require('cors')
const helmet = require('helmet') const helmet = require('helmet')
const nocache = require('nocache') const nocache = require('nocache')
const cookieParser = require('cookie-parser') const cookieParser = require('cookie-parser')
const { graphqlUploadExpress } = require('graphql-upload') const { ApolloServer } = require('@apollo/server')
const { ApolloServer } = require('apollo-server-express') const { expressMiddleware } = require('@apollo/server/express4')
const { ApolloServerPluginLandingPageDisabled } = require('@apollo/server/plugin/disabled')
const { ApolloServerPluginLandingPageLocalDefault } = require('@apollo/server/plugin/landingPage/default')
const { mergeResolvers } = require('@graphql-tools/merge')
const { makeExecutableSchema } = require('@graphql-tools/schema')
require('../environment-helper') require('../environment-helper')
const { asyncLocalStorage, defaultStore } = require('../async-storage') const { asyncLocalStorage, defaultStore } = require('../async-storage')
const logger = require('../logger') const logger = require('../logger')
const exchange = require('../exchange') const exchange = require('../exchange')
const { AuthDirective } = require('./graphql/directives') const { authDirectiveTransformer } = require('./graphql/directives')
const { typeDefs, resolvers } = require('./graphql/schema') const { typeDefs, resolvers } = require('./graphql/schema')
const findOperatorId = require('../middlewares/operatorId') const findOperatorId = require('../middlewares/operatorId')
const computeSchema = require('../compute-schema') const computeSchema = require('../compute-schema')
@ -28,6 +32,7 @@ const devMode = require('minimist')(process.argv.slice(2)).dev
const HOSTNAME = process.env.HOSTNAME const HOSTNAME = process.env.HOSTNAME
const KEY_PATH = process.env.KEY_PATH const KEY_PATH = process.env.KEY_PATH
const CERT_PATH = process.env.CERT_PATH const CERT_PATH = process.env.CERT_PATH
const CA_PATH = process.env.CA_PATH
const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR
const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR
const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR const OPERATOR_DATA_DIR = process.env.OPERATOR_DATA_DIR
@ -37,64 +42,79 @@ if (!HOSTNAME) {
process.exit(1) process.exit(1)
} }
const app = express() const loadRoutes = async () => {
const app = express()
app.use(helmet()) app.use(helmet())
app.use(compression()) app.use(compression())
app.use(nocache()) app.use(nocache())
app.use(cookieParser()) app.use(cookieParser())
app.use(express.json()) app.use(express.json())
app.use(express.urlencoded({ extended: true })) // support encoded bodies app.use(express.urlencoded({ extended: true })) // support encoded bodies
app.use(express.static(path.resolve(__dirname, '..', '..', 'public'))) app.use(express.static(path.resolve(__dirname, '..', '..', 'public')))
app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL)) app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL))
app.use(computeSchema) app.use(computeSchema)
app.use(findOperatorId) app.use(findOperatorId)
app.use(session) app.use(session)
app.use(graphqlUploadExpress())
const apolloServer = new ApolloServer({ // Dynamic import for graphql-upload since it's not a CommonJS module
const { default: graphqlUploadExpress } = await import('graphql-upload/graphqlUploadExpress.mjs')
const { default: GraphQLUpload } = await import('graphql-upload/GraphQLUpload.mjs')
app.use(graphqlUploadExpress())
const schema = makeExecutableSchema({
typeDefs, typeDefs,
resolvers, resolvers: mergeResolvers(resolvers, { Upload: GraphQLUpload }),
uploads: false, })
schemaDirectives: { const schemaWithDirectives = authDirectiveTransformer(schema)
auth: AuthDirective
}, const apolloServer = new ApolloServer({
playground: false, schema: schemaWithDirectives,
csrfPrevention: false,
introspection: false, introspection: false,
formatError: error => { formatError: (formattedError, error) => {
const exception = error?.extensions?.exception logger.error(error, JSON.stringify(error?.extensions || {}))
logger.error(error, JSON.stringify(exception || {})) return formattedError
return error
}, },
context: async (obj) => buildApolloContext(obj) plugins: [
}) devMode
? ApolloServerPluginLandingPageLocalDefault()
: ApolloServerPluginLandingPageDisabled()
]
})
apolloServer.applyMiddleware({ await apolloServer.start();
app,
cors: {
credentials: true,
origin: devMode && 'https://localhost:3001'
}
})
// cors on app for /api/register endpoint. app.use(
app.use(cors({ credentials: true, origin: devMode && 'https://localhost:3001' })) '/graphql',
express.json(),
expressMiddleware(apolloServer, {
context: async ({ req, res }) => buildApolloContext({ req, res })
})
);
app.use('/id-card-photo', serveStatic(ID_PHOTO_CARD_DIR, { index: false }))
app.use('/front-camera-photo', serveStatic(FRONT_CAMERA_DIR, { index: false }))
app.use('/operator-data', serveStatic(OPERATOR_DATA_DIR, { index: false }))
// Everything not on graphql or api/register is redirected to the front-end app.use('/id-card-photo', serveStatic(ID_PHOTO_CARD_DIR, { index: false }))
app.get('*', (req, res) => res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html'))) app.use('/front-camera-photo', serveStatic(FRONT_CAMERA_DIR, { index: false }))
app.use('/operator-data', serveStatic(OPERATOR_DATA_DIR, { index: false }))
// Everything not on graphql or api/register is redirected to the front-end
app.get('*', (req, res) => res.sendFile(path.resolve(__dirname, '..', '..', 'public', 'index.html')))
return app
}
const certOptions = { const certOptions = {
key: fs.readFileSync(KEY_PATH), key: fs.readFileSync(KEY_PATH),
cert: fs.readFileSync(CERT_PATH) cert: fs.readFileSync(CERT_PATH),
ca: fs.readFileSync(CA_PATH)
} }
function run () { function run () {
const store = defaultStore() const store = defaultStore()
asyncLocalStorage.run(store, () => { asyncLocalStorage.run(store, async () => {
const app = await loadRoutes()
const serverPort = devMode ? 8070 : 443 const serverPort = devMode ? 8070 : 443
const serverLog = `lamassu-admin-server listening on port ${serverPort}` const serverLog = `lamassu-admin-server listening on port ${serverPort}`

View file

@ -1,24 +0,0 @@
const express = require('express')
const { ApolloServer } = require('apollo-server-express')
require('../environment-helper')
const { typeDefs, resolvers } = require('./graphql/schema')
const logger = require('../logger')
const app = express()
const server = new ApolloServer({
typeDefs,
resolvers
})
server.applyMiddleware({ app })
app.use(express.json())
function run () {
const serverLog = `lamassu-admin-server listening on port ${9010}${server.graphqlPath}`
app.listen(9010, () => logger.info(serverLog))
}
module.exports = { run }

View file

@ -1,40 +1,49 @@
const _ = require('lodash/fp') const _ = require('lodash/fp')
const { mapSchema, getDirective, MapperKind } = require('@graphql-tools/utils')
const { SchemaDirectiveVisitor, AuthenticationError } = require('apollo-server-express')
const { defaultFieldResolver } = require('graphql') const { defaultFieldResolver } = require('graphql')
class AuthDirective extends SchemaDirectiveVisitor { const { AuthenticationError } = require('../errors')
visitObject (type) {
this.ensureFieldsWrapped(type) function authDirectiveTransformer(schema, directiveName = 'auth') {
type._requiredAuthRole = this.args.requires return mapSchema(schema, {
// For object types
[MapperKind.OBJECT_TYPE]: (objectType) => {
const directive = getDirective(schema, objectType, directiveName)?.[0]
if (directive) {
const requiredAuthRole = directive.requires
objectType._requiredAuthRole = requiredAuthRole
}
return objectType
},
// For field definitions
[MapperKind.OBJECT_FIELD]: (fieldConfig, _fieldName, typeName) => {
const directive = getDirective(schema, fieldConfig, directiveName)?.[0]
if (directive) {
const requiredAuthRole = directive.requires
fieldConfig._requiredAuthRole = requiredAuthRole
} }
visitFieldDefinition (field, details) { // Get the parent object type
this.ensureFieldsWrapped(details.objectType) const objectType = schema.getType(typeName)
field._requiredAuthRole = this.args.requires
}
ensureFieldsWrapped (objectType) { // Apply auth check to the field's resolver
if (objectType._authFieldsWrapped) return const { resolve = defaultFieldResolver } = fieldConfig
objectType._authFieldsWrapped = true fieldConfig.resolve = function (root, args, context, info) {
const requiredRoles = fieldConfig._requiredAuthRole || objectType._requiredAuthRole
const fields = objectType.getFields()
_.forEach(fieldName => {
const field = fields[fieldName]
const { resolve = defaultFieldResolver } = field
field.resolve = function (root, args, context, info) {
const requiredRoles = field._requiredAuthRole ? field._requiredAuthRole : objectType._requiredAuthRole
if (!requiredRoles) return resolve.apply(this, [root, args, context, info]) if (!requiredRoles) return resolve.apply(this, [root, args, context, info])
const user = context.req.session.user const user = context.req.session.user
if (!user || !_.includes(_.upperCase(user.role), requiredRoles)) throw new AuthenticationError('You do not have permission to access this resource!') if (!user || !_.includes(_.upperCase(user.role), requiredRoles)) {
throw new AuthenticationError('You do not have permission to access this resource!')
}
return resolve.apply(this, [root, args, context, info]) return resolve.apply(this, [root, args, context, info])
} }
}, _.keys(fields))
return fieldConfig
} }
})
} }
module.exports = AuthDirective module.exports = authDirectiveTransformer

View file

@ -1,3 +1,3 @@
const AuthDirective = require('./auth') const authDirectiveTransformer = require('./auth')
module.exports = { AuthDirective } module.exports = { authDirectiveTransformer }

View file

@ -0,0 +1,71 @@
const { GraphQLError } = require('graphql')
const { ApolloServerErrorCode } = require('@apollo/server/errors')
class AuthenticationError extends GraphQLError {
constructor() {
super('Authentication failed', {
extensions: {
code: 'UNAUTHENTICATED'
}
})
}
}
class InvalidCredentialsError extends GraphQLError {
constructor() {
super('Invalid credentials', {
extensions: {
code: 'INVALID_CREDENTIALS'
}
})
}
}
class UserAlreadyExistsError extends GraphQLError {
constructor() {
super('User already exists', {
extensions: {
code: 'USER_ALREADY_EXISTS'
}
})
}
}
class InvalidTwoFactorError extends GraphQLError {
constructor() {
super('Invalid two-factor code', {
extensions: {
code: 'INVALID_TWO_FACTOR_CODE'
}
})
}
}
class InvalidUrlError extends GraphQLError {
constructor() {
super('Invalid URL token', {
extensions: {
code: 'INVALID_URL_TOKEN'
}
})
}
}
class UserInputError extends GraphQLError {
constructor() {
super('User input error', {
extensions: {
code: ApolloServerErrorCode.BAD_USER_INPUT
}
})
}
}
module.exports = {
AuthenticationError,
InvalidCredentialsError,
UserAlreadyExistsError,
InvalidTwoFactorError,
InvalidUrlError,
UserInputError
}

View file

@ -1,37 +0,0 @@
const { ApolloError, AuthenticationError } = require('apollo-server-express')
class InvalidCredentialsError extends ApolloError {
constructor(message) {
super(message, 'INVALID_CREDENTIALS')
Object.defineProperty(this, 'name', { value: 'InvalidCredentialsError' })
}
}
class UserAlreadyExistsError extends ApolloError {
constructor(message) {
super(message, 'USER_ALREADY_EXISTS')
Object.defineProperty(this, 'name', { value: 'UserAlreadyExistsError' })
}
}
class InvalidTwoFactorError extends ApolloError {
constructor(message) {
super(message, 'INVALID_TWO_FACTOR_CODE')
Object.defineProperty(this, 'name', { value: 'InvalidTwoFactorError' })
}
}
class InvalidUrlError extends ApolloError {
constructor(message) {
super(message, 'INVALID_URL_TOKEN')
Object.defineProperty(this, 'name', { value: 'InvalidUrlError' })
}
}
module.exports = {
AuthenticationError,
InvalidCredentialsError,
UserAlreadyExistsError,
InvalidTwoFactorError,
InvalidUrlError
}

View file

@ -8,7 +8,7 @@ const loginHelper = require('../../services/login')
const T = require('../../../time') const T = require('../../../time')
const users = require('../../../users') const users = require('../../../users')
const sessionManager = require('../../../session-manager') const sessionManager = require('../../../session-manager')
const authErrors = require('../errors/authentication') const authErrors = require('../errors')
const credentials = require('../../../hardware-credentials') const credentials = require('../../../hardware-credentials')
const REMEMBER_ME_AGE = 90 * T.day const REMEMBER_ME_AGE = 90 * T.day

View file

@ -1,13 +1,9 @@
const { GraphQLDateTime } = require('graphql-iso-date') const { DateTimeISOResolver, JSONResolver, JSONObjectResolver } = require('graphql-scalars')
const { GraphQLJSON, GraphQLJSONObject } = require('graphql-type-json')
const { GraphQLUpload } = require('graphql-upload')
GraphQLDateTime.name = 'Date'
const resolvers = { const resolvers = {
JSON: GraphQLJSON, JSON: JSONResolver,
JSONObject: GraphQLJSONObject, JSONObject: JSONObjectResolver,
Date: GraphQLDateTime, Date: DateTimeISOResolver
UploadGQL: GraphQLUpload
} }
module.exports = resolvers module.exports = resolvers

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Bill { type Bill {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Blacklist { type Blacklist {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type CashboxBatch { type CashboxBatch {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Country { type Country {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Currency { type Currency {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Customer { type Customer {
@ -6,7 +6,7 @@ const typeDef = gql`
authorizedOverride: String authorizedOverride: String
daysSuspended: Int daysSuspended: Int
isSuspended: Boolean isSuspended: Boolean
newPhoto: UploadGQL newPhoto: Upload
photoType: String photoType: String
frontCameraPath: String frontCameraPath: String
frontCameraAt: Date frontCameraAt: Date
@ -18,7 +18,7 @@ const typeDef = gql`
idCardData: JSONObject idCardData: JSONObject
idCardDataOverride: String idCardDataOverride: String
idCardDataExpiration: Date idCardDataExpiration: Date
idCardPhoto: UploadGQL idCardPhoto: Upload
idCardPhotoPath: String idCardPhotoPath: String
idCardPhotoOverride: String idCardPhotoOverride: String
idCardPhotoAt: Date idCardPhotoAt: Date
@ -74,7 +74,7 @@ const typeDef = gql`
input CustomerEdit { input CustomerEdit {
idCardData: JSONObject idCardData: JSONObject
idCardPhoto: UploadGQL idCardPhoto: Upload
usSsn: String usSsn: String
subscriberInfo: JSONObject subscriberInfo: JSONObject
} }
@ -108,7 +108,7 @@ const typeDef = gql`
removeCustomField(customerId: ID!, fieldId: ID!): Boolean @auth removeCustomField(customerId: ID!, fieldId: ID!): Boolean @auth
editCustomer(customerId: ID!, customerEdit: CustomerEdit): Customer @auth editCustomer(customerId: ID!, customerEdit: CustomerEdit): Customer @auth
deleteEditedData(customerId: ID!, customerEdit: CustomerEdit): Customer @auth deleteEditedData(customerId: ID!, customerEdit: CustomerEdit): Customer @auth
replacePhoto(customerId: ID!, photoType: String, newPhoto: UploadGQL): Customer @auth replacePhoto(customerId: ID!, photoType: String, newPhoto: Upload): Customer @auth
createCustomerNote(customerId: ID!, title: String!, content: String!): Boolean @auth createCustomerNote(customerId: ID!, title: String!, content: String!): Boolean @auth
editCustomerNote(noteId: ID!, newContent: String!): Boolean @auth editCustomerNote(noteId: ID!, newContent: String!): Boolean @auth
deleteCustomerNote(noteId: ID!): Boolean @auth deleteCustomerNote(noteId: ID!): Boolean @auth

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type CoinFunds { type CoinFunds {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type MachineLog { type MachineLog {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type IndividualDiscount { type IndividualDiscount {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type MachineStatus { type MachineStatus {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Query { type Query {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Notification { type Notification {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Mutation { type Mutation {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Rate { type Rate {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type SanctionMatches { type SanctionMatches {

View file

@ -1,10 +1,10 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
scalar JSON scalar JSON
scalar JSONObject scalar JSONObject
scalar Date scalar Date
scalar UploadGQL scalar Upload
` `
module.exports = typeDef module.exports = typeDef

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Query { type Query {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type SMSNotice { type SMSNotice {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type ProcessStatus { type ProcessStatus {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Transaction { type Transaction {

View file

@ -1,4 +1,4 @@
const { gql } = require('apollo-server-express') const gql = require('graphql-tag')
const typeDef = gql` const typeDef = gql`
type Query { type Query {

View file

@ -1,6 +1,7 @@
const { AuthenticationError } = require('apollo-server-express')
const users = require('../../users') const users = require('../../users')
const { AuthenticationError } = require('../graphql/errors')
const buildApolloContext = async ({ req, res }) => { const buildApolloContext = async ({ req, res }) => {
if (!req.session.user) return { req, res } if (!req.session.user) return { req, res }

View file

@ -1,5 +1,5 @@
const machineLoader = require('../../machine-loader') const machineLoader = require('../../machine-loader')
const { UserInputError } = require('apollo-server-express') const { UserInputError } = require('../graphql/errors')
function getMachine (machineId) { function getMachine (machineId) {
return machineLoader.getMachines() return machineLoader.getMachines()

View file

@ -1,5 +1,4 @@
const express = require('express') const express = require('express')
const argv = require('minimist')(process.argv.slice(2))
const compression = require('compression') const compression = require('compression')
const helmet = require('helmet') const helmet = require('helmet')
const morgan = require('morgan') const morgan = require('morgan')
@ -35,11 +34,14 @@ const verifyPromoCodeRoutes = require('./routes/verifyPromoCodeRoutes')
const probeRoutes = require('./routes/probeLnRoutes') const probeRoutes = require('./routes/probeLnRoutes')
const failedQRScansRoutes = require('./routes/failedQRScans') const failedQRScansRoutes = require('./routes/failedQRScans')
const graphQLServer = require('./graphql/server') const { graphQLServer, context } = require('./graphql/server')
const app = express() const { expressMiddleware } = require('@apollo/server/express4')
const configRequiredRoutes = [ const loadRoutes = async () => {
const app = express()
const configRequiredRoutes = [
'/poll', '/poll',
'/terms_conditions', '/terms_conditions',
'/event', '/event',
@ -48,71 +50,81 @@ const configRequiredRoutes = [
'/tx', '/tx',
'/verify_promo_code', '/verify_promo_code',
'/graphql' '/graphql'
] ]
// middleware setup // middleware setup
app.use(addRWBytes()) app.use(addRWBytes())
app.use(compression({ threshold: 500 })) app.use(compression({ threshold: 500 }))
app.use(helmet()) app.use(helmet())
app.use(nocache()) app.use(nocache())
app.use(express.json({ limit: '2mb' })) app.use(express.json({ limit: '2mb' }))
morgan.token('bytesRead', (_req, res) => res.bytesRead) morgan.token('bytesRead', (_req, res) => res.bytesRead)
morgan.token('bytesWritten', (_req, res) => res.bytesWritten) morgan.token('bytesWritten', (_req, res) => res.bytesWritten)
app.use(morgan(':method :url :status :response-time ms -- :bytesRead/:bytesWritten B', { stream: logger.stream })) app.use(morgan(':method :url :status :response-time ms -- :bytesRead/:bytesWritten B', { stream: logger.stream }))
app.use('/robots.txt', (req, res) => { app.use('/robots.txt', (req, res) => {
res.type('text/plain') res.type('text/plain')
res.send("User-agent: *\nDisallow: /") res.send("User-agent: *\nDisallow: /")
}) })
app.get('/', (req, res) => { app.get('/', (req, res) => {
res.sendStatus(404) res.sendStatus(404)
}) })
// app /pair and /ca routes // app /pair and /ca routes
app.use('/', pairingRoutes) app.use('/', pairingRoutes)
app.use(findOperatorId) app.use(findOperatorId)
app.use(populateDeviceId) app.use(populateDeviceId)
app.use(computeSchema) app.use(computeSchema)
app.use(authorize) app.use(authorize)
app.use(configRequiredRoutes, populateSettings) app.use(configRequiredRoutes, populateSettings)
app.use(filterOldRequests) app.use(filterOldRequests)
// other app routes // other app routes
app.use('/graphql', recordPing) app.use('/graphql', recordPing)
app.use('/poll', pollingRoutes) app.use('/poll', pollingRoutes)
app.use('/terms_conditions', termsAndConditionsRoutes) app.use('/terms_conditions', termsAndConditionsRoutes)
app.use('/state', stateRoutes) app.use('/state', stateRoutes)
app.use('/cashbox', cashboxRoutes) app.use('/cashbox', cashboxRoutes)
app.use('/network', performanceRoutes) app.use('/network', performanceRoutes)
app.use('/diagnostics', diagnosticsRoutes) app.use('/diagnostics', diagnosticsRoutes)
app.use('/failedqrscans', failedQRScansRoutes) app.use('/failedqrscans', failedQRScansRoutes)
app.use('/verify_user', verifyUserRoutes) app.use('/verify_user', verifyUserRoutes)
app.use('/verify_transaction', verifyTxRoutes) app.use('/verify_transaction', verifyTxRoutes)
app.use('/verify_promo_code', verifyPromoCodeRoutes) app.use('/verify_promo_code', verifyPromoCodeRoutes)
// BACKWARDS_COMPATIBILITY 9.0 // BACKWARDS_COMPATIBILITY 9.0
// machines before 9.0 still use the phone_code route // machines before 9.0 still use the phone_code route
app.use('/phone_code', phoneCodeRoutes) app.use('/phone_code', phoneCodeRoutes)
app.use('/customer', customerRoutes) app.use('/customer', customerRoutes)
app.use('/tx', txRoutes) app.use('/tx', txRoutes)
app.use('/logs', logsRoutes) app.use('/logs', logsRoutes)
app.use('/units', unitsRoutes) app.use('/units', unitsRoutes)
app.use('/probe', probeRoutes) app.use('/probe', probeRoutes)
graphQLServer.applyMiddleware({ app }) await graphQLServer.start()
app.use('/graphql',
express.json(),
expressMiddleware(graphQLServer, {
context,
}),
);
app.use(errorHandler) app.use(errorHandler)
app.use((req, res) => { app.use((req, res) => {
res.status(404).json({ error: 'No such route' }) res.status(404).json({ error: 'No such route' })
}) })
module.exports = { app } return app
}
module.exports = { loadRoutes }

View file

@ -1 +0,0 @@
nodejs 22

View file

@ -1,20 +0,0 @@
with import (fetchTarball {
name = "nixpkgs-194846768975b7ad2c4988bdb82572c00222c0d7";
url = https://github.com/NixOS/nixpkgs/archive/194846768975b7ad2c4988bdb82572c00222c0d7.tar.gz;
sha256 = "0snj72i9dm99jlnnmk8id8ffjnfg1k81lr7aw8d01kz3hdiraqil";
}) {};
stdenv.mkDerivation {
name = "node";
buildInputs = [
nodejs_22
openssl
python3
entr
yasm
];
shellHook = ''
export PATH="$PWD/node_modules/.bin/:$PATH"
'';
}

31347
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
"license": "./LICENSE", "license": "./LICENSE",
"author": "Lamassu (https://lamassu.is)", "author": "Lamassu (https://lamassu.is)",
"dependencies": { "dependencies": {
"@apollo/server": "^4.11.3",
"@bitgo/sdk-api": "1.33.0", "@bitgo/sdk-api": "1.33.0",
"@bitgo/sdk-coin-bch": "1.5.22", "@bitgo/sdk-coin-bch": "1.5.22",
"@bitgo/sdk-coin-btc": "1.7.22", "@bitgo/sdk-coin-btc": "1.7.22",
@ -14,7 +15,9 @@
"@bitgo/sdk-coin-zec": "1.5.22", "@bitgo/sdk-coin-zec": "1.5.22",
"@ethereumjs/common": "^2.6.4", "@ethereumjs/common": "^2.6.4",
"@ethereumjs/tx": "^3.5.1", "@ethereumjs/tx": "^3.5.1",
"@graphql-tools/merge": "^6.2.5", "@graphql-tools/merge": "^9.0.22",
"@graphql-tools/schema": "^10.0.21",
"@graphql-tools/utils": "^10.8.4",
"@haensl/subset-sum": "^3.0.5", "@haensl/subset-sum": "^3.0.5",
"@lamassu/coins": "v1.6.1", "@lamassu/coins": "v1.6.1",
"@simplewebauthn/server": "^3.0.0", "@simplewebauthn/server": "^3.0.0",
@ -22,7 +25,6 @@
"@vonage/server-client": "1.7.0", "@vonage/server-client": "1.7.0",
"@vonage/sms": "1.7.0", "@vonage/sms": "1.7.0",
"@vonage/vetch": "1.5.0", "@vonage/vetch": "1.5.0",
"apollo-server-express": "2.25.1",
"argon2": "0.28.2", "argon2": "0.28.2",
"axios": "0.21.1", "axios": "0.21.1",
"base-x": "3.0.9", "base-x": "3.0.9",
@ -50,10 +52,10 @@
"form-data": "^4.0.0", "form-data": "^4.0.0",
"futoin-hkdf": "^1.0.2", "futoin-hkdf": "^1.0.2",
"got": "^7.1.0", "got": "^7.1.0",
"graphql": "^15.5.0", "graphql": "^16.10.0",
"graphql-iso-date": "^3.6.1", "graphql-scalars": "^1.24.1",
"graphql-type-json": "^0.3.1", "graphql-tag": "^2.12.6",
"graphql-upload": "12.0.0", "graphql-upload": "^17.0.0",
"helmet": "^3.8.1", "helmet": "^3.8.1",
"inquirer": "^5.2.0", "inquirer": "^5.2.0",
"json2csv": "^5.0.3", "json2csv": "^5.0.3",
@ -130,8 +132,7 @@
"build-admin": "npm run build-admin:css && npm run build-admin:main && npm run build-admin:lamassu", "build-admin": "npm run build-admin:css && npm run build-admin:main && npm run build-admin:lamassu",
"server": "nodemon bin/lamassu-server --mockScoring --logLevel silly", "server": "nodemon bin/lamassu-server --mockScoring --logLevel silly",
"admin-server": "nodemon bin/lamassu-admin-server --dev --logLevel silly", "admin-server": "nodemon bin/lamassu-admin-server --dev --logLevel silly",
"graphql-server": "nodemon bin/new-graphql-dev-insecure", "watch": "concurrently \"npm:server\" \"npm:admin-server\"",
"watch": "concurrently \"npm:server\" \"npm:admin-server\" \"npm:graphql-server\"",
"stress-test": "cd tests/stress/ && node index.js 50 -v" "stress-test": "cd tests/stress/ && node index.js 50 -v"
}, },
"nodemonConfig": { "nodemonConfig": {

View file

@ -1,13 +1,13 @@
with import (fetchTarball { with import (fetchTarball {
name = "8ad5e8"; name = "nixpkgs-194846768975b7ad2c4988bdb82572c00222c0d7";
url = https://github.com/NixOS/nixpkgs/archive/8ad5e8132c5dcf977e308e7bf5517cc6cc0bf7d8.tar.gz; url = https://github.com/NixOS/nixpkgs/archive/194846768975b7ad2c4988bdb82572c00222c0d7.tar.gz;
sha256 = "17v6wigks04x1d63a2wcd7cc4z9ca6qr0f4xvw1pdw83f8a3c0nj"; sha256 = "0snj72i9dm99jlnnmk8id8ffjnfg1k81lr7aw8d01kz3hdiraqil";
}) {}; }) {};
stdenv.mkDerivation { stdenv.mkDerivation {
name = "node"; name = "node";
buildInputs = [ buildInputs = [
nodejs-14_x nodejs_22
python3 python3
openssl openssl
postgresql postgresql