Merge branch 'release/rc-v1.2.0' of https://github.com/ONLYOFFICE/DocSpace into release/rc-v1.2.0
This commit is contained in:
commit
13dd1bdd62
@ -79,6 +79,7 @@ services_name_backend+=(ASC.Web.Studio)
|
||||
services_name_backend+=(ASC.Data.Backup.BackgroundTasks)
|
||||
services_name_backend+=(ASC.ClearEvents)
|
||||
services_name_backend+=(ASC.ApiSystem)
|
||||
services_name_backend+=(ASC.Web.HealthChecks.UI)
|
||||
|
||||
# Publish backend services
|
||||
for i in ${!services_name_backend[@]}; do
|
||||
|
@ -68,6 +68,7 @@
|
||||
PROXY_HOST=${CONTAINER_PREFIX}proxy
|
||||
DOCEDITOR_HOST=${CONTAINER_PREFIX}doceditor
|
||||
LOGIN_HOST=${CONTAINER_PREFIX}login
|
||||
HELTHCHECKS_HOST=${CONTAINER_PREFIX}healthchecks
|
||||
|
||||
# proxy upstream environment #
|
||||
SERVICE_API_SYSTEM=${API_SYSTEM_HOST}:${SERVICE_PORT}
|
||||
|
@ -296,6 +296,16 @@ COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Web.St
|
||||
|
||||
CMD ["ASC.Web.Studio.dll", "ASC.Web.Studio"]
|
||||
|
||||
## ASC.Web.HealthChecks.UI ##
|
||||
FROM dotnetrun AS healthchecks
|
||||
WORKDIR ${BUILD_PATH}/services/ASC.Web.HealthChecks.UI/service
|
||||
|
||||
COPY --chown=onlyoffice:onlyoffice docker-healthchecks-entrypoint.sh ./docker-healthchecks-entrypoint.sh
|
||||
COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Web.HealthChecks.UI/service/ .
|
||||
|
||||
ENTRYPOINT ["./docker-healthchecks-entrypoint.sh"]
|
||||
CMD ["ASC.Web.HealthChecks.UI.dll", "ASC.Web.HealthChecks.UI"]
|
||||
|
||||
## ASC.Migration.Runner ##
|
||||
FROM $DOTNET_RUN AS onlyoffice-migration-runner
|
||||
ARG BUILD_PATH
|
||||
|
@ -133,3 +133,10 @@ services:
|
||||
dockerfile: "${DOCKERFILE}"
|
||||
target: onlyoffice-migration-runner
|
||||
image: "${REPO}/${DOCKER_IMAGE_PREFIX}-migration-runner:${DOCKER_TAG}"
|
||||
|
||||
onlyoffice-healthchecks:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: "${DOCKERFILE}"
|
||||
target: healthchecks
|
||||
image: "${REPO}/${DOCKER_IMAGE_PREFIX}-healthchecks:${DOCKER_TAG}"
|
||||
|
46
build/install/docker/docker-healthchecks-entrypoint.sh
Executable file
46
build/install/docker/docker-healthchecks-entrypoint.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# read parameters
|
||||
if [ -n "$1" ]; then
|
||||
RUN_DLL="${1}";
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
NAME_SERVICE="${1}";
|
||||
shift
|
||||
fi
|
||||
|
||||
echo "Executing -- ${NAME_SERVICE}"
|
||||
|
||||
PRODUCT=${PRODUCT:-"onlyoffice"}
|
||||
CONTAINER_PREFIX=${PRODUCT}-
|
||||
SERVICE_PORT=${SERVICE_PORT:-"5050"}
|
||||
SHEME=${SHEME:-"http"}
|
||||
URLS=${URLS:-"${SHEME}://0.0.0.0:${SERVICE_PORT}"}
|
||||
PATH_TO_CONF=${PATH_TO_CONF:-"/var/www/services/ASC.Web.HealthChecks.UI/service"}
|
||||
|
||||
API_SYSTEM_HOST=${API_SYSTEM_HOST:-"${CONTAINER_PREFIX}api-system:${SERVICE_PORT}"}
|
||||
BACKUP_HOST=${BACKUP_HOST:-"${CONTAINER_PREFIX}backup:${SERVICE_PORT}"}
|
||||
BACKUP_BACKGRUOND_TASKS_HOST=${BACKUP_BACKGRUOND_TASKS_HOST:-"${CONTAINER_PREFIX}backup-background-tasks:${SERVICE_PORT}"}
|
||||
CLEAR_EVENTS_HOST=${CLEAR_EVENTS_HOST:-"${CONTAINER_PREFIX}clear-events:${SERVICE_PORT}"}
|
||||
FILES_HOST=${FILES_HOST:-"${CONTAINER_PREFIX}files:${SERVICE_PORT}"}
|
||||
FILES_SERVICES_HOST=${FILES_SERVICES_HOST:-"${CONTAINER_PREFIX}files-services:${SERVICE_PORT}"}
|
||||
NOTIFY_HOST=${NOTIFY_HOST:-"${CONTAINER_PREFIX}notify:${SERVICE_PORT}"}
|
||||
PEOPLE_SERVER_HOST=${PEOPLE_SERVER_HOST:-"${CONTAINER_PREFIX}people-server:${SERVICE_PORT}"}
|
||||
STUDIO_NOTIFY_HOST=${STUDIO_NOTIFY_HOST:-"${CONTAINER_PREFIX}studio-notify:${SERVICE_PORT}"}
|
||||
API_HOST=${API_HOST:-"${CONTAINER_PREFIX}api:${SERVICE_PORT}"}
|
||||
STUDIO_HOST=${STUDIO_HOST:-"${CONTAINER_PREFIX}studio:${SERVICE_PORT}"}
|
||||
|
||||
sed -i "s!localhost:5010!${API_SYSTEM_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5012!${BACKUP_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5032!${BACKUP_BACKGRUOND_TASKS_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5027!${CLEAR_EVENTS_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5007!${FILES_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5009!${FILES_SERVICES_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5005!${NOTIFY_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5004!${PEOPLE_SERVER_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5006!${API_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5000!${STUDIO_NOTIFY_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
sed -i "s!localhost:5003!${STUDIO_HOST}!g" ${PATH_TO_CONF}/appsettings.json
|
||||
|
||||
dotnet ${RUN_DLL} --urls=${URLS}
|
29
build/install/docker/helthchecks.yml
Normal file
29
build/install/docker/helthchecks.yml
Normal file
@ -0,0 +1,29 @@
|
||||
version: "3.8"
|
||||
x-service:
|
||||
&x-service-base
|
||||
container_name: base
|
||||
restart: always
|
||||
expose:
|
||||
- ${SERVICE_PORT}
|
||||
volumes:
|
||||
#- /app/onlyoffice/CommunityServer/data:/app/onlyoffice/data
|
||||
- app_data:/app/onlyoffice/data
|
||||
- files_data:/var/www/products/ASC.Files/server/
|
||||
- people_data:/var/www/products/ASC.People/server/
|
||||
|
||||
services:
|
||||
onlyoffice-health-checks-ui:
|
||||
<<: *x-service-base
|
||||
image: "${REPO}/${DOCKER_IMAGE_PREFIX}-healthchecks:${DOCKER_TAG}"
|
||||
container_name: ${HELTHCHECKS_HOST}
|
||||
|
||||
networks:
|
||||
default:
|
||||
external:
|
||||
name: ${NETWORK_NAME}
|
||||
|
||||
volumes:
|
||||
app_data:
|
||||
files_data:
|
||||
people_data:
|
||||
|
Binary file not shown.
Binary file not shown.
@ -42,8 +42,10 @@ public class MigrationCreator
|
||||
private string _pathToSave;
|
||||
private string _userName;
|
||||
private string _mail;
|
||||
private string _toRegion;
|
||||
private int _tenant;
|
||||
private string _toRegion;
|
||||
private string _toAlias;
|
||||
private string _fromAlias;
|
||||
private int _fromTenantId;
|
||||
private readonly object _locker = new object();
|
||||
private readonly int _limit = 1000;
|
||||
private readonly List<ModuleName> _namesModules = new List<ModuleName>()
|
||||
@ -55,6 +57,13 @@ public class MigrationCreator
|
||||
ModuleName.WebStudio
|
||||
};
|
||||
|
||||
private readonly List<ModuleName> _namesModulesForAlreadyExistPortal = new List<ModuleName>()
|
||||
{
|
||||
ModuleName.Core,
|
||||
ModuleName.Files,
|
||||
ModuleName.Files2,
|
||||
};
|
||||
|
||||
public string NewAlias { get; private set; }
|
||||
|
||||
public MigrationCreator(
|
||||
@ -73,9 +82,9 @@ public class MigrationCreator
|
||||
_moduleProvider = moduleProvider;
|
||||
}
|
||||
|
||||
public string Create(int tenant, string userName, string mail, string toRegion)
|
||||
public string Create(string fromAlias, string userName, string mail, string toRegion, string toAlias)
|
||||
{
|
||||
Init(tenant, userName, mail, toRegion);
|
||||
Init(fromAlias, userName, mail, toRegion, toAlias);
|
||||
|
||||
var id = GetUserId();
|
||||
var fileName = _userName + ".tar.gz";
|
||||
@ -88,49 +97,92 @@ public class MigrationCreator
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private void Init(int tenant, string userName, string mail, string toRegion)
|
||||
private void Init(string fromAlias, string userName, string mail, string toRegion, string toAlias)
|
||||
{
|
||||
_modules = _moduleProvider.AllModules.Where(m => _namesModules.Contains(m.ModuleName)).ToList();
|
||||
|
||||
_pathToSave = "";
|
||||
_toRegion = toRegion;
|
||||
_userName = userName;
|
||||
_mail = mail;
|
||||
_tenant = tenant;
|
||||
_fromAlias = fromAlias;
|
||||
_toAlias = toAlias;
|
||||
|
||||
using var dbContextTenant = _dbFactory.CreateDbContext<TenantDbContext>();
|
||||
var tenant = dbContextTenant.Tenants.SingleOrDefault(q => q.Alias == _fromAlias);
|
||||
|
||||
if (tenant == null)
|
||||
{
|
||||
throw new ArgumentException("tenant was not found");
|
||||
}
|
||||
_fromTenantId = tenant.Id;
|
||||
|
||||
_modules = string.IsNullOrEmpty(_toAlias)
|
||||
? _moduleProvider.AllModules.Where(m => _namesModules.Contains(m.ModuleName)).ToList()
|
||||
: _moduleProvider.AllModules.Where(m => _namesModulesForAlreadyExistPortal.Contains(m.ModuleName)).ToList();
|
||||
}
|
||||
|
||||
private Guid GetUserId()
|
||||
{
|
||||
try
|
||||
{
|
||||
var userDbContext = _dbFactory.CreateDbContext<UserDbContext>();
|
||||
using var userDbContext = _dbFactory.CreateDbContext<UserDbContext>();
|
||||
User user = null;
|
||||
if (string.IsNullOrEmpty(_userName) || string.IsNullOrEmpty(_mail))
|
||||
{
|
||||
if (string.IsNullOrEmpty(_userName))
|
||||
{
|
||||
user = userDbContext.Users.FirstOrDefault(q => q.Tenant == _tenant && q.Status == EmployeeStatus.Active && q.Email == _mail);
|
||||
user = userDbContext.Users.FirstOrDefault(q => q.Tenant == _fromTenantId && q.Status == EmployeeStatus.Active && q.Email == _mail);
|
||||
_userName = user.UserName;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = userDbContext.Users.FirstOrDefault(q => q.Tenant == _tenant && q.Status == EmployeeStatus.Active && q.UserName == _userName);
|
||||
user = userDbContext.Users.FirstOrDefault(q => q.Tenant == _fromTenantId && q.Status == EmployeeStatus.Active && q.UserName == _userName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = userDbContext.Users.FirstOrDefault(q => q.Tenant == _tenant && q.Status == EmployeeStatus.Active && q.UserName == _userName && q.Email == _mail);
|
||||
user = userDbContext.Users.FirstOrDefault(q => q.Tenant == _fromTenantId && q.Status == EmployeeStatus.Active && q.UserName == _userName && q.Email == _mail);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(_toAlias))
|
||||
{
|
||||
using var dbContextTenant = _dbFactory.CreateDbContext<TenantDbContext>(_toRegion);
|
||||
using var userDbContextToregion = _dbFactory.CreateDbContext<UserDbContext>(_toRegion);
|
||||
var tenant = dbContextTenant.Tenants.SingleOrDefault(t => t.Alias == _toAlias);
|
||||
if (tenant == null)
|
||||
{
|
||||
throw new ArgumentException("tenant was not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (userDbContextToregion.Users.Any(q => q.Tenant == tenant.Id && q.UserName == _userName || q.Email == _mail))
|
||||
{
|
||||
throw new ArgumentException("username already exist in the portal");
|
||||
}
|
||||
}
|
||||
}
|
||||
return user.Id;
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new Exception("username was not found");
|
||||
throw new ArgumentException("username was not found");
|
||||
}
|
||||
}
|
||||
|
||||
private void DoMigrationDb(Guid id, IDataWriteOperator writer)
|
||||
{
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_toAlias))
|
||||
{
|
||||
using (var connection = _dbFactory.OpenConnection())
|
||||
{
|
||||
var tenantsModule = _moduleProvider.AllModules.Single(q => q.ModuleName == ModuleName.Tenants);
|
||||
var coreUserTable = tenantsModule.Tables.Single(q => q.Name == "core_user");
|
||||
ArhiveTable(coreUserTable, writer, tenantsModule, connection, id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var module in _modules)
|
||||
{
|
||||
var tablesToProcess = module.Tables.Where(t => t.InsertMethod != InsertMethod.None).ToList();
|
||||
@ -143,55 +195,63 @@ public class MigrationCreator
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"backup table {table.Name}");
|
||||
using (var data = new DataTable(table.Name))
|
||||
{
|
||||
ActionInvoker.Try(
|
||||
state =>
|
||||
{
|
||||
data.Clear();
|
||||
int counts;
|
||||
var offset = 0;
|
||||
do
|
||||
{
|
||||
var t = (TableInfo)state;
|
||||
var dataAdapter = _dbFactory.CreateDataAdapter();
|
||||
dataAdapter.SelectCommand = module.CreateSelectCommand(connection.Fix(), _tenant, t, _limit, offset, id).WithTimeout(600);
|
||||
counts = ((DbDataAdapter)dataAdapter).Fill(data);
|
||||
offset += _limit;
|
||||
} while (counts == _limit);
|
||||
|
||||
},
|
||||
table,
|
||||
maxAttempts: 5,
|
||||
onFailure: error => { throw ThrowHelper.CantBackupTable(table.Name, error); });
|
||||
|
||||
foreach (var col in data.Columns.Cast<DataColumn>().Where(col => col.DataType == typeof(DateTime)))
|
||||
{
|
||||
col.DateTimeMode = DataSetDateTime.Unspecified;
|
||||
}
|
||||
|
||||
module.PrepareData(data);
|
||||
|
||||
if (data.TableName == "tenants_tenants")
|
||||
{
|
||||
ChangeAlias(data);
|
||||
ChangeName(data);
|
||||
}
|
||||
|
||||
using (var file = _tempStream.Create())
|
||||
{
|
||||
data.WriteXml(file, XmlWriteMode.WriteSchema);
|
||||
data.Clear();
|
||||
|
||||
writer.WriteEntry(KeyHelper.GetTableZipKey(module, data.TableName), file);
|
||||
}
|
||||
}
|
||||
ArhiveTable(table, writer, module, connection, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ArhiveTable(TableInfo table, IDataWriteOperator writer, IModuleSpecifics module, DbConnection connection, Guid id)
|
||||
{
|
||||
Console.WriteLine($"backup table {table.Name}");
|
||||
using (var data = new DataTable(table.Name))
|
||||
{
|
||||
ActionInvoker.Try(
|
||||
state =>
|
||||
{
|
||||
data.Clear();
|
||||
int counts;
|
||||
var offset = 0;
|
||||
do
|
||||
{
|
||||
var t = (TableInfo)state;
|
||||
var dataAdapter = _dbFactory.CreateDataAdapter();
|
||||
dataAdapter.SelectCommand = module.CreateSelectCommand(connection.Fix(), _fromTenantId, t, _limit, offset, id).WithTimeout(600);
|
||||
counts = ((DbDataAdapter)dataAdapter).Fill(data);
|
||||
offset += _limit;
|
||||
} while (counts == _limit);
|
||||
|
||||
},
|
||||
table,
|
||||
maxAttempts: 5,
|
||||
onFailure: error => { throw ThrowHelper.CantBackupTable(table.Name, error); });
|
||||
|
||||
foreach (var col in data.Columns.Cast<DataColumn>().Where(col => col.DataType == typeof(DateTime)))
|
||||
{
|
||||
col.DateTimeMode = DataSetDateTime.Unspecified;
|
||||
}
|
||||
|
||||
module.PrepareData(data);
|
||||
|
||||
if (data.TableName == "tenants_tenants")
|
||||
{
|
||||
ChangeAlias(data);
|
||||
ChangeName(data);
|
||||
}
|
||||
|
||||
WriteEnrty(data, writer, module);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteEnrty(DataTable data, IDataWriteOperator writer, IModuleSpecifics module)
|
||||
{
|
||||
using (var file = _tempStream.Create())
|
||||
{
|
||||
data.WriteXml(file, XmlWriteMode.WriteSchema);
|
||||
data.Clear();
|
||||
|
||||
writer.WriteEntry(KeyHelper.GetTableZipKey(module, data.TableName), file);
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeAlias(DataTable data)
|
||||
@ -255,8 +315,8 @@ public class MigrationCreator
|
||||
private void ChangeName(DataTable data)
|
||||
{
|
||||
data.Rows[0]["name"] = "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void DoMigrationStorage(Guid id, IDataWriteOperator writer)
|
||||
{
|
||||
Console.WriteLine($"start backup storage");
|
||||
@ -266,7 +326,7 @@ public class MigrationCreator
|
||||
Console.WriteLine($"start backup fileGroup: {group.Key}");
|
||||
foreach (var file in group)
|
||||
{
|
||||
var storage = _storageFactory.GetStorage(_tenant, group.Key);
|
||||
var storage = _storageFactory.GetStorage(_fromTenantId, group.Key);
|
||||
var file1 = file;
|
||||
ActionInvoker.Try(state =>
|
||||
{
|
||||
@ -307,9 +367,9 @@ public class MigrationCreator
|
||||
|
||||
var module = _storageFactoryConfig.GetModuleList().Where(m => m == "files").Single();
|
||||
|
||||
var store = _storageFactory.GetStorage(_tenant, module);
|
||||
var store = _storageFactory.GetStorage(_fromTenantId, module);
|
||||
|
||||
var dbFiles = filesDbContext.Files.Where(q => q.CreateBy == id && q.TenantId == _tenant).ToList();
|
||||
var dbFiles = filesDbContext.Files.Where(q => q.CreateBy == id && q.TenantId == _fromTenantId).ToList();
|
||||
|
||||
var tasks = new List<Task>(20);
|
||||
foreach (var dbFile in dbFiles)
|
||||
@ -331,7 +391,7 @@ public class MigrationCreator
|
||||
private async Task FindFiles(List<BackupFileInfo> list, IDataStore store, DbFile dbFile, string module)
|
||||
{
|
||||
var files = await store.ListFilesRelativeAsync(string.Empty, $"\\{GetUniqFileDirectory(dbFile.Id)}", "*.*", true)
|
||||
.Select(path => new BackupFileInfo(string.Empty, module, $"{GetUniqFileDirectory(dbFile.Id)}\\{path}", _tenant))
|
||||
.Select(path => new BackupFileInfo(string.Empty, module, $"{GetUniqFileDirectory(dbFile.Id)}\\{path}", _fromTenantId))
|
||||
.ToListAsync();
|
||||
|
||||
lock (_locker)
|
||||
|
@ -61,13 +61,29 @@ public class MigrationRunner
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Run(string backupFile, string region)
|
||||
public async Task Run(string backupFile, string region, string fromAlias, string toAlias)
|
||||
{
|
||||
_region = region;
|
||||
_modules = _moduleProvider.AllModules.Where(m => _namesModules.Contains(m.ModuleName)).ToList();
|
||||
_backupFile = backupFile;
|
||||
var columnMapper = new ColumnMapper();
|
||||
|
||||
if (!string.IsNullOrEmpty(toAlias))
|
||||
{
|
||||
using var dbContextTenant = _dbFactory.CreateDbContext<TenantDbContext>();
|
||||
var fromTenant = dbContextTenant.Tenants.SingleOrDefault(q => q.Alias == fromAlias);
|
||||
|
||||
using var dbContextToTenant = _dbFactory.CreateDbContext<TenantDbContext>(region);
|
||||
var toTenant = dbContextToTenant.Tenants.SingleOrDefault(q => q.Alias == toAlias);
|
||||
|
||||
toTenant.Status = TenantStatus.Restoring;
|
||||
toTenant.StatusChanged = DateTime.UtcNow;
|
||||
|
||||
dbContextTenant.Tenants.Update(toTenant);
|
||||
dbContextToTenant.SaveChanges();
|
||||
|
||||
columnMapper.SetMapping("tenants_tenants", "id", fromTenant.Id, toTenant.Id);
|
||||
columnMapper.Commit();
|
||||
}
|
||||
using (var dataReader = new ZipReadOperator(_backupFile))
|
||||
{
|
||||
foreach (var module in _modules)
|
||||
|
@ -37,8 +37,7 @@ var param = Parser.Default.ParseArguments<Options>(args).Value;
|
||||
{
|
||||
FromRegion = "personal",
|
||||
ToRegion = "personal",
|
||||
Tenant = 1,
|
||||
UserName = "administrator"
|
||||
FromAlias = "localhost"
|
||||
};*/
|
||||
|
||||
var builder = WebApplication.CreateBuilder(options);
|
||||
@ -94,11 +93,12 @@ if(string.IsNullOrEmpty(param.UserName) && string.IsNullOrEmpty(param.Mail))
|
||||
var app = builder.Build();
|
||||
Console.WriteLine("backup start");
|
||||
var migrationCreator = app.Services.GetService<MigrationCreator>();
|
||||
var fileName = migrationCreator.Create(param.Tenant, param.UserName, param.Mail, param.ToRegion);
|
||||
var fileName = migrationCreator.Create(param.FromAlias, param.UserName, param.Mail, param.ToRegion, param.ToAlias);
|
||||
Console.WriteLine("backup was success");
|
||||
|
||||
Console.WriteLine("restore start");
|
||||
var migrationRunner = app.Services.GetService<MigrationRunner>();
|
||||
await migrationRunner.Run(fileName, param.ToRegion);
|
||||
await migrationRunner.Run(fileName, param.ToRegion, param.FromAlias, param.ToAlias);
|
||||
Console.WriteLine("restore was success");
|
||||
|
||||
Directory.GetFiles(AppContext.BaseDirectory).Where(f => f.Equals(fileName)).ToList().ForEach(File.Delete);
|
||||
@ -109,12 +109,14 @@ if (Directory.Exists(AppContext.BaseDirectory + "\\temp"))
|
||||
}
|
||||
|
||||
Console.WriteLine("migration was success");
|
||||
Console.WriteLine($"new alias is - {migrationCreator.NewAlias}");
|
||||
|
||||
if (!string.IsNullOrEmpty(migrationCreator.NewAlias))
|
||||
{
|
||||
Console.WriteLine($"new alias is - {migrationCreator.NewAlias}");
|
||||
}
|
||||
public sealed class Options
|
||||
{
|
||||
[Option('t', "tenant", Required = true)]
|
||||
public int Tenant { get; set; }
|
||||
[Option('a', "FromAlias", Required = true)]
|
||||
public string FromAlias { get; set; }
|
||||
|
||||
[Option('u', "username", Required = false, HelpText = "enter username or mail for find user")]
|
||||
public string UserName { get; set; }
|
||||
@ -122,9 +124,12 @@ public sealed class Options
|
||||
[Option('m', "mail", Required = false, HelpText = "enter username or mail for find user")]
|
||||
public string Mail { get; set; }
|
||||
|
||||
[Option("toregion", Required = true)]
|
||||
[Option('t' ,"toregion", Required = true)]
|
||||
public string ToRegion { get; set; }
|
||||
|
||||
[Option("fromregion", Required = false, Default = "personal")]
|
||||
[Option('f' ,"fromregion", Required = false, Default = "personal")]
|
||||
public string FromRegion { get; set; }
|
||||
|
||||
[Option("ToAlias", Required = false, HelpText = "if you wish migration to already exist portal, enter the alias")]
|
||||
public string ToAlias { get; set; }
|
||||
}
|
@ -42,7 +42,7 @@
|
||||
"property": [
|
||||
{
|
||||
"name": "$STORAGE_ROOT",
|
||||
"value": "..\\Data\\"
|
||||
"value": "..\\..\\..\\Data\\"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -90,7 +90,7 @@
|
||||
"property": [
|
||||
{
|
||||
"name": "$STORAGE_ROOT",
|
||||
"value": "..\\Data\\"
|
||||
"value": "..\\..\\..\\Data\\"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,120 +0,0 @@
|
||||
{
|
||||
"DbProviderFactories": {
|
||||
"mysql": {
|
||||
"name": "MySQL Data Provider",
|
||||
"invariant": "MySql.Data.MySqlClient",
|
||||
"description": ".Net Framework Data Provider for MySQL",
|
||||
"type": "MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data"
|
||||
}
|
||||
},
|
||||
"core": {
|
||||
"machinekey": "1123askdasjklasbnd",
|
||||
"payment": {
|
||||
"delay": 10,
|
||||
"region": "test",
|
||||
"test": true,
|
||||
"url": "",
|
||||
"key": "",
|
||||
"secret": ""
|
||||
}
|
||||
},
|
||||
|
||||
"regions": {
|
||||
"personal": {
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
"name": "default",
|
||||
"connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"appender": [
|
||||
{
|
||||
"name": "generic",
|
||||
"append": "~/"
|
||||
}
|
||||
],
|
||||
"handler": [
|
||||
{
|
||||
"name": "disc",
|
||||
"type": "ASC.Data.Storage.DiscStorage.DiscDataStore, ASC.Data.Storage",
|
||||
"property": [
|
||||
{
|
||||
"name": "$STORAGE_ROOT",
|
||||
"value": "..\\Data\\"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"module": [
|
||||
{
|
||||
"name": "files",
|
||||
"data": "e67be73d-f9ae-4ce1-8fec-1880cb518cb4",
|
||||
"type": "disc",
|
||||
"path": "$STORAGE_ROOT\\Products\\Files",
|
||||
"expires": "0:16:0",
|
||||
"domain": [
|
||||
{
|
||||
"name": "files_temp",
|
||||
"visible": false,
|
||||
"data": "00000000-0000-0000-0000-000000000000",
|
||||
"path": "$STORAGE_ROOT\\Products\\Files\\{0}\\temp",
|
||||
"virtualpath": "~/products/community/modules/wiki/data/filestemp",
|
||||
"expires": "0:10:0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"docSpace": {
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
"name": "default",
|
||||
"connectionString": "Server=localhost;Database=onlyoffice1;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;ConnectionReset=false",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"appender": [
|
||||
{
|
||||
"name": "generic",
|
||||
"append": "~/"
|
||||
}
|
||||
],
|
||||
"handler": [
|
||||
{
|
||||
"name": "disc",
|
||||
"type": "ASC.Data.Storage.DiscStorage.DiscDataStore, ASC.Data.Storage",
|
||||
"property": [
|
||||
{
|
||||
"name": "$STORAGE_ROOT",
|
||||
"value": "..\\Data\\"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"module": [
|
||||
{
|
||||
"name": "files",
|
||||
"data": "e67be73d-f9ae-4ce1-8fec-1880cb518cb4",
|
||||
"type": "disc",
|
||||
"path": "$STORAGE_ROOT\\Products\\Files1",
|
||||
"expires": "0:16:0",
|
||||
"domain": [
|
||||
{
|
||||
"name": "files_temp",
|
||||
"visible": false,
|
||||
"data": "00000000-0000-0000-0000-000000000000",
|
||||
"path": "$STORAGE_ROOT\\Products\\Files1\\{0}\\temp",
|
||||
"virtualpath": "~/products/community/modules/wiki/data/filestemp",
|
||||
"expires": "0:10:0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
{
|
||||
"DeleteGroupUsersSuccessMessage": "Benutzer wurden erfolgreich gelöscht.",
|
||||
"DeleteUsers": "Benutzer löschen",
|
||||
"DeleteUsersMessage": "Die ausgewählten deaktivierten Benutzer werden aus dem Portal gelöscht. Persönliche Dokumente dieser Benutzer, die für andere verfügbar sind, werden auch gelöscht. Um dies zu vermeiden, müssen Sie den Datenübertragungsprozess starten."
|
||||
"DeleteUsers": "Benutzer löschen"
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"DeleteGroupUsersSuccessMessage": "Users have been successfully deleted.",
|
||||
"DeleteUsers": "Delete users",
|
||||
"DeleteUsersMessage": "The selected disabled users will be deleted from the DocSpace. Personal documents of these users which are available to others will be deleted. To avoid this, you need to start the data transfer process before deleting."
|
||||
"DeleteUsersMessage": "The selected disabled users will be deleted from the DocSpace. Personal documents of these users which are available to others will be deleted."
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{
|
||||
"DeleteGroupUsersSuccessMessage": "Les utilisateurs ont été supprimés avec succès.",
|
||||
"DeleteUsers": "Supprimer les utilisateurs",
|
||||
"DeleteUsersMessage": "Les utilisateurs désactivés sélectionnés seront supprimés du portail. Les documents personnels de ces utilisateurs qui sont partagés avec d'autres seront supprimés. Pour éviter cela, vous devez démarrer le processus de transfert de données avant la suppression."
|
||||
"DeleteUsers": "Supprimer les utilisateurs"
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{
|
||||
"DeleteGroupUsersSuccessMessage": "Gli utenti sono stati eliminati con successo.",
|
||||
"DeleteUsers": "Eliminare utenti",
|
||||
"DeleteUsersMessage": "Gli utenti disabilitati selezionati verranno eliminati dal portale. I documenti personali di questi utenti disponibili per gli altri verranno eliminati. Per evitarlo, è necessario avviare il processo di trasferimento dei dati prima di eliminarli."
|
||||
"DeleteUsers": "Eliminare utenti"
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{
|
||||
"DeleteGroupUsersSuccessMessage": "ユーザーが正常に削除されました。",
|
||||
"DeleteUsers": "ユーザーを削除する",
|
||||
"DeleteUsersMessage": "選択した無効のユーザーは、ポータルサイトから削除されます。これらのユーザーの個人文書で、他の人が利用できるものも削除されます。これを避けるには、削除する前にデータ転送プロセスを開始する必要があります。"
|
||||
"DeleteUsers": "ユーザーを削除する"
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"DeleteGroupUsersSuccessMessage": "Пользователи были успешно удалены",
|
||||
"DeleteUsers": "Удалить пользователей",
|
||||
"DeleteUsersMessage": "Выбранные заблокированные пользователи будут удалены с портала. Будут удалены личные документы этих пользователей, доступные для других. Чтобы избежать этого, нужно перед удалением запустить процесс передачи данных."
|
||||
"DeleteUsersMessage": "Выбранные заблокированные пользователи будут удалены с портала. Будут удалены личные документы этих пользователей, доступные для других."
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{
|
||||
"DeleteGroupUsersSuccessMessage": "用户已成功删除。",
|
||||
"DeleteUsers": "删除用户",
|
||||
"DeleteUsersMessage": "选择的禁用用户将从门户网站中删除。这些用户对其他人可用的个人文档将被删除。为了避免这种情况,您必须在删除前启动数据传输过程。"
|
||||
"DeleteUsers": "删除用户"
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ const EmptyFolderContainer = ({
|
||||
tReady,
|
||||
isLoadedFetchFiles,
|
||||
viewAs,
|
||||
setIsLoadedEmptyPage,
|
||||
}) => {
|
||||
const onBackToParentFolder = () => {
|
||||
setIsLoading(true);
|
||||
@ -35,10 +36,21 @@ const EmptyFolderContainer = ({
|
||||
: fetchFiles(parentId).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isLoadedFetchFiles && tReady) {
|
||||
setIsLoadedEmptyPage(true);
|
||||
} else {
|
||||
setIsLoadedEmptyPage(false);
|
||||
}
|
||||
}, [isLoadedFetchFiles, tReady]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsEmptyPage(true);
|
||||
|
||||
return () => setIsEmptyPage(false);
|
||||
return () => {
|
||||
setIsEmptyPage(false);
|
||||
setIsLoadedEmptyPage(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onInviteUsersClick = () => {
|
||||
@ -161,6 +173,7 @@ export default inject(
|
||||
setIsEmptyPage,
|
||||
isLoadedFetchFiles,
|
||||
viewAs,
|
||||
setIsLoadedEmptyPage,
|
||||
} = filesStore;
|
||||
const {
|
||||
navigationPath,
|
||||
@ -198,6 +211,7 @@ export default inject(
|
||||
folderId,
|
||||
isLoadedFetchFiles,
|
||||
viewAs,
|
||||
setIsLoadedEmptyPage,
|
||||
};
|
||||
}
|
||||
)(withTranslation(["Files", "Translations"])(observer(EmptyFolderContainer)));
|
||||
|
@ -48,6 +48,7 @@ const RootFolderContainer = (props) => {
|
||||
setIsEmptyPage,
|
||||
isVisitor,
|
||||
sectionWidth,
|
||||
setIsLoadedEmptyPage,
|
||||
} = props;
|
||||
const personalDescription = t("EmptyFolderDecription");
|
||||
|
||||
@ -87,8 +88,13 @@ const RootFolderContainer = (props) => {
|
||||
|
||||
return () => {
|
||||
setIsEmptyPage(false);
|
||||
setIsLoadedEmptyPage(false);
|
||||
};
|
||||
}, [isEmptyPage, setIsEmptyPage, rootFolderType]);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsLoadedEmptyPage(!isLoading);
|
||||
}, [isLoading]);
|
||||
|
||||
const onGoToPersonal = () => {
|
||||
const newFilter = filter.clone();
|
||||
@ -313,31 +319,17 @@ const RootFolderContainer = (props) => {
|
||||
const headerText = isPrivacyFolder ? privateRoomHeader : title;
|
||||
const emptyFolderProps = getEmptyFolderProps();
|
||||
|
||||
React.useEffect(() => {
|
||||
let timeout;
|
||||
|
||||
if (isLoading) {
|
||||
setShowLoader(isLoading);
|
||||
} else {
|
||||
timeout = setTimeout(() => setShowLoader(isLoading), 300);
|
||||
}
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [isLoading]);
|
||||
if (isLoading) {
|
||||
return <Loaders.EmptyContainerLoader viewAs={viewAs} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{showLoader ? (
|
||||
<Loaders.EmptyContainerLoader viewAs={viewAs} />
|
||||
) : (
|
||||
<EmptyContainer
|
||||
headerText={headerText}
|
||||
isEmptyPage={isEmptyPage}
|
||||
sectionWidth={sectionWidth}
|
||||
{...emptyFolderProps}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
<EmptyContainer
|
||||
headerText={headerText}
|
||||
isEmptyPage={isEmptyPage}
|
||||
sectionWidth={sectionWidth}
|
||||
{...emptyFolderProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -362,6 +354,7 @@ export default inject(
|
||||
setAlreadyFetchingRooms,
|
||||
isEmptyPage,
|
||||
setIsEmptyPage,
|
||||
setIsLoadedEmptyPage,
|
||||
} = filesStore;
|
||||
const { title, rootFolderType } = selectedFolderStore;
|
||||
const { isPrivacyFolder, myFolderId } = treeFoldersStore;
|
||||
@ -387,6 +380,7 @@ export default inject(
|
||||
setAlreadyFetchingRooms,
|
||||
isEmptyPage,
|
||||
setIsEmptyPage,
|
||||
setIsLoadedEmptyPage,
|
||||
};
|
||||
}
|
||||
)(withTranslation(["Files"])(observer(RootFolderContainer)));
|
||||
|
@ -169,10 +169,23 @@ const SectionFilterContent = ({
|
||||
searchTitleOpenLocation,
|
||||
isLoadedLocationFiles,
|
||||
setIsLoadedSearchFiles,
|
||||
isLoadedEmptyPage,
|
||||
isEmptyPage,
|
||||
clearSearch,
|
||||
setClearSearch,
|
||||
}) => {
|
||||
const [selectedFilterValues, setSelectedFilterValues] = React.useState(null);
|
||||
const [isLoadedFilter, setIsLoadedFilter] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isEmptyPage) {
|
||||
setIsLoadedFilter(isLoadedEmptyPage);
|
||||
}
|
||||
|
||||
if (!isEmptyPage && !isLoadedEmptyPage) {
|
||||
setIsLoadedFilter(true);
|
||||
}
|
||||
}, [isLoadedEmptyPage, isEmptyPage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!(searchTitleOpenLocation && isLoadedLocationFiles)) return;
|
||||
@ -1300,6 +1313,10 @@ const SectionFilterContent = ({
|
||||
}
|
||||
};
|
||||
|
||||
if (!isLoadedFilter) {
|
||||
return <Loaders.Filter />;
|
||||
}
|
||||
|
||||
return (
|
||||
<FilterInput
|
||||
t={t}
|
||||
@ -1353,6 +1370,8 @@ export default inject(
|
||||
thirdPartyStore,
|
||||
clearSearch,
|
||||
setClearSearch,
|
||||
isLoadedEmptyPage,
|
||||
isEmptyPage,
|
||||
} = filesStore;
|
||||
|
||||
const { providers } = thirdPartyStore;
|
||||
@ -1411,6 +1430,9 @@ export default inject(
|
||||
isLoadedLocationFiles,
|
||||
setIsLoadedSearchFiles,
|
||||
|
||||
isLoadedEmptyPage,
|
||||
isEmptyPage,
|
||||
|
||||
clearSearch,
|
||||
setClearSearch,
|
||||
};
|
||||
|
@ -492,6 +492,7 @@ class PureHome extends React.Component {
|
||||
frameConfig,
|
||||
withPaging,
|
||||
isEmptyPage,
|
||||
isLoadedEmptyPage,
|
||||
} = this.props;
|
||||
|
||||
if (window.parent && !frameConfig) {
|
||||
@ -538,7 +539,7 @@ class PureHome extends React.Component {
|
||||
</Section.SectionHeader>
|
||||
)}
|
||||
|
||||
{!isEmptyPage && !isErrorRoomNotAvailable && (
|
||||
{!isLoadedEmptyPage && !isErrorRoomNotAvailable && (
|
||||
<Section.SectionFilter>
|
||||
{isFrame ? (
|
||||
showFilter && <SectionFilterContent />
|
||||
@ -548,6 +549,12 @@ class PureHome extends React.Component {
|
||||
</Section.SectionFilter>
|
||||
)}
|
||||
|
||||
{isLoadedEmptyPage && (
|
||||
<Section.SectionFilter>
|
||||
<div style={{ height: "32px" }} />
|
||||
</Section.SectionFilter>
|
||||
)}
|
||||
|
||||
<Section.SectionBody>
|
||||
<Consumer>
|
||||
{(context) => (
|
||||
@ -622,7 +629,7 @@ export default inject(
|
||||
refreshFiles,
|
||||
setViewAs,
|
||||
isEmptyPage,
|
||||
|
||||
isLoadedEmptyPage,
|
||||
disableDrag,
|
||||
isErrorRoomNotAvailable,
|
||||
} = filesStore;
|
||||
@ -773,6 +780,7 @@ export default inject(
|
||||
setViewAs,
|
||||
withPaging,
|
||||
isEmptyPage,
|
||||
isLoadedEmptyPage,
|
||||
};
|
||||
}
|
||||
)(withRouter(observer(Home)));
|
||||
|
@ -113,6 +113,8 @@ class FilesStore {
|
||||
|
||||
clearSearch = false;
|
||||
|
||||
isLoadedEmptyPage = false;
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
selectedFolderStore,
|
||||
@ -135,7 +137,6 @@ class FilesStore {
|
||||
|
||||
this.roomsController = new AbortController();
|
||||
this.filesController = new AbortController();
|
||||
|
||||
const { socketHelper, withPaging } = authStore.settingsStore;
|
||||
|
||||
socketHelper.on("s:modify-folder", async (opt) => {
|
||||
@ -438,6 +439,10 @@ class FilesStore {
|
||||
this.isEmptyPage = isEmptyPage;
|
||||
};
|
||||
|
||||
setIsLoadedEmptyPage = (isLoadedEmptyPage) => {
|
||||
this.isLoadedEmptyPage = isLoadedEmptyPage;
|
||||
};
|
||||
|
||||
get tooltipOptions() {
|
||||
if (!this.dragging) return null;
|
||||
|
||||
@ -2958,22 +2963,26 @@ class FilesStore {
|
||||
(f) => f.id === item.id && f.isFolder === item.isFolder
|
||||
);
|
||||
if (fileIndex === -1) {
|
||||
this.setSelection([item, ...this.selection]);
|
||||
this.setSelection([...this.selection, item]);
|
||||
} else {
|
||||
this.deselectFile(item);
|
||||
}
|
||||
};
|
||||
|
||||
withShiftSelect = (item) => {
|
||||
const caretStart = this.hotkeyCaretStart
|
||||
? this.hotkeyCaretStart
|
||||
: this.filesList[0];
|
||||
const caret = this.hotkeyCaret ? this.hotkeyCaret : caretStart;
|
||||
|
||||
if (!caret || !caretStart) return;
|
||||
|
||||
const startCaretIndex = this.filesList.findIndex(
|
||||
(f) =>
|
||||
f.id === this.hotkeyCaretStart.id &&
|
||||
f.isFolder === this.hotkeyCaretStart.isFolder
|
||||
(f) => f.id === caretStart.id && f.isFolder === caretStart.isFolder
|
||||
);
|
||||
|
||||
const caretIndex = this.filesList.findIndex(
|
||||
(f) =>
|
||||
f.id === this.hotkeyCaret.id && f.isFolder === this.hotkeyCaret.isFolder
|
||||
(f) => f.id === caret.id && f.isFolder === caret.isFolder
|
||||
);
|
||||
|
||||
const itemIndex = this.filesList.findIndex(
|
||||
@ -2994,6 +3003,11 @@ class FilesStore {
|
||||
);
|
||||
if (selectionIndex === -1) {
|
||||
newSelection.push(filesItem);
|
||||
} else {
|
||||
newSelection = newSelection.filter(
|
||||
(_, fIndex) => selectionIndex !== fIndex
|
||||
);
|
||||
newSelection.push(filesItem);
|
||||
}
|
||||
|
||||
if (isMoveDown) {
|
||||
@ -3003,13 +3017,18 @@ class FilesStore {
|
||||
}
|
||||
}
|
||||
|
||||
const lastSelection = this.selection[this.selection.length - 1];
|
||||
const indexOfLast = this.filesList.findIndex(
|
||||
(f) => f.id === lastSelection.id && f.isFolder === lastSelection.isFolder
|
||||
);
|
||||
|
||||
newSelection = newSelection.filter((f) => {
|
||||
const listIndex = this.filesList.findIndex(
|
||||
(x) => x.id === f.id && x.isFolder === f.isFolder
|
||||
);
|
||||
|
||||
if (isMoveDown) {
|
||||
const isSelect = itemIndex > startCaretIndex;
|
||||
const isSelect = listIndex < indexOfLast;
|
||||
if (isSelect) return true;
|
||||
|
||||
if (listIndex >= startCaretIndex) {
|
||||
@ -3018,7 +3037,7 @@ class FilesStore {
|
||||
return listIndex >= itemIndex;
|
||||
}
|
||||
} else {
|
||||
const isSelect = itemIndex < startCaretIndex;
|
||||
const isSelect = listIndex > indexOfLast;
|
||||
if (isSelect) return true;
|
||||
|
||||
if (listIndex <= startCaretIndex) {
|
||||
|
Loading…
Reference in New Issue
Block a user