Сервер хранилища конфигураций 1С — служба crserver.exe, через которую конфигураторы команды работают с общим хранилищем: в офисе по TCP (порт 1542), удалённо — по HTTP через Apache или IIS. Его ставят, когда разработчиков больше одного, а файловое хранилище в общей папке тормозит по VPN и требует раздавать права на каталог.
Основа статьи — заметка автора сайта (его команды и сохранённая им инструкция по Apache), сверенная со справкой платформы 8.3.27. Создание хранилища и работа с ним — в статье «Хранилище конфигурации 1С 8.3: создание и работа».
-d задаёт корневой каталог, каждое хранилище — его подкаталог: tcp://srv01/trade ведёт в D:\1C\Repo\trade. Другой порт пишут в адресе: tcp://srv01:1642/trade.Сервер хранилища есть и для Linux (64-разрядный — с 8.3.13), но дальше речь о Windows.
Отметьте в установщике платформы компонент сервера хранилища — в bin этой версии появится crserver.exe. Службу регистрирует сам crserver. Из практики автора — три команды в PowerShell от имени администратора (exe из текущего каталога PowerShell запускает только с .\):
# Из практики автора: PowerShell от имени администратора, 32-разрядная платформа 8.3.18
cd "C:\Program Files (x86)\1cv8\8.3.18.1208\bin"
.\crserver.exe -instsrvc -d D:\Repo\trade -usr .\usr1cv8 -pwd <пароль>
.\crserver.exe -start
Порт не указан, значит, 1542. -d — корень для всех хранилищ сервера, так что лучше нейтральный D:\1C\Repo, чем каталог с именем одной базы. Для нового сервера удобнее скрипт с проверками и правами на каталог:
@echo off
chcp 65001 >nul
rem install-crserver.cmd - служба сервера хранилища конфигураций 1С.
rem Запуск от имени администратора, файл сохранить в UTF-8 без BOM.
rem Пароль учетной записи службы задать заранее: set CR_PWD=...
setlocal
set "VER=8.3.27.1786"
set "CR=C:\Program Files\1cv8\%VER%\bin\crserver.exe"
set "ROOT=D:\1C\Repo"
set "SVCUSER=usr1cv8"
set "PORT=1542"
if not exist "%CR%" goto :no_cr
if not defined CR_PWD goto :no_pwd
rem Корень хранилищ: адрес tcp://srv01/trade - это каталог D:\1C\Repo\trade
if not exist "%ROOT%" mkdir "%ROOT%"
rem Учетной записи службы - полный доступ к корню хранилищ с наследованием на подкаталоги
icacls "%ROOT%" /grant "%SVCUSER%:(OI)(CI)F" || exit /b 1
rem Регистрация и запуск службы. Путь в -d - без обратной косой черты в конце
"%CR%" -instsrvc -port %PORT% -d "%ROOT%" -usr .\%SVCUSER% -pwd "%CR_PWD%"
"%CR%" -start
rem Порт открыт только для локальной сети, снаружи хранилище отдает веб-сервер
netsh advfirewall firewall add rule name="1C Repository %PORT%" dir=in action=allow protocol=TCP localport=%PORT% remoteip=localsubnet
rem Проверка: служба, ее строка запуска и прослушиваемый порт
powershell -NoProfile -Command "Get-CimInstance Win32_Service | Where-Object PathName -like '*crserver.exe*' | Format-List Name, State, StartName, PathName"
netstat -ano | findstr /c:":%PORT% "
exit /b 0
:no_cr
echo Нет %CR% - установите компонент "Сервер хранилища конфигураций 1С:Предприятия"
exit /b 1
:no_pwd
echo Задайте пароль учетной записи службы: set CR_PWD=...
exit /b 1
Учётная запись usr1cv8 обычно уже есть, если на машине стоит сервер 1С; иначе заведите локального пользователя с бессрочным паролем и правом «Вход в качестве службы». Хранилище создают в конфигураторе: «Конфигурация → Хранилище конфигурации → Создать хранилище», расположение — tcp://srv01/trade.
Схема из инструкции, которую сохранил автор: в каталоге публикации лежит XML-файл .1ccr (имя любое, расширение обязательно) с атрибутом connectString — tcp-адресом службы. Веб-сервер отдаёт запросы к файлу модулю 1С, модуль — службе.
<?xml version="1.0" encoding="UTF-8"?>
<repository connectString="tcp://srv01:1542"/>
Адрес для разработчика: http://srv01/repo/main.1ccr/trade, где /repo — публикация, main.1ccr — файл, trade — хранилище в корне crserver. По нему хранилище создают, подключают и администрируют, как по tcp. Через интернет — только https://; сертификат, как и в других HTTPS-соединениях платформы, проверяется по доверенным корневым сертификатам Windows — самоподписанный добавьте в доверенные на ПК разработчиков.
К инструкции две правки. В ней нет LoadModule: если на этом Apache не публиковали базы, обработчика 1cws-process просто нет. И .1ccr лежит прямо в каталоге хранилищ, куда смотрит Alias: стоит однажды убрать LoadModule (например, снимая публикации баз), и Apache начнёт отдавать файлы каталога как статику — вместе с 1cv8ddb.1CD. Держите .1ccr в отдельном пустом каталоге.
# httpd.conf, Apache 2.4. Модуль 1С - той же разрядности, что Apache; версию лучше взять ту же, что у crserver.
# Если LoadModule уже есть от публикации баз, вторую такую строку не добавляйте.
LoadModule _1cws_module "C:/Program Files/1cv8/8.3.27.1786/bin/wsap24.dll"
# Хранилище через HTTP. В каталоге D:/1C/www/repo только файл main.1ccr
AddHandler 1cws-process .1ccr
Alias "/repo" "D:/1C/www/repo/"
<Directory "D:/1C/www/repo/">
DirectorySlash Off
SetHandler 1cws-process
Require all granted
# Apache 2.2: вместо Require - Order allow,deny и Allow from all
# Пустить только офис и VPN: Require ip 192.168.1.0/24
</Directory>
Для Apache 2.2 нужен wsap22.dll, а вместо Require all granted — Order allow,deny и Allow from all (в инструкции — только Allow from all), но 2.2 давно не поддерживается. Разрядность модуля и Apache должна совпадать, после правки перезапустите Apache. Установка Apache для 1С — в статье «Публикация 1С на веб-сервере».
Про регистр, о котором предупреждает автор: Apache сравнивает адрес с Alias с учётом регистра, и /Repo/main.1ccr/trade при Alias "/repo" не сработает. Если crserver на Linux, регистр важен и в имени хранилища.
На IIS каталог с .1ccr делают приложением и отдают все его запросы wsisapi.dll — так платформа публикует и базы. Компоненты, пул и разрядность — как в статье «Публикация 1С на IIS».
:: Хранилище через IIS: каталог D:\1C\www\repo с файлом main.1ccr становится приложением /repo.
:: Командная строка от имени администратора. Пул 64-разрядный - как wsisapi.dll из Program Files.
set APPCMD=%windir%\system32\inetsrv\appcmd.exe
set DLL=C:\Program Files\1cv8\8.3.27.1786\bin\wsisapi.dll
set SITE=Default Web Site
:: 0. Резервная копия настроек IIS. Откат: appcmd restore backup before-1c-repo
%APPCMD% add backup before-1c-repo
:: 1. Приложение /repo
%APPCMD% add app /site.name:"%SITE%" /path:/repo /physicalPath:"D:\1C\www\repo"
:: 2. Все запросы приложения - модулю 1С (аналог SetHandler в Apache), запись в web.config каталога
%APPCMD% unlock config /section:handlers
%APPCMD% set config "%SITE%/repo" /section:handlers /+"[name='1C Configuration Repository',path='*',verb='*',modules='IsapiModule',scriptProcessor='%DLL%',resourceType='Unspecified',requireAccess='None']"
:: 3. Разрешить модуль в ограничениях ISAPI и CGI (если базы этой версии уже публиковались - он разрешен)
%APPCMD% set config /section:isapiCgiRestriction /+"[path='%DLL%',description='1C Web-service Extension',allowed='True']" /commit:apphost
:: 4. Запас для помещения крупных объектов: по умолчанию IIS ограничивает тело запроса примерно 28,6 МБ
%APPCMD% set config "%SITE%/repo" /section:requestFiltering /requestLimits.maxAllowedContentLength:1073741824
:: 5. Проверка
%APPCMD% list app "%SITE%/repo"
Раз версии обязаны совпадать, обновление — переезд всей команды за вечер:
crserver.exe.Службу можно переустановить (crserver -rmsrvc старой версией, -instsrvc новой с теми же -port и -d, снова с -usr и -pwd). Автор делает быстрее: правит номер версии в параметре ImagePath раздела службы (HKLM\SYSTEM\CurrentControlSet\Services\1C:Enterprise 8.3 Configuration Repository Server) и перезапускает её — учётная запись и пароль остаются. В regedit легко задеть ключи запуска, поэтому тот же приём скриптом: копия раздела в .reg, замена только пути к exe, проверка порта. Сохраните .ps1 в UTF-8 с BOM, иначе Windows PowerShell 5.1 прочитает кириллицу как ANSI.
# switch-crserver.ps1 - перевод службы сервера хранилища на другую версию платформы.
# Прием автора: правится ImagePath службы, учетная запись и пароль службы не меняются.
# Запуск: PowerShell от имени администратора, после резервной копии каталога хранилищ.
param(
[Parameter(Mandatory = $true)] [string] $Version, # например 8.3.27.1786
[string] $ServiceName = '', # пусто - найти службу по crserver.exe
[string] $PlatformRoot = 'C:\Program Files\1cv8'
)
$ErrorActionPreference = 'Stop'
$exe = Join-Path $PlatformRoot "$Version\bin\crserver.exe"
if (-not (Test-Path -LiteralPath $exe)) { throw "Не найден $exe - установите компонент сервера хранилища этой версии" }
if ($ServiceName -eq '') {
$found = @(Get-CimInstance Win32_Service | Where-Object { $_.PathName -like '*\crserver.exe*' })
if ($found.Count -ne 1) { throw "Найдено служб crserver: $($found.Count), укажите -ServiceName" }
$ServiceName = $found[0].Name
}
$key = "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName"
$old = (Get-ItemProperty -LiteralPath $key -Name ImagePath).ImagePath
# Меняется только путь к exe, ключи -srvc -port -d остаются прежними
$new = $old -replace '(?i)^\s*"?[^"]*\\crserver\.exe"?', ('"' + $exe + '"')
if ($new -eq $old) { throw "Служба уже запускает $exe" }
# Копия раздела службы. Откат: reg import <файл> и перезапуск службы
$reg = Join-Path $env:TEMP ('crserver-{0:yyyyMMdd-HHmmss}.reg' -f (Get-Date))
& reg.exe export ($key -replace '^HKLM:', 'HKLM') $reg /y | Out-Null
if ($LASTEXITCODE -ne 0) { throw 'Не удалось сохранить раздел службы в .reg' }
Write-Host "Было: $old"
Write-Host "Стало: $new"
Stop-Service -Name $ServiceName
Set-ItemProperty -LiteralPath $key -Name ImagePath -Value $new
try {
Start-Service -Name $ServiceName
}
catch {
# Новая версия не стартовала: возвращаем прежнюю строку запуска
Set-ItemProperty -LiteralPath $key -Name ImagePath -Value $old
Start-Service -Name $ServiceName
throw "Служба не запустилась с $exe, возвращена прежняя версия. $($_.Exception.Message)"
}
# Проверка: служба запущена и слушает свой порт
$port = if ($new -match '-port\s+(\d+)') { [int]$Matches[1] } else { 1542 }
Get-Service -Name $ServiceName | Format-Table Name, Status -AutoSize
Get-NetTCPConnection -State Listen -LocalPort $port -ErrorAction SilentlyContinue |
Format-Table LocalAddress, LocalPort, OwningProcess -AutoSize
Хранилища из 8.2 и первых релизов 8.3 могут работать в режиме совместимости: «Конфигурация → Хранилище конфигурации → Администрирование хранилища», закладка «Прочие», «Режим совместимости хранилища». В режиме «Версия 8.3.2 и ниже» сервер хранилища работает без оптимизации, «Не использовать» даёт новую структуру базы хранилища и быстрое помещение. Кнопка «Установить» запускает долгую конвертацию, после которой остальным придётся переподключиться. Кнопка «Оптимизировать» (пакетно — /ConfigurationRepositoryOptimizeData) доступна в режиме «Не использовать», когда платформа рекомендует оптимизацию.
С 8.3.3 хранилище без режима совместимости «Версия 8.3.2 и ниже» — не один файл: в 1cv8ddb.1CD сведения об изменениях, объекты — файлами в каталоге хранилища. Копируйте весь каталог, когда никто не помещает изменения, — проще всего ночью с остановленной службой: копия на ходу может оказаться несогласованной. Выгрузка cf (/ConfigurationRepositoryDumpCfg) — не бэкап: в ней одна версия без истории.
# backup-crserver.ps1 - ночная копия всех хранилищ сервера crserver.
# Планировщик: powershell.exe -NoProfile -ExecutionPolicy Bypass -File D:\Scripts\backup-crserver.ps1
param(
[string] $ServiceName = '', # пусто - найти службу по crserver.exe
[string] $Root = 'D:\1C\Repo', # каталог из ключа -d
[string] $Target = '\\nas01\backup\1c-repo', # куда складывать копии
[int] $KeepDays = 14
)
$ErrorActionPreference = 'Stop'
if ($ServiceName -eq '') {
$found = @(Get-CimInstance Win32_Service | Where-Object { $_.PathName -like '*\crserver.exe*' })
if ($found.Count -ne 1) { throw "Найдено служб crserver: $($found.Count), укажите -ServiceName" }
$ServiceName = $found[0].Name
}
$stamp = Get-Date -Format 'yyyy-MM-dd_HHmm'
$dest = Join-Path $Target $stamp
$log = Join-Path $Target "$stamp.log"
New-Item -ItemType Directory -Path $Target -Force | Out-Null
# Служба стоит, пока идет копия: 1cv8ddb.1CD и файлы объектов попадут в копию согласованными
$rc = 16 # если robocopy не запустится - считаем ошибкой
Stop-Service -Name $ServiceName
try {
robocopy $Root $dest /E /COPY:DAT /R:2 /W:5 /NP "/LOG:$log" | Out-Null
$rc = $LASTEXITCODE
}
finally {
Start-Service -Name $ServiceName # служба поднимается и после ошибки копирования
}
if ($rc -ge 8) { throw "robocopy вернул код $rc, подробности в $log" }
# Срок хранения: удаляются только папки с именем-датой этого скрипта и их журналы
$limit = (Get-Date).AddDays(-$KeepDays)
Get-ChildItem -LiteralPath $Target -Directory |
Where-Object { $_.Name -match '^\d{4}-\d{2}-\d{2}_\d{4}$' -and $_.CreationTime -lt $limit } |
Remove-Item -Recurse -Force
Get-ChildItem -LiteralPath $Target -File -Filter '*.log' |
Where-Object { $_.LastWriteTime -lt $limit } |
Remove-Item -Force
Write-Host "Копия готова: $dest"
Скрипт запускают планировщиком заданий от учётной записи, которая может останавливать службу и писать в папку копий. Восстановление обратное: остановить службу, вернуть каталог из копии, запустить. Базы, успевшие получить версии новее копии, переподключите к хранилищу. Копии проверяйте функцией из следующего раздела — она принимает и путь к каталогу копии.
Узнать о проблеме раньше разработчиков поможет служебная база. Функция запускает конфигуратор той же версии в пакетном режиме и строит отчёт по последней версии хранилища: построился — служба работает, адрес верный, вход проходит, версии совпадают; нет — сообщение из журнала превращается в подсказку. Место для кода — общий серверный модуль (регламентное задание) или внешняя обработка для толстого клиента. Нужен установленный конфигуратор; пользователю хранилища хватит права на просмотр.
// Проверка сервера хранилища из 1С: связь, вход пользователя, совпадение версий.
// Запускает конфигуратор этой же версии платформы в пакетном режиме и строит
// отчет по последней версии хранилища. Нужен установленный 1cv8.exe.
// Адрес: "tcp://srv01/trade", "http://srv01/repo/main.1ccr/trade" или каталог копии.
// Пароль на время проверки виден в командной строке процесса: заведите отдельного
// пользователя хранилища только с правом просмотра.
Функция ПроверитьХранилище(Адрес, Пользователь, Пароль) Экспорт
ИнформацияОСистеме = Новый СистемнаяИнформация;
Результат = Новый Структура;
Результат.Вставить("Успех", Ложь);
Результат.Вставить("ВерсияКонфигуратора", ИнформацияОСистеме.ВерсияПриложения);
Результат.Вставить("Диагноз", "");
Результат.Вставить("Журнал", "");
Результат.Вставить("Отчет", "");
Конфигуратор = КаталогПрограммы() + "1cv8.exe";
ФайлКонфигуратора = Новый Файл(Конфигуратор);
Если Не ФайлКонфигуратора.Существует() Тогда
Результат.Диагноз = "Нет " + Конфигуратор + ": нужен конфигуратор этой версии платформы.";
Возврат Результат;
КонецЕсли;
// Рабочий каталог: временная база, журнал, код результата и отчет
Каталог = ПолучитьИмяВременногоФайла("crcheck");
СоздатьКаталог(Каталог);
КаталогБазы = Каталог + "\ib";
ФайлЖурнала = Каталог + "\out.log";
ФайлРезультата = Каталог + "\result.txt";
ФайлОтчета = Каталог + "\report.txt";
// 1. Пустая файловая база: пакетному конфигуратору нужна информационная база
Команда = СтрШаблон("""%1"" CREATEINFOBASE File=""%2""; /DisableStartupDialogs /Out ""%3""",
Конфигуратор, КаталогБазы, ФайлЖурнала);
ЗапуститьПриложение(Команда, , Истина);
ФайлБазы = Новый Файл(КаталогБазы + "\1Cv8.1CD");
Если Не ФайлБазы.Существует() Тогда
Результат.Журнал = ТекстФайла(ФайлЖурнала);
Результат.Диагноз = "Не удалось создать временную базу, подробности в журнале.";
УдалитьКаталогПроверки(Каталог);
Возврат Результат;
КонецЕсли;
// 2. Отчет по последней версии хранилища (-NBegin -1) в текстовом формате
Команда = СтрШаблон("""%1"" DESIGNER /F ""%2"" /DisableStartupDialogs "
+ "/ConfigurationRepositoryF ""%3"" /ConfigurationRepositoryN ""%4"" /ConfigurationRepositoryP ""%5"" "
+ "/ConfigurationRepositoryReport ""%6"" -NBegin -1 -ReportFormat txt /Out ""%7"" /DumpResult ""%8""",
Конфигуратор, КаталогБазы, Адрес, Пользователь, Пароль, ФайлОтчета, ФайлЖурнала, ФайлРезультата);
ЗапуститьПриложение(Команда, , Истина);
Результат.Журнал = ТекстФайла(ФайлЖурнала);
Результат.Отчет = ТекстФайла(ФайлОтчета);
Результат.Успех = (СокрЛП(ТекстФайла(ФайлРезультата)) = "0");
Результат.Диагноз = ДиагнозПоЖурналу(Результат.Журнал, Результат.Успех);
УдалитьКаталогПроверки(Каталог);
Возврат Результат;
КонецФункции
// Чтение файла: без явной кодировки платформа определит ее по BOM, иначе возьмет UTF-8
Функция ТекстФайла(ИмяФайла)
Файл = Новый Файл(ИмяФайла);
Если Не Файл.Существует() Тогда
Возврат "";
КонецЕсли;
Текст = Новый ТекстовыйДокумент;
Текст.Прочитать(ИмяФайла);
Возврат Текст.ПолучитьТекст();
КонецФункции
// Сообщения конфигуратора из журнала - в подсказку администратору
Функция ДиагнозПоЖурналу(Журнал, Успех)
Если Успех Тогда
Возврат "Хранилище доступно: связь есть, вход выполнен, версии совпадают.";
ИначеЕсли СтрНайти(Журнал, "Несоответствие версий") > 0 Тогда
Возврат "Версии конфигуратора и сервера хранилища различаются: обновите клиент или службу crserver.";
ИначеЕсли СтрНайти(Журнал, "преобразован") > 0 Тогда
Возврат "Хранилище нужно преобразовать под новую версию платформы: сначала резервная копия.";
ИначеЕсли СтрНайти(Журнал, "Пользователь хранилища") > 0
Или СтрНайти(НРег(Журнал), "аутентифи") > 0 Тогда
Возврат "Неверное имя или пароль пользователя хранилища.";
ИначеЕсли СтрНайти(Журнал, "не обнаружено") > 0 Тогда
Возврат "По адресу нет хранилища: проверьте имя после адреса сервера и регистр букв.";
ИначеЕсли СтрНайти(Журнал, "Ошибка соединения") > 0
Или СтрНайти(Журнал, "удаленного хранилища") > 0 Тогда
Возврат "Нет связи с сервером хранилища: служба остановлена, порт закрыт или неверен адрес.";
КонецЕсли;
Возврат "Проверка не прошла, подробности в журнале конфигуратора.";
КонецФункции
Процедура УдалитьКаталогПроверки(Каталог)
Попытка
УдалитьФайлы(Каталог);
Исключение
// Файл базы мог остаться занятым: каталог во временных файлах можно удалить позже
ЗаписьЖурналаРегистрации("Проверка хранилища", УровеньЖурналаРегистрации.Предупреждение, , ,
ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
// Вызов из регламентного задания служебной базы, результат - в журнал регистрации.
// Пароль берите из защищенного хранилища настроек, а не строкой в коде.
Процедура ЗаписатьПроверкуХранилища(Адрес, Пользователь, Пароль) Экспорт
Результат = ПроверитьХранилище(Адрес, Пользователь, Пароль);
Уровень = ?(Результат.Успех, УровеньЖурналаРегистрации.Информация, УровеньЖурналаРегистрации.Ошибка);
ЗаписьЖурналаРегистрации("Проверка хранилища", Уровень, , ,
СтрШаблон("%1 Конфигуратор %2.%3%4", Результат.Диагноз, Результат.ВерсияКонфигуратора,
Символы.ПС, Результат.Журнал));
КонецПроцедуры
-instsrvc -port -d -usr -pwd — регистрация службы, .\usr1cv8 — локальная учётная запись (доменная — DOMAIN\user). Путь в -d не заканчивайте на \: в кавычках \" многие программы читают как экранированную кавычку.-srvc в ImagePath — режим службы; регулярное выражение в скрипте меняет только путь к exe, ключи остаются.connectString — куда модуль веб-сервера передаёт запросы. Имя хранилища — хвост адреса после .1ccr, поэтому одна публикация обслуживает все хранилища сервера.AddHandler отдаёт модулю 1С файлы .1ccr, SetHandler — все запросы каталога, DirectorySlash Off отключает перенаправление на адрес со слешем.-ge 8; finally запускает службу при любом исходе./DisableStartupDialogs превращает неверную авторизацию в хранилище в ошибку вместо диалога, -NBegin -1 — отчёт по последней версии, /DumpResult — 0 при успехе, журнал /Out — в UTF-8.Test-NetConnection srv01 -Port 1542.-d: опечатка, лишний уровень (D:\1C\Repo\trade\trade) или регистр на Linux./repo/main.1ccr/... — не сработал обработчик (нет LoadModule или сопоставления в IIS) или не совпал регистр. Если веб-сервер отвечает, а конфигуратор пишет об ошибке «сервера удаленного хранилища», проверьте доступ с веб-сервера к адресу из connectString.Да, отдельными службами со своим crserver.exe, портом и корневым каталогом. Вторую регистрируют через sc create под другим именем со строкой запуска по образцу первой: "C:\Program Files\1cv8\<версия>\bin\crserver.exe" -srvc -port 1642 -d D:\1C\Repo2. Адрес для этой версии — tcp://srv01:1642/trade. Похожая схема для сервера 1С — в статье «Несколько версий сервера 1С на одном компьютере».
По пути к exe: services.msc → свойства службы → «Исполняемый файл» или Get-CimInstance Win32_Service | Where-Object PathName -like '*crserver*' | Select-Object Name, PathName. Версию конфигуратора покажет «Справка → О программе» — номера должны совпасть вместе со сборкой.
Нет. Внешней команде — HTTPS через веб-сервер или VPN; правило брандмауэра в скрипте установки пускает на 1542 только локальную подсеть.
Когда все поместили изменения, скопируйте каталог хранилища в корень -d (например, D:\1C\Repo\trade), не меняя версию платформы. Разработчики отключают базы от старого пути и подключают к tcp://srv01/trade.
Поднять сервер хранилища для команды, опубликовать его по HTTPS, перевести на новую платформу без потери истории или наладить ночной бэкап можно поручить исполнителю: разместите задачу на площадке — оценка бесплатная, ответ в течение рабочего дня. Другие направления — в разделе услуг.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.