import { v4 } from 'uuid' ; import { ASSET_TYPE , SEARCH_KEY_TRUNCATE_LENGTH , SKIPPED_LIST_ITEM_TYPES , USER_ROLE , } from 'common/enums' ; import logger from 'common/logger' ; import { BaseService } from 'connectors' ; import { GQLFeatureFlag , GQLFeatureName , ItemData , SkippedListItemType , Viewer , } from 'definitions' ; export class SystemService extends BaseService { featureFlagTable : string ; constructor ( ) { super ( 'noop' ) ; this . featureFlagTable = 'feature_flag' ; } /*********************************
     *                               *
     *           Search              *
     *                               *
     *********************************/ frequentSearch = async ( { key = '' , first = 5 , } ) => { const query = this . knex ( 'search_history' ) . select ( 'search_key' ) . count ( 'id' ) . whereNot ( { searchKey : '' } ) . groupBy ( 'search_key' ) . orderBy ( 'count' , 'desc' ) . limit ( first ) ; if ( key ) { query . where ( 'search_key' , 'like' , `%${ key }%` ) ; } else { query . where ( 'created_at' , '>=' , this . knex . raw ( `now() -  interval '1 days'` ) ) ; } const result = await query ; return result . map ( ( { searchKey } ) => ( searchKey as string ) . slice ( 0 , SEARCH_KEY_TRUNCATE_LENGTH ) ) ; } ; /*********************************
     *                               *
     *            Features           *
     *                               *
     *********************************/ getFeatureFlags = ( ) => this . knex ( this . featureFlagTable ) . select ( '*' ) . limit ( 50 ) ; getFeatureFlag = async ( name ) => { const [ featureFlag ] = await this . knex ( this . featureFlagTable ) . where ( { name } ) ; return featureFlag ; } ; setFeatureFlag = async ( { name , flag , } ) => { const [ featureFlag ] = await this . knex . where ( { name } ) . update ( { name , flag , updatedAt : this . knex . fn . now ( ) , } ) . into ( this . featureFlagTable ) . returning ( '*' ) ; return featureFlag ; } ; isFeatureEnabled = async ( flag , viewer ) => { switch ( flag ) { case GQLFeatureFlag . on : { return true ; } case GQLFeatureFlag . admin : { return viewer . role === USER_ROLE . admin ; } case GQLFeatureFlag . seeding : { if ( ! ( 'id' in viewer ) ) { return false ; } if ( viewer . role === USER_ROLE . admin ) { return true ; } const seedingUser = await this . baseCount ( { userId : viewer . id , } , 'seeding_user' ) ; return seedingUser > 0 ; } } return false ; } ; /*********************************
     *                               *
     *              Asset            *
     *                               *
     *********************************/ /**
     * Create asset and asset_map
     */ createAssetAndAssetMap = async ( asset , entityTypeId , entityId ) => this . knex . transaction ( async ( trx ) => { const [ newAsset ] = await trx . insert ( asset ) . into ( 'asset' ) . returning ( '*' ) ; await trx . insert ( { assetId : newAsset . id , entityTypeId , entityId , } ) . into ( 'asset_map' ) ; return newAsset ; } ) ; /**
     * Find asset by a given uuid
     */ findAssetByUUID = async ( uuid ) => this . baseFindByUUID ( uuid , 'asset' ) ; findAssetOrCreateByPath = async ( data , entityTypeId , entityId ) => this . knex . transaction ( async ( trx ) => { const { path , type , authorId , uuid } = data ; let asset = await trx ( 'asset' ) . select ( ) . where ( { path , type , authorId } ) . first ( ) ; if ( ! asset ) { ; [ asset ] = await trx . insert ( { path , type , authorId , uuid : uuid || v4 ( ) , } ) . into ( 'asset' ) . returning ( '*' ) ; } const assetMData = { assetId : asset . id , entityTypeId , entityId , } ; const assetMResultId = await trx ( 'asset_map' ) . select ( 'id' ) . where ( assetMData ) . first ( ) ; if ( ! assetMResultId ) { await trx . insert ( assetMData ) . into ( 'asset_map' ) ; } return asset ; } ) ; /**
     * Find assets by given uuids
     */ findAssetByUUIDs = async ( uuids ) => this . baseFindByUUIDs ( uuids , 'asset' ) ; /**
     * Find the url of an asset by a given id.
     */ findAssetUrl = async ( id ) : Promise < string | null > => { const result = await this . baseFindById ( id , 'asset' ) ; return result && result . path ? `${ this . aws . s3Endpoint }/${ result . path }` : null ; } ; /**
     * Find asset and asset map by given entity type and id
     */ findAssetAndAssetMap = async ( { entityTypeId , entityId , assetType , } ) => { const query = this . knex ( 'asset_map' ) . select ( 'asset_map.*' , 'uuid' , 'path' , 'type' , 'created_at' ) . rightJoin ( 'asset' , 'asset_map.asset_id' , 'asset.id' ) . where ( { entityTypeId , entityId } ) ; if ( assetType ) { query . andWhere ( { type : assetType } ) ; } return query ; } ; /**
     * Swap entity of asset map by given ids
     */ swapAssetMapEntity = async ( assetMapIds , entityTypeId , entityId ) => this . knex ( 'asset_map' ) . whereIn ( 'id' , assetMapIds ) . update ( { entityTypeId , entityId , } ) ; /**
     * Copy entity of asset map by given ids
     */ copyAssetMapEntities = async ( { source , target , entityTypeId , } ) => { const maps = await this . knex . select ( ) . from ( 'asset_map' ) . where ( { entityTypeId , entityId : source } ) ; await Promise . all ( maps . map ( ( map ) => this . baseCreate ( { ... map , id : undefined , entityId : target } , 'asset_map' ) ) ) ; } ; /**
     * Delete asset and asset map by the given id:path maps
     */ deleteAssetAndAssetMap = async ( assetPaths ) => { const ids = Object . keys ( assetPaths ) ; const paths = Object . keys ( assetPaths ) ; await this . knex . transaction ( async ( trx ) => { await trx ( 'asset_map' ) . whereIn ( 'asset_id' , ids ) . del ( ) ; await trx ( 'asset' ) . whereIn ( 'id' , ids ) . del ( ) ; } ) ; try { await Promise . all ( paths . map ( ( path ) => this . aws . baseDeleteFile ( path ) ) ) ; } catch ( e ) { logger . error ( e ) ; } } ; /**
     * Find or Delete assets by given author id and types
     */ findAssetsByAuthorAndTypes = ( authorId , types ) => this . knex ( 'asset' ) . whereIn ( 'type' , types ) . andWhere ( { authorId } ) ; /*********************************
     *                               *
     *            Log Record         *
     *                               *
     *********************************/ findLogRecord = async ( where ) => this . knex . select ( ) . from ( 'log_record' ) . where ( where ) . first ( ) ; logRecord = async ( data ) => { return this . baseUpdateOrCreate ( { where : data , data : { readAt : new Date ( ) , ... data } , table : 'log_record' , } ) ; } ; /*********************************
     *                               *
     *           Skipped             *
     *                               *
     *********************************/ findSkippedItems = async ( { types , skip , take , } ) => { const query = this . knex ( 'blocklist' ) . whereIn ( 'type' , types ) . orderBy ( 'id' , 'desc' ) ; if ( skip ) { query . offset ( skip ) ; } if ( take || take === 0 ) { query . limit ( take ) ; } return query ; } ; findSkippedItem = async ( type , value ) => { return this . knex ( 'blocklist' ) . where ( { type , value } ) . first ( ) ; } ; countSkippedItems = async ( { types } ) => { const result = await this . knex ( 'blocklist' ) . whereIn ( 'type' , types ) . count ( ) . first ( ) ; return parseInt ( result ? ( result . count as string ) : '0' , 10 ) ; } ; createSkippedItem = async ( { type , value , uuid , note , archived , } ) => { const where = { type , value } ; return this . baseUpdateOrCreate ( { where , data : { type , value , note , archived , uuid : uuid || v4 ( ) , updatedAt : new Date ( ) , } , table : 'blocklist' , } ) ; } ; saveAgentHash = async ( value , note ? ) => { if ( ! value ) { return ; } return this . createSkippedItem ( { type : SKIPPED_LIST_ITEM_TYPES . AGENT_HASH , uuid : v4 ( ) , value , note , } ) ; } ; updateSkippedItem = async ( where , data ) => { const [ updateItem ] = await this . knex . where ( where ) . update ( data ) . into ( 'blocklist' ) . returning ( '*' ) ; return updateItem ; } ; }