diff --git a/packages/admin-ui/src/Main.jsx b/packages/admin-ui/src/Main.jsx index 4600cee..e162b96 100644 --- a/packages/admin-ui/src/Main.jsx +++ b/packages/admin-ui/src/Main.jsx @@ -29,22 +29,11 @@ const GET_USER_DATA = gql` const Main = () => { const [location, navigate] = useLocation() - const { wizardTested, userData, setUserData} = useContext(AppContext) + const { wizardTested, userData, setUserData } = useContext(AppContext) const [loading, setLoading] = useState(true) const [restrictionLevel, setRestrictionLevel] = useState(null) - // Skip auth queries on unauthenticated pages (like /register and /login) - const isPublicPage = location.startsWith('/register') || location.startsWith('/login') - - // Set loading to false immediately for public pages - React.useEffect(() => { - if (isPublicPage) { - setLoading(false) - } - }, [isPublicPage]) - useQuery(GET_USER_DATA, { - skip: isPublicPage, onCompleted: userResponse => { if (!userData && userResponse?.userData) { setUserData(userResponse.userData) @@ -54,10 +43,6 @@ const Main = () => { } setLoading(false) }, - onError: () => { - // If query fails, just mark as not loading - setLoading(false) - }, }) const sidebar = hasSidebar(location) diff --git a/packages/admin-ui/src/pages/Authentication/LoginState.jsx b/packages/admin-ui/src/pages/Authentication/LoginState.jsx index 1ba1420..ef7541c 100644 --- a/packages/admin-ui/src/pages/Authentication/LoginState.jsx +++ b/packages/admin-ui/src/pages/Authentication/LoginState.jsx @@ -83,11 +83,6 @@ const LoginState = ({ dispatch, strategy }) => { if (!loginResponse.login) return - // Handle SKIP2FA case - directly get user data and navigate - if (loginResponse.login === 'SKIP2FA') { - return getUserData() - } - return dispatch({ type: loginResponse.login, payload: { diff --git a/packages/admin-ui/src/pages/Services/schemas/index.js b/packages/admin-ui/src/pages/Services/schemas/index.js index f6519a9..695aa59 100644 --- a/packages/admin-ui/src/pages/Services/schemas/index.js +++ b/packages/admin-ui/src/pages/Services/schemas/index.js @@ -11,7 +11,6 @@ import inforu from './inforu' import infura from './infura' import _itbit from './itbit' import _kraken from './kraken' -import lnbits from './lnbits' import mailgun from './mailgun' import scorechain from './scorechain' import sumsub from './sumsub' @@ -32,7 +31,6 @@ const schemas = (markets = {}) => { return { [bitgo.code]: bitgo, [galoy.code]: galoy, - [lnbits.code]: lnbits, [bitstamp.code]: bitstamp, [blockcypher.code]: blockcypher, [elliptic.code]: elliptic, diff --git a/packages/admin-ui/src/pages/Services/schemas/lnbits.js b/packages/admin-ui/src/pages/Services/schemas/lnbits.js deleted file mode 100644 index 00d03e5..0000000 --- a/packages/admin-ui/src/pages/Services/schemas/lnbits.js +++ /dev/null @@ -1,36 +0,0 @@ -import * as Yup from 'yup' - -import { - SecretInput, - TextInput, -} from '../../../components/inputs/formik' - -import { secretTest } from './helper' - -export default { - code: 'lnbits', - name: 'LNBits', - title: 'LNBits (Wallet)', - elements: [ - { - code: 'endpoint', - display: 'LNBits Server URL', - component: TextInput, - }, - { - code: 'adminKey', - display: 'Admin Key', - component: SecretInput, - }, - ], - getValidationSchema: account => { - return Yup.object().shape({ - endpoint: Yup.string('The endpoint must be a string') - .max(200, 'The endpoint is too long') - .required('The endpoint is required'), - adminKey: Yup.string('The Admin Key must be a string') - .max(200, 'The Admin Key is too long') - .test(secretTest(account?.adminKey)), - }) - }, -} \ No newline at end of file diff --git a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx index f46bb5f..c99295b 100644 --- a/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx +++ b/packages/admin-ui/src/pages/Wizard/components/Wallet/ChooseWallet.jsx @@ -36,7 +36,7 @@ const SAVE_ACCOUNTS = gql` ` const isConfigurable = it => - R.includes(it)(['infura', 'bitgo', 'trongrid', 'galoy', 'lnbits']) + R.includes(it)(['infura', 'bitgo', 'trongrid', 'galoy']) const isLocalHosted = it => R.includes(it)([ @@ -178,19 +178,6 @@ const ChooseWallet = ({ data: currentData, addData }) => { /> )} - {selected === 'lnbits' && ( - <> -

Enter wallet information

- - - )} ) } diff --git a/packages/server/lib/new-admin/config/accounts.js b/packages/server/lib/new-admin/config/accounts.js index e955082..87a3e35 100644 --- a/packages/server/lib/new-admin/config/accounts.js +++ b/packages/server/lib/new-admin/config/accounts.js @@ -103,7 +103,6 @@ const ALL_ACCOUNTS = [ cryptos: [BTC, ZEC, LTC, BCH, DASH], }, { code: 'galoy', display: 'Galoy', class: WALLET, cryptos: [LN] }, - { code: 'lnbits', display: 'LNBits', class: WALLET, cryptos: [LN] }, { code: 'bitstamp', display: 'Bitstamp', diff --git a/packages/server/lib/new-admin/graphql/modules/userManagement.js b/packages/server/lib/new-admin/graphql/modules/userManagement.js index 4591ed2..492afa9 100644 --- a/packages/server/lib/new-admin/graphql/modules/userManagement.js +++ b/packages/server/lib/new-admin/graphql/modules/userManagement.js @@ -10,7 +10,6 @@ const users = require('../../../users') const sessionManager = require('../../../session-manager') const authErrors = require('../errors') const credentials = require('../../../hardware-credentials') -const { skip2fa } = require('../../../environment-helper') const REMEMBER_ME_AGE = 90 * T.day @@ -163,25 +162,15 @@ const deleteSession = (sessionID, context) => { return sessionManager.deleteSessionById(sessionID) } -const login = (username, password, context) => { +const login = (username, password) => { return authenticateUser(username, password) .then(user => { - // Skip 2FA if environment variable is set - if (skip2fa) { - initializeSession(context, user, false) - return 'SKIP2FA' - } - return Promise.all([ credentials.getHardwareCredentialsByUserId(user.id), user.twofa_code, ]) }) - .then(result => { - // If we already handled skip2fa, return the result - if (result === 'SKIP2FA') return result - - const [devices, twoFASecret] = result + .then(([devices, twoFASecret]) => { if (!_.isEmpty(devices)) return 'FIDO' return twoFASecret ? 'INPUT2FA' : 'SETUP2FA' }) diff --git a/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js b/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js index 8e2f941..513a341 100644 --- a/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js +++ b/packages/server/lib/new-admin/graphql/resolvers/users.resolver.js @@ -124,8 +124,8 @@ const resolver = { sessionManager.deleteSessionsByUsername(username), changeUserRole: (...[, { confirmationCode, id, newRole }, context]) => userManagement.changeUserRole(confirmationCode, id, newRole, context), - login: (...[, { username, password }, context]) => - userManagement.login(username, password, context), + login: (...[, { username, password }]) => + userManagement.login(username, password), input2FA: (...[, { username, password, rememberMe, code }, context]) => userManagement.input2FA(username, password, rememberMe, code, context), setup2FA: ( diff --git a/packages/server/lib/new-admin/services/restriction-level.js b/packages/server/lib/new-admin/services/restriction-level.js index 7c96af6..63196f0 100644 --- a/packages/server/lib/new-admin/services/restriction-level.js +++ b/packages/server/lib/new-admin/services/restriction-level.js @@ -5,15 +5,7 @@ const { machines } = require('typesafe-db') const CACHE_DURATION = 30 * 60 * 1000 const _getHighestRestrictionLevel = async () => { - try { - const level = await machines.getHighestRestrictionLevel() - // Return 0 if null/undefined (no machines in database) - return level ?? 0 - } catch (err) { - // Log error and return 0 for empty database or other errors - console.error('Error fetching restriction level:', err.message) - return 0 - } + return machines.getHighestRestrictionLevel() } const getCachedRestrictionLevel = mem(_getHighestRestrictionLevel, { diff --git a/packages/server/lib/plugins/wallet/lnbits/lnbits.js b/packages/server/lib/plugins/wallet/lnbits/lnbits.js index e0fa1b1..e48ced7 100644 --- a/packages/server/lib/plugins/wallet/lnbits/lnbits.js +++ b/packages/server/lib/plugins/wallet/lnbits/lnbits.js @@ -93,11 +93,11 @@ async function newAddress(account, info, tx) { const endpoint = `${account.endpoint}/api/v1/payments` const result = await request(endpoint, 'POST', invoiceData, account.adminKey) - if (!result.bolt11) { - throw new Error('LNBits did not return a bolt11 invoice') + if (!result.payment_request) { + throw new Error('LNBits did not return a payment request') } - return result.bolt11 + return result.payment_request } async function getStatus(account, tx) { @@ -131,44 +131,12 @@ async function getStatus(account, tx) { } } -async function sendLNURL(account, lnurl, cryptoAtoms) { - validateConfig(account) - - const paymentData = { - lnurl: lnurl, - amount: parseInt(cryptoAtoms.toString()) * 1000, // Convert satoshis to millisatoshis - comment: `Lamassu ATM - ${new Date().toISOString()}` - } - - const endpoint = `${account.endpoint}/api/v1/payments/lnurl` - const result = await request(endpoint, 'POST', paymentData, account.adminKey) - - if (!result.payment_hash) { - throw new Error('LNBits LNURL payment failed: No payment hash returned') - } - - return { - txid: result.payment_hash, - fee: result.fee_msat ? Math.ceil(result.fee_msat / 1000) : 0 - } -} - async function sendCoins(account, tx) { validateConfig(account) const { toAddress, cryptoAtoms, cryptoCode } = tx await checkCryptoCode(cryptoCode) - // Handle LNURL addresses - if (isLnurl(toAddress)) { - return sendLNURL(account, toAddress, cryptoAtoms) - } - - // Handle bolt11 invoices - if (!isLnInvoice(toAddress)) { - throw new Error('Invalid Lightning address: must be bolt11 invoice or LNURL') - } - const paymentData = { out: true, bolt11: toAddress @@ -221,9 +189,10 @@ async function newFunding(account, cryptoCode) { const [walletBalance, fundingAddress] = await Promise.all(promises) return { - fundingPendingBalance: new BN(0), - fundingConfirmedBalance: walletBalance, - fundingAddress + fundingAddress, + fundingAddressQr: fundingAddress, + confirmed: walletBalance.gte(0), + confirmedBalance: walletBalance.toString() } } diff --git a/packages/server/migrations/1750000000000-add-lnbits-config.js b/packages/server/migrations/1750000000000-add-lnbits-config.js index 351f0b1..6e9c34a 100644 --- a/packages/server/migrations/1750000000000-add-lnbits-config.js +++ b/packages/server/migrations/1750000000000-add-lnbits-config.js @@ -1,17 +1,36 @@ -const { saveConfig } = require('../lib/new-settings-loader') +const db = require('./db') exports.up = function (next) { - const config = { - 'lnbits_endpoint': '', - 'lnbits_adminKey': '', - 'LN_wallet': 'lnbits' - } + const sql = ` + INSERT INTO user_config (name, display_name, type, data_type, config_type, enabled, secret, options) + VALUES + ('lnbitsEndpoint', 'LNBits Server URL', 'text', 'string', 'wallets', false, false, null), + ('lnbitsAdminKey', 'LNBits Admin Key', 'text', 'string', 'wallets', false, true, null) + ON CONFLICT (name) DO NOTHING; + + -- Add LNBits as a valid wallet option for Lightning Network + INSERT INTO user_config (name, display_name, type, data_type, config_type, enabled, secret, options) + VALUES + ('LN_wallet', 'Lightning Network Wallet', 'text', 'string', 'wallets', true, false, + '[{"code": "lnbits", "display": "LNBits"}, {"code": "galoy", "display": "Galoy (Blink)"}, {"code": "bitcoind", "display": "Bitcoin Core"}]') + ON CONFLICT (name) + DO UPDATE SET options = EXCLUDED.options + WHERE user_config.options NOT LIKE '%lnbits%'; + ` - saveConfig(config).then(next).catch(next) + db.multi(sql, next) } exports.down = function (next) { - // No-op - removing config entries is not typically done in down migrations - // as it could break existing configurations - next() + const sql = ` + DELETE FROM user_config + WHERE name IN ('lnbitsEndpoint', 'lnbitsAdminKey'); + + -- Remove LNBits from wallet options + UPDATE user_config + SET options = REPLACE(options, ', {"code": "lnbits", "display": "LNBits"}', '') + WHERE name = 'LN_wallet'; + ` + + db.multi(sql, next) } \ No newline at end of file diff --git a/packages/server/package.json b/packages/server/package.json index d72f949..696271d 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -31,7 +31,6 @@ "bchaddrjs": "^0.3.0", "bignumber.js": "9.0.1", "bip39": "^2.3.1", - "bolt11": "^1.4.1", "ccxt": "2.9.16", "compression": "^1.7.4", "connect-pg-simple": "^6.2.1",