diff --git a/backend/Logger.ts b/backend/Logger.ts index 42aaaee9..75418fba 100644 --- a/backend/Logger.ts +++ b/backend/Logger.ts @@ -1,9 +1,11 @@ import * as winston from 'winston'; +import {Config} from '../common/config/private/Config'; +import {LogLevel} from '../common/config/private/IPrivateConfig'; export const winstonSettings = { transports: [ new winston.transports.Console({ - level: process.env.NODE_ENV === 'production' ? 'info' : 'silly', + level: LogLevel[Config.Server.log.level], handleExceptions: true, json: false, colorize: true, diff --git a/backend/model/sql/GalleryManager.ts b/backend/model/sql/GalleryManager.ts index 379ca838..146ea9e7 100644 --- a/backend/model/sql/GalleryManager.ts +++ b/backend/model/sql/GalleryManager.ts @@ -100,7 +100,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { }); if (queryFilter.recursive) { - qb.orWhere('directory.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + queryFilter.directory + '%'}); + qb.orWhere('directory.name LIKE :text COLLATE utf8_general_ci', {text: '%' + queryFilter.directory + '%'}); } })); } diff --git a/backend/model/sql/SQLConnection.ts b/backend/model/sql/SQLConnection.ts index ae828a0b..6b771dfb 100644 --- a/backend/model/sql/SQLConnection.ts +++ b/backend/model/sql/SQLConnection.ts @@ -6,7 +6,7 @@ import {PhotoEntity} from './enitites/PhotoEntity'; import {DirectoryEntity} from './enitites/DirectoryEntity'; import {Config} from '../../../common/config/private/Config'; import {SharingEntity} from './enitites/SharingEntity'; -import {DataBaseConfig, DatabaseType} from '../../../common/config/private/IPrivateConfig'; +import {DataBaseConfig, DatabaseType, SQLLogLevel} from '../../../common/config/private/IPrivateConfig'; import {PasswordHelper} from '../PasswordHelper'; import {ProjectPath} from '../../ProjectPath'; import {VersionEntity} from './enitites/VersionEntity'; @@ -45,9 +45,9 @@ export class SQLConnection { VersionEntity ]; options.synchronize = false; - options.logging = 'error'; - // options.logging = 'all'; - + if (Config.Server.log.sqlLevel !== SQLLogLevel.none) { + options.logging = SQLLogLevel[Config.Server.log.sqlLevel]; + } this.connection = await this.createConnection(options); await SQLConnection.schemeSync(this.connection); @@ -75,7 +75,9 @@ export class SQLConnection { VersionEntity ]; options.synchronize = false; - // options.logging = "all"; + if (Config.Server.log.sqlLevel !== SQLLogLevel.none) { + options.logging = SQLLogLevel[Config.Server.log.sqlLevel]; + } const conn = await this.createConnection(options); await SQLConnection.schemeSync(conn); await conn.close(); @@ -171,7 +173,7 @@ export class SQLConnection { username: config.mysql.username, password: config.mysql.password, database: config.mysql.database, - charset: 'utf8mb4' + charset: 'utf8' }; } else if (config.type === DatabaseType.sqlite) { driver = { diff --git a/backend/model/sql/SearchManager.ts b/backend/model/sql/SearchManager.ts index 987f0eeb..12d76500 100644 --- a/backend/model/sql/SearchManager.ts +++ b/backend/model/sql/SearchManager.ts @@ -40,7 +40,7 @@ export class SearchManager implements ISearchManager { (await photoRepository .createQueryBuilder('photo') .select('DISTINCT(photo.metadata.keywords)') - .where('photo.metadata.keywords LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('photo.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) .getRawMany()) .map(r => >(r.metadataKeywords).split(',')) @@ -52,7 +52,7 @@ export class SearchManager implements ISearchManager { result = result.concat(this.encapsulateAutoComplete((await personRepository .createQueryBuilder('person') .select('DISTINCT(person.name)') - .where('person.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) .orderBy('person.name') .getRawMany()) @@ -62,9 +62,9 @@ export class SearchManager implements ISearchManager { .createQueryBuilder('photo') .select('photo.metadata.positionData.country as country, ' + 'photo.metadata.positionData.state as state, photo.metadata.positionData.city as city') - .where('photo.metadata.positionData.country LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('photo.metadata.positionData.state LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('photo.metadata.positionData.city LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('photo.metadata.positionData.country LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('photo.metadata.positionData.state LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('photo.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .groupBy('photo.metadata.positionData.country, photo.metadata.positionData.state, photo.metadata.positionData.city') .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) .getRawMany()) @@ -78,7 +78,7 @@ export class SearchManager implements ISearchManager { result = result.concat(this.encapsulateAutoComplete((await photoRepository .createQueryBuilder('media') .select('DISTINCT(media.name)') - .where('media.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) .getRawMany()) .map(r => r.name), SearchTypes.photo)); @@ -87,7 +87,7 @@ export class SearchManager implements ISearchManager { result = result.concat(this.encapsulateAutoComplete((await photoRepository .createQueryBuilder('media') .select('DISTINCT(media.metadata.caption) as caption') - .where('media.metadata.caption LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) .getRawMany()) .map(r => r.caption), SearchTypes.photo)); @@ -96,7 +96,7 @@ export class SearchManager implements ISearchManager { result = result.concat(this.encapsulateAutoComplete((await videoRepository .createQueryBuilder('media') .select('DISTINCT(media.name)') - .where('media.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) .getRawMany()) .map(r => r.name), SearchTypes.video)); @@ -104,7 +104,7 @@ export class SearchManager implements ISearchManager { result = result.concat(this.encapsulateAutoComplete((await directoryRepository .createQueryBuilder('dir') .select('DISTINCT(dir.name)') - .where('dir.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(Config.Client.Search.AutoComplete.maxItemsPerCategory) .getRawMany()) .map(r => r.name), SearchTypes.directory)); @@ -142,31 +142,31 @@ export class SearchManager implements ISearchManager { if (!searchType || searchType === SearchTypes.directory) { subQuery.leftJoin('media.directory', 'directory') - .orWhere('directory.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}); + .orWhere('directory.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}); } if (!searchType || searchType === SearchTypes.photo || searchType === SearchTypes.video) { - subQuery.orWhere('media.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}); + subQuery.orWhere('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}); } if (!searchType || searchType === SearchTypes.photo) { - subQuery.orWhere('media.metadata.caption LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}); + subQuery.orWhere('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}); } if (!searchType || searchType === SearchTypes.person) { subQuery .leftJoin('media.metadata.faces', 'faces') .leftJoin('faces.person', 'person') - .orWhere('person.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}); + .orWhere('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}); } if (!searchType || searchType === SearchTypes.position) { - subQuery.orWhere('media.metadata.positionData.country LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.state LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.city LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}); + subQuery.orWhere('media.metadata.positionData.country LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('media.metadata.positionData.state LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('media.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}); } if (!searchType || searchType === SearchTypes.keyword) { - subQuery.orWhere('media.metadata.keywords LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}); + subQuery.orWhere('media.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}); } return subQuery; @@ -187,7 +187,7 @@ export class SearchManager implements ISearchManager { result.directories = await connection .getRepository(DirectoryEntity) .createQueryBuilder('dir') - .where('dir.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(201) .getMany(); @@ -217,13 +217,13 @@ export class SearchManager implements ISearchManager { .leftJoin('media.directory', 'directory') .leftJoin('media.metadata.faces', 'faces') .leftJoin('faces.person', 'person') - .where('media.metadata.keywords LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.country LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.state LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.positionData.city LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('media.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('media.metadata.caption LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) - .orWhere('person.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('media.metadata.keywords LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('media.metadata.positionData.country LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('media.metadata.positionData.state LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('media.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('media.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('media.metadata.caption LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) + .orWhere('person.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) , 'innerMedia', 'media.id=innerMedia.id') @@ -238,7 +238,7 @@ export class SearchManager implements ISearchManager { result.directories = await connection .getRepository(DirectoryEntity) .createQueryBuilder('dir') - .where('dir.name LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + text + '%'}) + .where('dir.name LIKE :text COLLATE utf8_general_ci', {text: '%' + text + '%'}) .limit(10) .getMany(); diff --git a/backend/model/sql/enitites/EntityUtils.ts b/backend/model/sql/enitites/EntityUtils.ts index 8dbb85e8..d2b02a81 100644 --- a/backend/model/sql/enitites/EntityUtils.ts +++ b/backend/model/sql/enitites/EntityUtils.ts @@ -5,11 +5,11 @@ import {ColumnOptions} from 'typeorm/decorator/options/ColumnOptions'; export class ColumnCharsetCS implements ColumnOptions { public get charset(): string { - return Config.Server.database.type === DatabaseType.mysql ? 'utf8mb4' : null; + return Config.Server.database.type === DatabaseType.mysql ? 'utf8' : null; } public get collation(): string { - return Config.Server.database.type === DatabaseType.mysql ? 'utf8mb4_bin' : null; + return Config.Server.database.type === DatabaseType.mysql ? 'utf8_bin' : null; } } diff --git a/common/config/private/IPrivateConfig.ts b/common/config/private/IPrivateConfig.ts index 7c233609..156ed03d 100644 --- a/common/config/private/IPrivateConfig.ts +++ b/common/config/private/IPrivateConfig.ts @@ -8,6 +8,10 @@ export enum LogLevel { error = 1, warn = 2, info = 3, debug = 4, verbose = 5 } +export enum SQLLogLevel { + none = 1, error = 2, all = 3 +} + export enum ThumbnailProcessingLib { Jimp = 1, gm = 2, @@ -60,6 +64,11 @@ export interface DuplicatesConfig { listingLimit: number; // maximum number of duplicates to list } +export interface LogConfig { + level: LogLevel; + sqlLevel: SQLLogLevel; +} + export interface ServerConfig { port: number; host: string; @@ -72,6 +81,7 @@ export interface ServerConfig { indexing: IndexingConfig; photoMetadataSize: number; duplicates: DuplicatesConfig; + log: LogConfig; } export interface IPrivateConfig { diff --git a/common/config/private/PrivateConfigClass.ts b/common/config/private/PrivateConfigClass.ts index 0f5dfa4a..22a062a7 100644 --- a/common/config/private/PrivateConfigClass.ts +++ b/common/config/private/PrivateConfigClass.ts @@ -1,5 +1,13 @@ import {PublicConfigClass} from '../public/ConfigClass'; -import {DatabaseType, IPrivateConfig, ReIndexingSensitivity, ServerConfig, ThumbnailProcessingLib} from './IPrivateConfig'; +import { + DatabaseType, + IPrivateConfig, + LogLevel, + ReIndexingSensitivity, + ServerConfig, + SQLLogLevel, + ThumbnailProcessingLib +} from './IPrivateConfig'; import * as path from 'path'; import {ConfigLoader} from 'typeconfig'; import {Utils} from '../../Utils'; @@ -19,6 +27,10 @@ export class PrivateConfigClass extends PublicConfigClass implements IPrivateCon processingLibrary: ThumbnailProcessingLib.sharp, qualityPriority: true }, + log: { + level: LogLevel.info, + sqlLevel: SQLLogLevel.error + }, sessionTimeout: 1000 * 60 * 60 * 24 * 7, photoMetadataSize: 512 * 1024, database: { @@ -72,6 +84,12 @@ export class PrivateConfigClass extends PublicConfigClass implements IPrivateCon if (Utils.enumToArray(UserRoles).map(r => r.key).indexOf(this.Client.unAuthenticatedUserRole) === -1) { throw new Error('Unknown user role for Client.unAuthenticatedUserRole, found: ' + this.Client.unAuthenticatedUserRole); } + if (Utils.enumToArray(LogLevel).map(r => r.key).indexOf(this.Server.log.level) === -1) { + throw new Error('Unknown Server.log.level, found: ' + this.Server.log.level); + } + if (Utils.enumToArray(SQLLogLevel).map(r => r.key).indexOf(this.Server.log.sqlLevel) === -1) { + throw new Error('Unknown Server.log.level, found: ' + this.Server.log.sqlLevel); + } } diff --git a/frontend/app/settings/settings.service.ts b/frontend/app/settings/settings.service.ts index 2f19fe12..06742110 100644 --- a/frontend/app/settings/settings.service.ts +++ b/frontend/app/settings/settings.service.ts @@ -1,6 +1,13 @@ import {Injectable} from '@angular/core'; import {BehaviorSubject} from 'rxjs'; -import {DatabaseType, IPrivateConfig, ReIndexingSensitivity, ThumbnailProcessingLib} from '../../../common/config/private/IPrivateConfig'; +import { + DatabaseType, + IPrivateConfig, + LogLevel, + ReIndexingSensitivity, + SQLLogLevel, + ThumbnailProcessingLib +} from '../../../common/config/private/IPrivateConfig'; import {NetworkService} from '../model/network/network.service'; import {SortingMethods} from '../../../common/entities/SortingMethods'; import {UserRoles} from '../../../common/entities/UserDTO'; @@ -74,6 +81,10 @@ export class SettingsService { database: { type: DatabaseType.memory }, + log: { + level: LogLevel.info, + sqlLevel: SQLLogLevel.error + }, sharing: { updateTimeout: 2000 }, diff --git a/test/backend/integration/model/sql/typeorm.ts b/test/backend/integration/model/sql/typeorm.ts index e7744627..c159efcb 100644 --- a/test/backend/integration/model/sql/typeorm.ts +++ b/test/backend/integration/model/sql/typeorm.ts @@ -184,7 +184,7 @@ describe('Typeorm integration', () => { const photos = await pr .createQueryBuilder('media') .orderBy('media.metadata.creationDate', 'ASC') - .where('media.metadata.positionData.city LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + photo.metadata.positionData.city + '%'}) + .where('media.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + photo.metadata.positionData.city + '%'}) .innerJoinAndSelect('media.directory', 'directory') .limit(10) .getMany(); @@ -206,7 +206,7 @@ describe('Typeorm integration', () => { const photos = await pr .createQueryBuilder('media') .orderBy('media.metadata.creationDate', 'ASC') - .where('media.metadata.positionData.city LIKE :text COLLATE utf8mb4_general_ci', {text: '%' + city + '%'}) + .where('media.metadata.positionData.city LIKE :text COLLATE utf8_general_ci', {text: '%' + city + '%'}) .innerJoinAndSelect('media.directory', 'directory') .limit(10) .getMany();