Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collector to send to mongodb for dev environments only #1078

Merged
merged 12 commits into from
Mar 13, 2024
2 changes: 2 additions & 0 deletions demos/client-example-server/env.development
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ PROSOPO_SITE_PRIVATE_KEY=//Bob
PROSOPO_SERVER_PORT=9228
PROSOPO_DEFAULT_ENVIRONMENT=development
PROSOPO_DEFAULT_NETWORK=development
PROSOPO_MONGO_EVENTS_URI=mongodb+srv://<MONGO_URI_HERE>/frictionless_events
_DEV_ONLY_WATCH_EVENTS=false
2 changes: 2 additions & 0 deletions demos/client-example/env.development
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ PROSOPO_SERVER_URL=http://localhost:9228
PROSOPO_PORT=9230
PROSOPO_DEFAULT_NETWORK=development
PROSOPO_DEFAULT_ENVIRONMENT=development
PROSOPO_MONGO_EVENTS_URI=mongodb+srv://<MONGO_URI_HERE>/frictionless_events
_DEV_ONLY_WATCH_EVENTS=false
7 changes: 4 additions & 3 deletions demos/client-example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
ApiParams,
EnvironmentTypes,
EnvironmentTypesSchema,
ProcaptchaConfigSchema,
ProcaptchaOutput,
ProsopoClientConfigSchema,
} from '@prosopo/types'
import { ExtensionAccountSelect, Procaptcha } from '@prosopo/procaptcha-react'
import { useState } from 'react'
Expand All @@ -40,7 +40,7 @@ function App() {
// the result of the captcha process. Submit this to your backend server to verify the user is human on the backend
const [procaptchaOutput, setProcaptchaOutput] = useState<ProcaptchaOutput | undefined>(undefined)

const config = ProsopoClientConfigSchema.parse({
const config = ProcaptchaConfigSchema.parse({
userAccountAddress: account,
account: {
address: process.env.PROSOPO_SITE_KEY || '',
Expand All @@ -50,7 +50,8 @@ function App() {
defaultEnvironment:
(process.env.PROSOPO_DEFAULT_ENVIRONMENT as EnvironmentTypes) || EnvironmentTypesSchema.enum.development,
serverUrl: process.env.PROSOPO_SERVER_URL || '',
atlasUri: process.env._DEV_ONLY_WATCH_EVENTS === 'true' || false,
mongoAtlasUri: process.env.PROSOPO_MONGO_EVENTS_URI || '',
devOnlyWatchEvents: process.env._DEV_ONLY_WATCH_EVENTS === 'true' || false,
})

const label = isLogin ? 'Login' : 'Sign up'
Expand Down
1 change: 1 addition & 0 deletions dev/config/src/vite/vite.frontend.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default async function (
'process.env.PROSOPO_DEFAULT_NETWORK': JSON.stringify(process.env.PROSOPO_DEFAULT_NETWORK),
'process.env.PROSOPO_SERVER_URL': JSON.stringify(process.env.PROSOPO_SERVER_URL),
'process.env._DEV_ONLY_WATCH_EVENTS': JSON.stringify(process.env._DEV_ONLY_WATCH_EVENTS),
'process.env.PROSOPO_MONGO_EVENTS_URI': JSON.stringify(process.env.PROSOPO_MONGO_EVENTS_URI),
'process.env.PROSOPO_CONTRACT_ADDRESS': JSON.stringify(process.env.PROSOPO_CONTRACT_ADDRESS),
// only needed if bundling with a site key
'process.env.PROSOPO_SITE_KEY': JSON.stringify(process.env.PROSOPO_SITE_KEY),
Expand Down
2 changes: 2 additions & 0 deletions dev/scripts/env.development
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ NODE_ENV=development
PROSOPO_LOG_LEVEL=debug
PROSOPO_DEFAULT_ENVIRONMENT=development
PROSOPO_DEFAULT_NETWORK=development
PROSOPO_MONGO_EVENTS_URI=mongodb+srv://<MONGO_URI_HERE>/frictionless_events
_DEV_ONLY_WATCH_EVENTS=false
2 changes: 2 additions & 0 deletions packages/cli/src/prosopo.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,7 @@ export default function getConfig(
captchaSolutions: captchaSolutionsConfig,
batchCommit: batchCommitConfig,
captchas: captchaServeConfig,
devOnlyWatchEvents: process.env._DEV_ONLY_WATCH_EVENTS === 'true',
mongoEventsUri: process.env.PROSOPO_MONGO_EVENTS_URI || '',
} as ProsopoConfigInput)
}
49 changes: 8 additions & 41 deletions packages/database/src/eventsDatabase/eventsDatabase.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ProsopoDBError, getLoggerDefault } from '@prosopo/common'
import { StoredEventRecord, StoredEvents } from '@prosopo/types'
import { getLoggerDefault } from '@prosopo/common'
import mongoose, { Model } from 'mongoose'
const logger = getLoggerDefault()
const MAX_RETRIES = 3
Expand Down Expand Up @@ -36,47 +36,14 @@ try {
CaptchaEvent = mongoose.model('CaptchaEvent', captchaEventSchema)
}

const addCaptchaEventRecord = async (record: StoredEventRecord): Promise<void> => {
try {
const newRecord = new CaptchaEvent(record)
await newRecord.save()
logger.info('Record added successfully')
} catch (error) {
logger.error('Error adding record to the database:', error)
}
}

export const saveCaptchaEvent = async (events: StoredEvents, accountId: string, atlasUri: string) => {
return new Promise((resolve, reject) => {
const connection = mongoose.createConnection(atlasUri)
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
connection.once('open', resolve).on('error', (e) => {
logger.warn(`Mongoose connection error`)
logger.error(e)
await mongoose.connect(atlasUri).then(() => console.log('Connected to MongoDB Atlas'))

// Only reject on the last attempt, otherwise handle the retry logic
if (attempt === MAX_RETRIES) {
reject(
new ProsopoDBError('DATABASE.CONNECT_ERROR', {
context: { failedFuncName: saveCaptchaEvent.name, db: 'events database' },
logger: logger,
})
)
} else {
// Remove the error listener to avoid accumulated listeners on retries
connection?.removeAllListeners('error')
}
})
}

const captchaEventData = {
...events,
accountId,
}
const captchaEventData = {
...events,
accountId,
}

addCaptchaEventRecord(captchaEventData)
.then(() => logger.info('Captcha event data saved'))
.catch((error) => logger.error('Error saving captcha event data:', error))
.finally(() => connection.close())
})
const saved = await CaptchaEvent.create(captchaEventData)
console.log('Mongo Saved Event', saved)
}
3 changes: 2 additions & 1 deletion packages/procaptcha-bundle/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ const getConfig = (siteKey?: string) => {
address: siteKey,
},
serverUrl: process.env.PROSOPO_SERVER_URL || '',
mongoAtlasUri: process.env._DEV_ONLY_WATCH_EVENTS === 'true' || false,
mongoAtlasUri: process.env.PROSOPO_MONGO_EVENTS_URI || '',
devOnlyWatchEvents: process.env._DEV_ONLY_WATCH_EVENTS === 'true' || false,
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ const ProcaptchaWidget = (props: ProcaptchaProps) => {
</div>
</div>
{config.devOnlyWatchEvents && (
<Collector onProcessData={manager.exportData} sendData={state.showModal}></Collector>
<Collector onProcessData={manager.exportData} sendData={state.sendData}></Collector>
)}
</div>
)
Expand Down
27 changes: 24 additions & 3 deletions packages/procaptcha/src/modules/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ export function Manager(
},
onOpen: () => {
console.log('onOpen event triggered')
updateState({ sendData: !state.sendData })
},
onClose: () => {
console.log('onClose event triggered')
Expand Down Expand Up @@ -597,12 +596,34 @@ export function Manager(
}

const exportData = async (events: StoredEvents) => {
const providerUrlFromStorage = storage.getProviderUrl()
let providerApi: ProviderApi

if (providerUrlFromStorage) {
providerApi = await loadProviderApi(providerUrlFromStorage)
} else {
const contract = await loadContract()
const getRandomProviderResponse: RandomProvider = await wrapQuery(
contract.query.getRandomActiveProvider,
contract.query
)(getAccount().account.address, getDappAccount())
const providerUrl = trimProviderUrl(getRandomProviderResponse.provider.url.toString())
providerApi = await loadProviderApi(providerUrl)
}

const providerUrl = storage.getProviderUrl() || state.captchaApi?.provider.provider.url.toString()
if (!providerUrl) {
return
}
const providerApi = await loadProviderApi(providerUrl)
await providerApi.submitUserEvents(events, getAccount().account.address)
console.log('Submitting events to provider', events)

let account = ''
try {
account = getAccount().account.address
} catch (e) {
console.error(e)
goastler marked this conversation as resolved.
Show resolved Hide resolved
}
await providerApi.submitUserEvents(events, account)
}

return {
Expand Down
9 changes: 2 additions & 7 deletions packages/procaptcha/src/modules/collector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ProsopoKeyboardEvent, ProsopoMouseEvent, ProsopoTouchEvent } from '@prosopo/types'

const COLLECTOR_LIMIT = 1000
const COLLECTOR_LIMIT = 10000
HughParry marked this conversation as resolved.
Show resolved Hide resolved

type SetStateAction<T> = T | ((prevState: T) => T)
type SetStateEvent<T> = (setValueFunc: SetStateAction<T[]>) => void
Expand Down Expand Up @@ -38,12 +38,7 @@ const logKeyboardEvent = (event: globalThis.KeyboardEvent, setKeyboardEvent: Set
}

const logTouchEvent = (event: globalThis.TouchEvent, setTouchEvent: SetTouchEvent) => {
// Iterate over the TouchList (map doesn't work on TouchList)
for (let i = 0; i < event.touches.length; i++) {
const touch = event.touches[i]
if (!touch) {
continue
}
for (const touch of Array.from(event.touches)) {
storeLog({ x: touch.clientX, y: touch.clientY, timestamp: event.timeStamp }, setTouchEvent)
}
}
Expand Down
5 changes: 3 additions & 2 deletions packages/provider/src/tasks/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -711,9 +711,10 @@ export class Tasks {
}

async saveCaptchaEvent(events: StoredEvents, accountId: string) {
if (!this.config.mongoAtlasUri) {
if (!this.config.devOnlyWatchEvents || !this.config.mongoEventsUri) {
console.log('Dev watch events not set to true, not saving events')
return
}
await saveCaptchaEvent(events, accountId, this.config.mongoAtlasUri)
await saveCaptchaEvent(events, accountId, this.config.mongoEventsUri)
goastler marked this conversation as resolved.
Show resolved Hide resolved
}
}
4 changes: 2 additions & 2 deletions packages/types/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export const ProsopoBasicConfigSchema = ProsopoBaseConfigSchema.merge(
object({
networks: ProsopoNetworksSchema.default(networks),
database: DatabaseConfigSchema.optional(),
devOnlyWatchEvents: boolean().optional(),
})
)
export type ProsopoNetworksSchemaInput = input<typeof ProsopoNetworksSchema>
Expand Down Expand Up @@ -145,7 +146,6 @@ export const ProcaptchaConfigSchema = ProsopoClientConfigSchema.and(
accountCreator: AccountCreatorConfigSchema.optional(),
theme: ThemeType.optional(),
challengeValidLength: number().positive().optional(),
devOnlyWatchEvents: boolean().optional(),
})
)

Expand All @@ -168,7 +168,7 @@ export const ProsopoConfigSchema = ProsopoBasicConfigSchema.merge(
maxBatchExtrinsicPercentage: 59,
}),
server: ProsopoImageServerConfigSchema,
mongoAtlasUri: string().optional(),
mongoEventsUri: string().optional(),
})
)

Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/procaptcha/collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ export type StoredEvents = {
}

export interface StoredEventRecord extends StoredEvents {
accountId: string
accountId?: string
}
Loading