@@ -8,10 +8,15 @@ var tracker = {},
88 stats = require ( '../data/stats.js' ) ,
99 common = require ( '../../utils/common.js' ) ,
1010 logger = require ( '../../utils/log.js' ) ,
11+ countlyFs = require ( '../../utils/countlyFs.js' ) ,
1112 //log = logger("tracker:server"),
1213 Countly = require ( 'countly-sdk-nodejs' ) ,
1314 fs = require ( 'fs' ) ,
1415 path = require ( 'path' ) ,
16+ https = require ( 'https' ) ,
17+ http = require ( 'http' ) ,
18+ FormData = require ( 'form-data' ) ,
19+ { Readable } = require ( 'node:stream' ) ,
1520 versionInfo = require ( '../../../frontend/express/version.info' ) ,
1621 server = "9c28c347849f2c03caf1b091ec7be8def435e85e" ,
1722 user = "fa6e9ae7b410cb6d756e8088c5f3936bf1fab5f3" ,
@@ -72,6 +77,17 @@ tracker.enable = function() {
7277
7378 isEnabled = true ;
7479 Countly . user_details ( { "name" : config . device_id } ) ;
80+ if ( plugins . getConfig ( "white-labeling" ) && ( plugins . getConfig ( "white-labeling" ) . favicon || plugins . getConfig ( "white-labeling" ) . stopleftlogo || plugins . getConfig ( "white-labeling" ) . prelogo ) ) {
81+ var id = plugins . getConfig ( "white-labeling" ) . favicon || plugins . getConfig ( "white-labeling" ) . stopleftlogo || plugins . getConfig ( "white-labeling" ) . prelogo ;
82+ countlyFs . gridfs . getDataById ( "white-labeling" , id , function ( errWhitelabel , data ) {
83+ if ( ! errWhitelabel && data ) {
84+ tracker . uploadBase64FileFromGridFS ( data ) . catch ( ( ) => { } ) ;
85+ }
86+ } ) ;
87+ }
88+ else {
89+ Countly . user_details ( { "picture" : "./images/favicon.png" } ) ;
90+ }
7591 if ( plugins . getConfig ( "tracking" ) . server_sessions ) {
7692 Countly . begin_session ( true ) ;
7793 }
@@ -420,6 +436,114 @@ tracker.getSDKData = async function() {
420436 }
421437} ;
422438
439+ /**
440+ * Upload a base64-encoded file from GridFS to the stats server
441+ * This function handles files stored in GridFS as base64 strings (e.g., data URIs)
442+ * and decodes them before uploading
443+ *
444+ * @param {Object } base64String - Picture data
445+ * @returns {Promise<Object> } Upload result
446+ */
447+ tracker . uploadBase64FileFromGridFS = function ( base64String ) {
448+ return new Promise ( ( resolve , reject ) => {
449+ var domain = stripTrailingSlash ( ( plugins . getConfig ( "api" ) . domain + "" ) . split ( "://" ) . pop ( ) ) ;
450+ if ( domain && domain !== "localhost" ) {
451+ try {
452+ let mimeType = "image/png" ;
453+ // Strip data URI prefix if present and stripDataURI is true
454+ if ( base64String . includes ( 'base64,' ) ) {
455+ // Extract MIME type from data URI if not provided
456+ const dataURIMatch = base64String . match ( / ^ d a t a : ( [ ^ ; ] + ) ; b a s e 6 4 , / ) ;
457+ if ( dataURIMatch ) {
458+ mimeType = dataURIMatch [ 1 ] ;
459+ }
460+ // Remove data URI prefix
461+ base64String = base64String . split ( 'base64,' ) [ 1 ] ;
462+ }
463+
464+ // Decode base64 to binary buffer
465+ const binaryBuffer = Buffer . from ( base64String , 'base64' ) ;
466+
467+ // Create a readable stream from the decoded buffer
468+ const decodedStream = Readable . from ( binaryBuffer ) ;
469+
470+ // Parse the URL
471+ const statsUrl = new URL ( url ) ;
472+ const protocol = statsUrl . protocol === 'https:' ? https : http ;
473+
474+ // Build query parameters
475+ const queryParams = new URLSearchParams ( {
476+ device_id : domain ,
477+ app_key : server ,
478+ user_details : ""
479+ } ) ;
480+
481+ // Create form data
482+ const form = new FormData ( ) ;
483+
484+ // Prepare form options with MIME type if available
485+ const formOptions = { filename : "profile" } ;
486+ if ( mimeType ) {
487+ formOptions . contentType = mimeType ;
488+ }
489+
490+ form . append ( 'file' , decodedStream , formOptions ) ;
491+
492+ // Prepare request options
493+ const requestOptions = {
494+ hostname : statsUrl . hostname ,
495+ port : statsUrl . port || ( statsUrl . protocol === 'https:' ? 443 : 80 ) ,
496+ path : `/i?${ queryParams . toString ( ) } ` ,
497+ method : 'POST' ,
498+ headers : form . getHeaders ( )
499+ } ;
500+
501+ // Make the request
502+ const req = protocol . request ( requestOptions , ( res ) => {
503+ let data = '' ;
504+
505+ res . on ( 'data' , ( chunk ) => {
506+ data += chunk ;
507+ } ) ;
508+
509+ res . on ( 'end' , ( ) => {
510+ if ( res . statusCode >= 200 && res . statusCode < 300 ) {
511+ try {
512+ const result = JSON . parse ( data ) ;
513+ resolve ( {
514+ success : true ,
515+ statusCode : res . statusCode ,
516+ data : result
517+ } ) ;
518+ }
519+ catch ( e ) {
520+ resolve ( {
521+ success : true ,
522+ statusCode : res . statusCode ,
523+ data : data
524+ } ) ;
525+ }
526+ }
527+ else {
528+ reject ( new Error ( `Upload failed with status ${ res . statusCode } : ${ data } ` ) ) ;
529+ }
530+ } ) ;
531+ } ) ;
532+
533+ req . on ( 'error' , ( error ) => {
534+ reject ( error ) ;
535+ } ) ;
536+
537+ // Pipe the form data to the request
538+ form . pipe ( req ) ;
539+ }
540+ catch ( error ) {
541+ reject ( error ) ;
542+ }
543+ }
544+ } ) ;
545+ } ;
546+
423547/**
424548 * Check if running in Docker environment
425549 * @returns {boolean } if running in docker
0 commit comments