重构 monorepo 并完善网页端订阅与首页体验
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构 - 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐 - 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作 - 新增管理后台、支付与云端文件 API,更新 README 与项目规范 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
MYSQL_ROOT_PASSWORD=rootlocal
|
||||
MYSQL_DATABASE=soondesign
|
||||
MYSQL_USER=soondesign
|
||||
MYSQL_PASSWORD=soondesign
|
||||
WEB_PORT=8100
|
||||
ELECTRON_PORT=8101
|
||||
BACKEND_PORT=8102
|
||||
SOON_DEV_ADMIN_EMAIL=admin@local.test
|
||||
SOON_DEV_ADMIN_PASS=admin123
|
||||
@@ -0,0 +1 @@
|
||||
*.sh text eol=lf
|
||||
@@ -0,0 +1,67 @@
|
||||
name: soondesign
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: soondesign-mysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootlocal}
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-soondesign}
|
||||
MYSQL_USER: ${MYSQL_USER:-soondesign}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-soondesign}
|
||||
command:
|
||||
- --character-set-server=utf8mb4
|
||||
- --collation-server=utf8mb4_unicode_ci
|
||||
volumes:
|
||||
- soondesign_mysql_data:/var/lib/mysql
|
||||
- ../backend-web/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD:-rootlocal}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
|
||||
api:
|
||||
image: php:8.2-cli-bookworm
|
||||
container_name: soondesign-api
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${BACKEND_PORT:-8102}:8080"
|
||||
environment:
|
||||
SOON_DB_HOST: mysql
|
||||
SOON_DB_PORT: 3306
|
||||
SOON_DB_NAME: ${MYSQL_DATABASE:-soondesign}
|
||||
SOON_DB_USER: ${MYSQL_USER:-soondesign}
|
||||
SOON_DB_PASS: ${MYSQL_PASSWORD:-soondesign}
|
||||
SOON_DEV_ADMIN_EMAIL: ${SOON_DEV_ADMIN_EMAIL:-admin@local.test}
|
||||
SOON_DEV_ADMIN_PASS: ${SOON_DEV_ADMIN_PASS:-admin123}
|
||||
volumes:
|
||||
- ../backend-web:/var/www
|
||||
- ./config/local.php:/var/www/config/local.php:ro
|
||||
- ../soonModels:/var/soonModels
|
||||
- ./php:/docker/php:ro
|
||||
entrypoint: ["/bin/sh", "-c", "tr -d '\\r' < /docker/php/entrypoint-dev.sh | sh"]
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
|
||||
frontend:
|
||||
image: nginx:1.27-alpine
|
||||
container_name: soondesign-frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${WEB_PORT:-8100}:8080"
|
||||
- "${ELECTRON_PORT:-8101}:8081"
|
||||
volumes:
|
||||
- ../frontend-web:/var/www/web:ro
|
||||
- ../frontend-electron:/var/www/electron:ro
|
||||
- ./config/deploy-config.js:/var/www/web/assets/deploy-config.js:ro
|
||||
- ./nginx/frontend.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- api
|
||||
|
||||
volumes:
|
||||
soondesign_mysql_data:
|
||||
name: soondesign_mysql_data
|
||||
@@ -0,0 +1,29 @@
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
root /var/www/web;
|
||||
index pages/index.web.html;
|
||||
charset utf-8;
|
||||
absolute_redirect off;
|
||||
client_max_body_size 32m;
|
||||
location = / { return 302 /pages/index.web.html; }
|
||||
location = /pages { return 302 /pages/index.web.html; }
|
||||
location = /pages/ { return 302 /pages/index.web.html; }
|
||||
location ~* \.(js|css)$ {
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
try_files $uri =404;
|
||||
}
|
||||
location / { try_files $uri $uri/ =404; }
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
root /var/www/electron;
|
||||
index pages/index.html;
|
||||
charset utf-8;
|
||||
absolute_redirect off;
|
||||
client_max_body_size 32m;
|
||||
location = / { return 302 /pages/index.html; }
|
||||
location / { try_files $uri $uri/ =404; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if ! php -m 2>/dev/null | grep -q pdo_mysql; then
|
||||
echo "[api] installing pdo_mysql..."
|
||||
apt-get update -qq
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \
|
||||
libmariadb-dev-compat $PHPIZE_DEPS
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql
|
||||
apt-get purge -y --auto-remove $PHPIZE_DEPS
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
|
||||
host="${SOON_DB_HOST:-mysql}"
|
||||
port="${SOON_DB_PORT:-3306}"
|
||||
user="${SOON_DB_USER:-soondesign}"
|
||||
pass="${SOON_DB_PASS:-soondesign}"
|
||||
db="${SOON_DB_NAME:-soondesign}"
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 60 ]; do
|
||||
if php -r "
|
||||
try {
|
||||
new PDO(
|
||||
'mysql:host=${host};port=${port};dbname=${db};charset=utf8mb4',
|
||||
'${user}',
|
||||
'${pass}',
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
|
||||
);
|
||||
exit(0);
|
||||
} catch (Throwable \$e) {
|
||||
exit(1);
|
||||
}
|
||||
"; then
|
||||
break
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 2
|
||||
done
|
||||
|
||||
php /docker/php/migrate-dev-schema.php || true
|
||||
php /docker/php/seed-dev-admin.php || true
|
||||
|
||||
exec php -S 0.0.0.0:8080 -t /var/www/public /docker/php/router-dev.php
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
$host = getenv('SOON_DB_HOST') ?: 'mysql';
|
||||
$port = (int)(getenv('SOON_DB_PORT') ?: 3306);
|
||||
$user = getenv('SOON_DB_USER') ?: 'soondesign';
|
||||
$pass = getenv('SOON_DB_PASS') ?: 'soondesign';
|
||||
$db = getenv('SOON_DB_NAME') ?: 'soondesign';
|
||||
|
||||
try {
|
||||
$pdo = new PDO(
|
||||
sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', $host, $port, $db),
|
||||
$user,
|
||||
$pass,
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
|
||||
);
|
||||
} catch (Throwable $e) {
|
||||
fwrite(STDERR, 'migrate-dev-schema: ' . $e->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function columnExists(PDO $pdo, string $table, string $column): bool
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT COUNT(*) FROM information_schema.COLUMNS '
|
||||
. 'WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t AND COLUMN_NAME = :c'
|
||||
);
|
||||
$stmt->execute(['t' => $table, 'c' => $column]);
|
||||
return (int)$stmt->fetchColumn() > 0;
|
||||
}
|
||||
|
||||
$alters = [];
|
||||
if (!columnExists($pdo, 'subscriptions', 'source_order_id')) {
|
||||
$alters[] = 'ALTER TABLE subscriptions ADD COLUMN source_order_id BIGINT UNSIGNED DEFAULT NULL AFTER plan_id';
|
||||
$alters[] = 'ALTER TABLE subscriptions ADD KEY source_order (source_order_id)';
|
||||
}
|
||||
if (!columnExists($pdo, 'pay_orders', 'refund_status')) {
|
||||
$alters[] = "ALTER TABLE pay_orders ADD COLUMN refund_status ENUM('none','pending','approved','rejected') NOT NULL DEFAULT 'none' AFTER status";
|
||||
$alters[] = 'ALTER TABLE pay_orders ADD COLUMN refund_reason TEXT DEFAULT NULL AFTER refund_status';
|
||||
$alters[] = 'ALTER TABLE pay_orders ADD COLUMN refund_note TEXT DEFAULT NULL AFTER refund_reason';
|
||||
$alters[] = 'ALTER TABLE pay_orders ADD COLUMN cancelled_at DATETIME DEFAULT NULL AFTER paid_at';
|
||||
$alters[] = 'ALTER TABLE pay_orders ADD COLUMN refunded_at DATETIME DEFAULT NULL AFTER cancelled_at';
|
||||
$alters[] = 'ALTER TABLE pay_orders ADD KEY refund_status (refund_status)';
|
||||
}
|
||||
|
||||
if (!columnExists($pdo, 'plans', 'description')) {
|
||||
$alters[] = 'ALTER TABLE plans ADD COLUMN description VARCHAR(255) DEFAULT NULL AFTER name';
|
||||
$alters[] = 'ALTER TABLE plans ADD COLUMN max_files INT UNSIGNED NOT NULL DEFAULT 0 AFTER quota_mb';
|
||||
$alters[] = 'ALTER TABLE plans ADD COLUMN features TEXT DEFAULT NULL AFTER duration_days';
|
||||
$alters[] = 'ALTER TABLE plans ADD COLUMN sort_order INT NOT NULL DEFAULT 0 AFTER features';
|
||||
$alters[] = 'ALTER TABLE plans ADD COLUMN is_recommended TINYINT(1) NOT NULL DEFAULT 0 AFTER sort_order';
|
||||
}
|
||||
|
||||
if (!columnExists($pdo, 'users', 'admin_level')) {
|
||||
$alters[] = "ALTER TABLE users ADD COLUMN admin_level ENUM('full','ops') DEFAULT NULL AFTER role";
|
||||
}
|
||||
|
||||
foreach ($alters as $sql) {
|
||||
$pdo->exec($sql);
|
||||
fwrite(STDOUT, "migrate-dev-schema: {$sql}" . PHP_EOL);
|
||||
}
|
||||
|
||||
if (columnExists($pdo, 'users', 'admin_level')) {
|
||||
$pdo->exec(
|
||||
"UPDATE users SET admin_level = 'full' WHERE role = 'admin' AND (admin_level IS NULL OR admin_level = '')"
|
||||
);
|
||||
fwrite(STDOUT, 'migrate-dev-schema: admin_level backfill for existing admins' . PHP_EOL);
|
||||
}
|
||||
|
||||
if (columnExists($pdo, 'plans', 'description')) {
|
||||
$legacyCodes = ['pro_monthly' => 'member_monthly', 'pro_yearly' => 'member_yearly'];
|
||||
foreach ($legacyCodes as $oldCode => $newCode) {
|
||||
$stmt = $pdo->prepare('SELECT id FROM plans WHERE code = :c LIMIT 1');
|
||||
$stmt->execute(['c' => $oldCode]);
|
||||
if ($stmt->fetch()) {
|
||||
$conflict = $pdo->prepare('SELECT id FROM plans WHERE code = :c LIMIT 1');
|
||||
$conflict->execute(['c' => $newCode]);
|
||||
if ($conflict->fetch()) {
|
||||
$pdo->prepare('UPDATE plans SET is_active = 0 WHERE code = :c')->execute(['c' => $oldCode]);
|
||||
} else {
|
||||
$pdo->prepare('UPDATE plans SET code = :new WHERE code = :old')->execute(['new' => $newCode, 'old' => $oldCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$quota = 2048;
|
||||
$maxFiles = 200;
|
||||
$seed = [
|
||||
['free', '免费版', '免费体验设计与编辑', 0, $quota, $maxFiles, 0,
|
||||
'["设计与编辑工具免费使用"]', 0, 0, 1],
|
||||
['member_monthly', '月度订阅', '按月灵活使用,随时续订', 1999, $quota, $maxFiles, 30,
|
||||
'["高清预览","成品打印","云端保存","导出设计文件","订阅周期:1 个月"]', 10, 1, 1],
|
||||
['member_quarterly', '季度订阅', '连续三个月,更省心', 5299, $quota, $maxFiles, 90,
|
||||
'["高清预览","成品打印","云端保存","导出设计文件","订阅周期:1 季"]', 15, 0, 1],
|
||||
['member_yearly', '年度订阅', '全年畅享,性价比更高', 19999, $quota, $maxFiles, 365,
|
||||
'["高清预览","成品打印","云端保存","导出设计文件","订阅周期:1 年"]', 20, 0, 1],
|
||||
];
|
||||
$upsert = $pdo->prepare(
|
||||
'INSERT INTO plans (code, name, description, price_cents, quota_mb, max_files, duration_days, features, sort_order, is_recommended, is_active) '
|
||||
. 'VALUES (:c, :n, :d, :p, :q, :mf, :dd, :f, :so, :ir, :a) '
|
||||
. 'ON DUPLICATE KEY UPDATE name=VALUES(name), description=VALUES(description), price_cents=VALUES(price_cents), '
|
||||
. 'quota_mb=VALUES(quota_mb), max_files=VALUES(max_files), duration_days=VALUES(duration_days), '
|
||||
. 'sort_order=VALUES(sort_order), is_recommended=VALUES(is_recommended), is_active=VALUES(is_active)'
|
||||
);
|
||||
foreach ($seed as $row) {
|
||||
$upsert->execute([
|
||||
'c' => $row[0], 'n' => $row[1], 'd' => $row[2], 'p' => $row[3], 'q' => $row[4],
|
||||
'mf' => $row[5], 'dd' => $row[6], 'f' => $row[7], 'so' => $row[8], 'ir' => $row[9], 'a' => $row[10],
|
||||
]);
|
||||
}
|
||||
fwrite(STDOUT, 'migrate-dev-schema: membership plans catalog synced' . PHP_EOL);
|
||||
|
||||
$legacyFeatStmt = $pdo->query(
|
||||
'SELECT code, duration_days FROM plans WHERE features LIKE \'%预览成品效果%\' '
|
||||
. 'OR features LIKE \'%到期后恢复为非会员%\' OR features LIKE \'%会员权益一致%\' '
|
||||
. 'OR name LIKE \'%会员 ·%\''
|
||||
);
|
||||
$featUpd = $pdo->prepare('UPDATE plans SET features = :f WHERE code = :c');
|
||||
$coreFeatures = ['高清预览', '成品打印', '云端保存', '导出设计文件'];
|
||||
foreach ($legacyFeatStmt->fetchAll() as $legacyRow) {
|
||||
$code = (string)$legacyRow['code'];
|
||||
$days = (int)$legacyRow['duration_days'];
|
||||
if ($code === 'free') {
|
||||
$featUpd->execute([
|
||||
'f' => '["设计与编辑工具免费使用"]',
|
||||
'c' => $code,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
$lines = $coreFeatures;
|
||||
if ($days > 0) {
|
||||
$period = $days >= 365 ? '1 年' : ($days === 90 ? '1 季' : ($days >= 30 && $days % 30 === 0 ? ((int)($days / 30)) . ' 个月' : $days . ' 天'));
|
||||
$lines[] = '订阅周期:' . $period;
|
||||
}
|
||||
$featUpd->execute([
|
||||
'f' => json_encode($lines, JSON_UNESCAPED_UNICODE),
|
||||
'c' => $code,
|
||||
]);
|
||||
}
|
||||
fwrite(STDOUT, 'migrate-dev-schema: legacy plan copy refreshed' . PHP_EOL);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
$publicRoot = '/var/www/public';
|
||||
$uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
|
||||
|
||||
if ($uri === '/health') {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo 'ok';
|
||||
return true;
|
||||
}
|
||||
|
||||
$file = $publicRoot . $uri;
|
||||
if ($uri !== '/' && is_file($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preg_match('#^/api/admin(/.*)?$#', $uri)) {
|
||||
require $publicRoot . '/admin.php';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (preg_match('#^/api/v1(/.*)?$#', $uri)) {
|
||||
require $publicRoot . '/index.php';
|
||||
return true;
|
||||
}
|
||||
|
||||
http_response_code(404);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['ok' => false, 'error' => 'not_found', 'message' => 'Not found'], JSON_UNESCAPED_UNICODE);
|
||||
return true;
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
$host = getenv('SOON_DB_HOST') ?: 'mysql';
|
||||
$port = (int)(getenv('SOON_DB_PORT') ?: 3306);
|
||||
$user = getenv('SOON_DB_USER') ?: 'soondesign';
|
||||
$pass = getenv('SOON_DB_PASS') ?: 'soondesign';
|
||||
$db = getenv('SOON_DB_NAME') ?: 'soondesign';
|
||||
$email = getenv('SOON_DEV_ADMIN_EMAIL') ?: 'admin@local.test';
|
||||
$plain = getenv('SOON_DEV_ADMIN_PASS') ?: 'admin123';
|
||||
|
||||
try {
|
||||
$pdo = new PDO(
|
||||
sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', $host, $port, $db),
|
||||
$user,
|
||||
$pass,
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
|
||||
);
|
||||
} catch (Throwable $e) {
|
||||
fwrite(STDERR, 'seed-dev-admin: ' . $e->getMessage() . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id FROM users WHERE email = :email LIMIT 1');
|
||||
$stmt->execute(['email' => $email]);
|
||||
if ($stmt->fetchColumn()) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$hash = password_hash($plain, PASSWORD_BCRYPT);
|
||||
$ins = $pdo->prepare(
|
||||
'INSERT INTO users (email, password_hash, role, admin_level, status, created_at) '
|
||||
. 'VALUES (:email, :hash, :role, :level, :status, :created_at)'
|
||||
);
|
||||
$ins->execute([
|
||||
'email' => $email,
|
||||
'hash' => $hash,
|
||||
'role' => 'admin',
|
||||
'level' => 'full',
|
||||
'status' => 'active',
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
fwrite(STDOUT, "seed-dev-admin: created {$email}" . PHP_EOL);
|
||||
@@ -0,0 +1,70 @@
|
||||
param([string]$EnvFile = "$PSScriptRoot\.env", [string]$Root = (Split-Path $PSScriptRoot -Parent))
|
||||
|
||||
if (-not (Test-Path $EnvFile)) { Copy-Item "$PSScriptRoot\.env.example" $EnvFile }
|
||||
|
||||
$ports = @{ WEB_PORT = '8100'; ELECTRON_PORT = '8101'; BACKEND_PORT = '8102' }
|
||||
Get-Content $EnvFile -Encoding UTF8 | ForEach-Object {
|
||||
if ($_ -match '^\s*(WEB_PORT|ELECTRON_PORT|BACKEND_PORT)\s*=\s*(\d+)') { $ports[$matches[1]] = $matches[2] }
|
||||
}
|
||||
$w, $e, $b = $ports.WEB_PORT, $ports.ELECTRON_PORT, $ports.BACKEND_PORT
|
||||
|
||||
$deployJs = @"
|
||||
window.SOON_DEPLOY_CONFIG = {
|
||||
api_v1_base: 'http://localhost:$b/api/v1',
|
||||
api_admin_base: 'http://localhost:$b/api/admin',
|
||||
front_base_url: 'http://localhost:$w',
|
||||
web_base: 'http://localhost:$w',
|
||||
version: 'local-dev'
|
||||
};
|
||||
"@
|
||||
|
||||
$localPhp = @"
|
||||
<?php
|
||||
return [
|
||||
'app' => [
|
||||
'env' => 'development',
|
||||
'debug' => true,
|
||||
'timezone' => 'Asia/Shanghai',
|
||||
'jwt_secret' => 'local-dev-jwt-secret-minimum-32-chars',
|
||||
'jwt_ttl' => 3600,
|
||||
'jwt_refresh_ttl' => 2592000,
|
||||
'cors_origins' => [
|
||||
'http://localhost:$w', 'http://127.0.0.1:$w',
|
||||
'http://localhost:$e', 'http://127.0.0.1:$e',
|
||||
],
|
||||
],
|
||||
'db' => [
|
||||
'host' => 'mysql', 'port' => 3306, 'name' => 'soondesign',
|
||||
'user' => 'soondesign', 'pass' => 'soondesign', 'charset' => 'utf8mb4',
|
||||
],
|
||||
'site' => [
|
||||
'base_url' => 'http://localhost:$b',
|
||||
'front_base_url' => 'http://localhost:$w',
|
||||
],
|
||||
'storage' => [
|
||||
'users_dir' => '/var/www/storage/users',
|
||||
'models_dir' => '/var/soonModels',
|
||||
],
|
||||
'limits' => ['free_quota_mb' => 20, 'preview_require_membership' => false],
|
||||
'rate_limits' => [
|
||||
'admin/*' => ['capacity' => 120, 'window' => 60],
|
||||
'POST /api/v1/auth/login' => ['capacity' => 10, 'window' => 60],
|
||||
'POST /api/v1/auth/register' => ['capacity' => 5, 'window' => 60],
|
||||
'POST /api/v1/files' => ['capacity' => 30, 'window' => 60],
|
||||
],
|
||||
'alipay' => ['app_id' => '', 'private_key' => '', 'public_key' => '', 'sandbox' => true],
|
||||
'wechat' => [
|
||||
'app_id' => '', 'mch_id' => '', 'mch_serial_no' => '',
|
||||
'mch_private_key' => '', 'api_v3_key' => '', 'sandbox' => true,
|
||||
],
|
||||
];
|
||||
"@
|
||||
|
||||
$dir = Join-Path $PSScriptRoot 'config'
|
||||
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
||||
$enc = [Text.UTF8Encoding]::new($false)
|
||||
[IO.File]::WriteAllText((Join-Path $dir 'deploy-config.js'), $deployJs.Trim() + "`n", $enc)
|
||||
[IO.File]::WriteAllText((Join-Path $dir 'local.php'), $localPhp.Trim() + "`n", $enc)
|
||||
|
||||
$webDeploy = Join-Path $Root 'frontend-web\assets\deploy-config.js'
|
||||
[IO.File]::WriteAllText($webDeploy, $deployJs.Trim() + "`n", $enc)
|
||||
Reference in New Issue
Block a user