作者 liyuhang

Merge branch 'dev' of http://47.244.231.31:8099/zhl/globalso-v6 into dev

@@ -15,4 +15,5 @@ yarn-error.log @@ -15,4 +15,5 @@ yarn-error.log
15 /.idea 15 /.idea
16 /.vscode 16 /.vscode
17 composer.lock 17 composer.lock
  18 +app/Console/Commands/Test/Demo.php
18 /public/upload 19 /public/upload
  1 +<?php
  2 +
  3 +namespace App\Console\Commands;
  4 +
  5 +
  6 +
  7 +
  8 +use App\Helper\Arr;
  9 +use Illuminate\Console\Command;
  10 +use Illuminate\Support\Facades\Artisan;
  11 +use Illuminate\Support\Facades\DB;
  12 +use Symfony\Component\Process\Process;
  13 +
  14 +class DiffDb extends Command
  15 +{
  16 + protected $signature = 'project:diff_db';
  17 +
  18 + /**
  19 + * The console command description.
  20 + *
  21 + * @var string
  22 + */
  23 + protected $description = '对比数据库结构';
  24 +
  25 + /**
  26 + * Create a new command instance.
  27 + *
  28 + * @return void
  29 + */
  30 + public function __construct()
  31 + {
  32 + parent::__construct();
  33 + }
  34 +
  35 + public function handle()
  36 + {
  37 + //C端
  38 +// $process = new Process(['git', 'pull']);
  39 +// $process->run();
  40 +// dump($process->getExitCodeText());
  41 +// dump($process->getExitCode());
  42 +// dump($process->getErrorOutput());
  43 +// $output = explode(PHP_EOL, $process->getOutput());
  44 +// dump($output);
  45 +// exit;
  46 + $custom_mysql_config = [
  47 + 'database.connections.custom_mysql.host' => '43.154.15.250',
  48 + 'database.connections.custom_mysql.port' => '6063',
  49 + 'database.connections.custom_mysql.database' => 'globalso_v6',
  50 + 'database.connections.custom_mysql.username' => 'globalso_v6',
  51 + 'database.connections.custom_mysql.password' => 'PFxFdzj4ha6w7T3j',
  52 + ];
  53 + config($custom_mysql_config);
  54 +
  55 + $this->output->writeln("开始进行数据表对比!");
  56 + $tablesSource = DB::select("show tables");
  57 + $tablesRemote = DB::connection('custom_mysql')->select("show tables");
  58 + $tablesSource = array_map(function($item){
  59 + return Arr::first($item);
  60 + }, $tablesSource);
  61 + $tablesRemote = array_map(function($item){
  62 + return Arr::first($item);
  63 + }, $tablesRemote);
  64 +
  65 +
  66 + $tablesNotInRemote = [];
  67 + $tablesInRemote = [];
  68 +
  69 + foreach ($tablesSource as $t) {
  70 + if (!in_array($t, $tablesRemote)) {
  71 + $tablesNotInRemote[] = $t;
  72 + } else {
  73 + $tablesInRemote[] = $t;
  74 + }
  75 + }
  76 +
  77 + //print reports
  78 + echo "本地存在,但是不在custom_mysql中的表:" . PHP_EOL;
  79 + echo ">>>>>>==================================<<<<<<" . PHP_EOL;
  80 + foreach ($tablesNotInRemote as $t) {
  81 + echo $t . PHP_EOL;
  82 + }
  83 + echo ">>>>>>==================================<<<<<<" . PHP_EOL . PHP_EOL . PHP_EOL;
  84 +
  85 +
  86 + echo "本地与custom_mysql结构不一致的表:" . PHP_EOL;
  87 + echo ">>>>>>==================================<<<<<<" . PHP_EOL;
  88 +
  89 + $only127 = $onlyRemote = $modify127 = [];
  90 + foreach ($tablesInRemote as $t) {
  91 + $columns127 = DB::select("show columns from `{$t}`");
  92 + foreach ($columns127 as &$item){
  93 + $item = get_object_vars($item);
  94 + }
  95 + $columnsRemote = DB::connection('custom_mysql')->select("show columns from `{$t}`");
  96 + foreach ($columnsRemote as &$item){
  97 + $item = get_object_vars($item);
  98 + }
  99 +
  100 + $fields127 = $fieldsRemote = [];
  101 + foreach ($columns127 as $v) {
  102 + $fields127[$v['Field']] = $v;
  103 + }
  104 + foreach ($columnsRemote as $v) {
  105 + $fieldsRemote[$v['Field']] = $v;
  106 + }
  107 +
  108 + foreach ($fields127 as $f => $column) {
  109 + if (!isset($fieldsRemote[$f])) {
  110 + $only127[$t][] = $f;
  111 + } else if ($column !== $fieldsRemote[$f]) {
  112 + dump($column);
  113 + dump($fieldsRemote[$f]);
  114 + $modify127[$t][] = $f;
  115 + }
  116 + }
  117 +
  118 + foreach ($fieldsRemote as $f => $column) {
  119 + if (!isset($fields127[$f])) {
  120 + $onlyRemote[$t][] = $f;
  121 + }
  122 + }
  123 + }
  124 +
  125 + if (!empty($only127)) {
  126 + echo "只本地存在:" . PHP_EOL;
  127 + foreach ($only127 as $t => $columns) {
  128 + echo $t . ":";
  129 + foreach ($columns as $field) {
  130 + echo $field . "\t";
  131 + }
  132 + echo PHP_EOL;
  133 + }
  134 + }
  135 + if (!empty($onlyRemote)) {
  136 + echo "只custom_mysql存在:" . PHP_EOL;
  137 + foreach ($onlyRemote as $t => $columns) {
  138 + echo $t . ":";
  139 + foreach ($columns as $field) {
  140 + echo $field . "\t";
  141 + }
  142 + echo PHP_EOL;
  143 + }
  144 + }
  145 + if (!empty($modify127)) {
  146 + echo "本地更新:" . PHP_EOL;
  147 + foreach ($modify127 as $t => $columns) {
  148 + echo $t . ":";
  149 + foreach ($columns as $field) {
  150 + echo $field . "\t";
  151 + }
  152 + echo PHP_EOL;
  153 + }
  154 + }
  155 + echo ">>>>>>==================================<<<<<<" . PHP_EOL . PHP_EOL . PHP_EOL;
  156 + }
  157 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2023/4/19
  6 + * Time: 15:04
  7 + */
  8 +namespace App\Console\Commands;
  9 +
  10 +use App\Models\Project;
  11 +use App\Services\ProjectServer;
  12 +use Illuminate\Console\Command;
  13 +use Illuminate\Support\Facades\DB;
  14 +
  15 +/**
  16 + * Class ProjectDatabaseUpdate
  17 + * @package App\Console\Commands
  18 + */
  19 +class ProjectDatabaseUpdate extends Command
  20 +{
  21 + /**
  22 + * The name and signature of the console command.
  23 + *
  24 + * @var string
  25 + */
  26 + protected $signature = 'project:database_update';
  27 +
  28 + /**
  29 + * The console command description.
  30 + *
  31 + * @var string
  32 + */
  33 + protected $description = '项目数据库更新';
  34 +
  35 + /**
  36 + * Create a new command instance.
  37 + *
  38 + * @return void
  39 + */
  40 + public function __construct()
  41 + {
  42 + parent::__construct();
  43 + }
  44 +
  45 + /**
  46 + * @return bool
  47 + */
  48 + public function handle()
  49 + {
  50 + #TODO 未处理更新 更新对象(base, project) 更新内容
  51 + return true;
  52 + }
  53 +
  54 + /**
  55 + * 主库内容更新
  56 + * @param $sql
  57 + * @return array
  58 + */
  59 + public function baseUpdate($sql)
  60 + {
  61 + return DB::select($sql);
  62 + }
  63 +
  64 + /**
  65 + * 客户数据表更新
  66 + * @param $sql
  67 + * @return bool
  68 + */
  69 + public function projectDatabaseUpdate($sql)
  70 + {
  71 + $project = Project::get();
  72 + foreach ($project as $val) {
  73 + ProjectServer::useProject($val->id);
  74 + DB::connection('custom_mysql')->select($sql);
  75 + }
  76 + return true;
  77 + }
  78 +
  79 + /**
  80 + * 指定项目数据库更新
  81 + * @param $project_id
  82 + * @param $sql
  83 + */
  84 + public function appointProjectUpdate($project_id, $sql)
  85 + {
  86 + ProjectServer::useProject($project_id);
  87 + DB::connection('custom_mysql')->select($sql);
  88 + }
  89 +}
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 namespace App\Console\Commands; 8 namespace App\Console\Commands;
9 9
10 use App\Models\Project; 10 use App\Models\Project;
  11 +use App\Services\ProjectServer;
11 use Illuminate\Console\Command; 12 use Illuminate\Console\Command;
12 use Illuminate\Support\Facades\DB; 13 use Illuminate\Support\Facades\DB;
13 use Illuminate\Support\Facades\Schema; 14 use Illuminate\Support\Facades\Schema;
@@ -71,11 +72,7 @@ class ProjectInit extends Command @@ -71,11 +72,7 @@ class ProjectInit extends Command
71 // 创建数据库失败 添加通知以及再次处理 72 // 创建数据库失败 添加通知以及再次处理
72 } 73 }
73 // 设置 database.connections.custom_mysql 数据 74 // 设置 database.connections.custom_mysql 数据
74 - config(['database.connections.custom_mysql.host' => $project->mysqlConfig()->host]);  
75 - config(['database.connections.custom_mysql.port' => $project->mysqlConfig()->port]);  
76 - config(['database.connections.custom_mysql.database' => $project->databaseName()]);  
77 - config(['database.connections.custom_mysql.username' => $project->mysqlConfig()->user]);  
78 - config(['database.connections.custom_mysql.password' => $project->mysqlConfig()->password]); 75 + ProjectServer::useProject($project->id);
79 76
80 // TODO 创建对应库 初始化数据表 77 // TODO 创建对应库 初始化数据表
81 $this->initTable(); 78 $this->initTable();
@@ -87,12 +84,6 @@ class ProjectInit extends Command @@ -87,12 +84,6 @@ class ProjectInit extends Command
87 */ 84 */
88 public function initTable() 85 public function initTable()
89 { 86 {
90 -// $table = DB::select('show tables');  
91 -// $table = array_column($table, 'Tables_in_globalso_dev');  
92 -// $table = DB::connection('custom_tmp_mysql')->select('show tables');  
93 -// $table_in = DB::connection('custom_tmp_mysql')->getDatabaseName();  
94 -// dd($table, $table_in);  
95 -  
96 $database_name = DB::connection('custom_tmp_mysql')->getDatabaseName(); 87 $database_name = DB::connection('custom_tmp_mysql')->getDatabaseName();
97 88
98 $table = Schema::connection('custom_tmp_mysql')->getAllTables(); 89 $table = Schema::connection('custom_tmp_mysql')->getAllTables();
@@ -31,6 +31,9 @@ class LoginController extends BaseController @@ -31,6 +31,9 @@ class LoginController extends BaseController
31 31
32 return $this->success(); 32 return $this->success();
33 } 33 }
  34 + if($this->manage()){
  35 + return redirect(route('admin.home.white'));
  36 + }
34 return view('admin.login'); 37 return view('admin.login');
35 } 38 }
36 39
@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Aside; @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Aside;
5 use App\Enums\Common\Code; 5 use App\Enums\Common\Code;
6 use App\Http\Controllers\Bside\BaseController; 6 use App\Http\Controllers\Bside\BaseController;
7 7
  8 +use App\Http\Logic\Aside\ServerConfigLogic;
  9 +use App\Http\Requests\Aside\ServerConfigRequest;
8 use App\Models\Project as ProjectModel; 10 use App\Models\Project as ProjectModel;
9 11
10 /** 12 /**
@@ -33,4 +35,16 @@ class ProjectController extends BaseController @@ -33,4 +35,16 @@ class ProjectController extends BaseController
33 public function add(){ 35 public function add(){
34 $projectModel = new ProjectModel(); 36 $projectModel = new ProjectModel();
35 } 37 }
  38 +
  39 + /**
  40 + * 保存配置
  41 + * @param ServerConfigRequest $request
  42 + * @param ServerConfigLogic $logic
  43 + * @author zbj
  44 + * @date 2023/4/23
  45 + */
  46 + public function saveServerConfig(ServerConfigRequest $request, ServerConfigLogic $logic){
  47 + $data = $logic->save($this->param);
  48 + return $this->success($data);
  49 + }
36 } 50 }
@@ -47,9 +47,6 @@ class ProductController extends BaseController @@ -47,9 +47,6 @@ class ProductController extends BaseController
47 47
48 public function save(ProductRequest $request, ProductLogic $logic) 48 public function save(ProductRequest $request, ProductLogic $logic)
49 { 49 {
50 - //封面取第一个图片  
51 - $this->param['thumb'] = $this->param['gallery'][0] ?? '';  
52 -  
53 $data = $logic->save($this->param); 50 $data = $logic->save($this->param);
54 return $this->success($data); 51 return $this->success($data);
55 } 52 }
  1 +<?php
  2 +
  3 +namespace App\Http\Logic\Aside;
  4 +
  5 +use App\Models\Product\Product;
  6 +use App\Models\ServerConfig;
  7 +use Illuminate\Support\Facades\DB;
  8 +
  9 +/**
  10 + * Class ServerConfigLogic
  11 + * @package App\Http\Logic\Aside
  12 + * @author zbj
  13 + * @date 2023/4/23
  14 + */
  15 +class ServerConfigLogic extends BaseLogic
  16 +{
  17 + public function __construct()
  18 + {
  19 + parent::__construct();
  20 +
  21 + $this->model = new ServerConfig();
  22 + }
  23 +
  24 +
  25 + public function save($param)
  26 + {
  27 + DB::beginTransaction();
  28 + try {
  29 + $res = parent::save($param);
  30 +
  31 + $data = ['sql_id' => $res['id']];
  32 + if ($param['type'] == ServerConfig::TYPE_SERVER) {
  33 + $data = ['serve_id' => $res['id']];
  34 + }
  35 + ProjectLogic::save($data);
  36 +
  37 + DB::commit();
  38 + } catch (\Exception $e) {
  39 + DB::rollBack();
  40 +
  41 + dump($e->getMessage());
  42 + $this->fail('保存失败');
  43 + }
  44 + }
  45 +}
@@ -5,6 +5,7 @@ namespace App\Http\Logic\Bside\Product; @@ -5,6 +5,7 @@ namespace App\Http\Logic\Bside\Product;
5 use App\Helper\Arr; 5 use App\Helper\Arr;
6 use App\Http\Logic\Bside\BaseLogic; 6 use App\Http\Logic\Bside\BaseLogic;
7 use App\Models\Product\Category; 7 use App\Models\Product\Category;
  8 +use App\Models\Product\CategoryRelated;
8 use App\Models\Product\Product; 9 use App\Models\Product\Product;
9 10
10 /** 11 /**
@@ -47,7 +48,7 @@ class CategoryLogic extends BaseLogic @@ -47,7 +48,7 @@ class CategoryLogic extends BaseLogic
47 $this->fail("分类{$info['title']}存在子分类,不能删除"); 48 $this->fail("分类{$info['title']}存在子分类,不能删除");
48 } 49 }
49 //是否有对应商品 50 //是否有对应商品
50 - if(Product::whereRaw("FIND_IN_SET({$id},`category_id`)")->count()){ 51 + if(CategoryRelated::where('cate_id', $id)->count()){
51 $this->fail("分类{$info['title']}存在产品,不能删除"); 52 $this->fail("分类{$info['title']}存在产品,不能删除");
52 } 53 }
53 } 54 }
@@ -4,6 +4,7 @@ namespace App\Http\Logic\Bside\Product; @@ -4,6 +4,7 @@ namespace App\Http\Logic\Bside\Product;
4 4
5 use App\Helper\Arr; 5 use App\Helper\Arr;
6 use App\Http\Logic\Bside\BaseLogic; 6 use App\Http\Logic\Bside\BaseLogic;
  7 +use App\Models\Product\CategoryRelated;
7 use App\Models\Product\Product; 8 use App\Models\Product\Product;
8 use App\Models\RouteMap; 9 use App\Models\RouteMap;
9 use Illuminate\Support\Facades\DB; 10 use Illuminate\Support\Facades\DB;
@@ -24,11 +25,15 @@ class ProductLogic extends BaseLogic @@ -24,11 +25,15 @@ class ProductLogic extends BaseLogic
24 } 25 }
25 26
26 public function save($param){ 27 public function save($param){
  28 + //封面取第一个图片
  29 + $param['thumb'] = $param['gallery'][0] ?? '';
27 DB::beginTransaction(); 30 DB::beginTransaction();
28 try { 31 try {
29 $data = $param; 32 $data = $param;
30 unset($data['route']); 33 unset($data['route']);
31 $res = parent::save($data); 34 $res = parent::save($data);
  35 + //关联分类
  36 + CategoryRelated::saveRelated($res['id'], $data['category_id']);
32 //路由映射 37 //路由映射
33 RouteMap::setRoute($param['route'], RouteMap::SOURCE_PRODUCT, $res['id'], $this->user['project_id']); 38 RouteMap::setRoute($param['route'], RouteMap::SOURCE_PRODUCT, $res['id'], $this->user['project_id']);
34 DB::commit(); 39 DB::commit();
@@ -49,6 +54,9 @@ class ProductLogic extends BaseLogic @@ -49,6 +54,9 @@ class ProductLogic extends BaseLogic
49 foreach ($ids as $id){ 54 foreach ($ids as $id){
50 //删除路由映射 55 //删除路由映射
51 RouteMap::delRoute(RouteMap::SOURCE_PRODUCT, $id, $this->user['project_id']); 56 RouteMap::delRoute(RouteMap::SOURCE_PRODUCT, $id, $this->user['project_id']);
  57 +
  58 + //删除分类关联
  59 + CategoryRelated::where('product_id', $id)->delete();
52 } 60 }
53 61
54 DB::commit(); 62 DB::commit();
@@ -8,6 +8,7 @@ use App\Exceptions\AsideGlobalException; @@ -8,6 +8,7 @@ use App\Exceptions\AsideGlobalException;
8 use App\Exceptions\BsideGlobalException; 8 use App\Exceptions\BsideGlobalException;
9 use App\Helper\Arr; 9 use App\Helper\Arr;
10 use Illuminate\Support\Facades\Cache; 10 use Illuminate\Support\Facades\Cache;
  11 +use Illuminate\Support\Facades\Schema;
11 12
12 /** 13 /**
13 * @notes: 逻辑层基类 控制器调用 统一返回 统一抛出异常 14 * @notes: 逻辑层基类 控制器调用 统一返回 统一抛出异常
@@ -73,7 +74,7 @@ class Logic @@ -73,7 +74,7 @@ class Logic
73 $result = $query->select($columns)->get(); 74 $result = $query->select($columns)->get();
74 } 75 }
75 if($this->side == Common::A){ 76 if($this->side == Common::A){
76 - return $this->success($result); 77 + return $result;
77 } 78 }
78 return $this->success($result ? $result->toArray() : []); 79 return $this->success($result ? $result->toArray() : []);
79 } 80 }
@@ -132,9 +133,12 @@ class Logic @@ -132,9 +133,12 @@ class Logic
132 $this->fail('数据不存在或者已经删除'); 133 $this->fail('数据不存在或者已经删除');
133 } 134 }
134 } 135 }
  136 + $columns = Schema::getColumnListing($this->model->getTable());
135 foreach ($param as $name => $value){ 137 foreach ($param as $name => $value){
  138 + if(in_array($name, $columns)){
136 $this->model[$name] = $value; 139 $this->model[$name] = $value;
137 } 140 }
  141 + }
138 142
139 $res = $this->model->save(); 143 $res = $this->model->save();
140 144
  1 +<?php
  2 +
  3 +namespace App\Http\Requests\Aside;
  4 +
  5 +use App\Models\ServerConfig;
  6 +use Illuminate\Foundation\Http\FormRequest;
  7 +use Illuminate\Validation\Rule;
  8 +
  9 +/**
  10 + * Class ServerConfigRequest
  11 + * @package App\Http\Requests\Aside
  12 + * @author zbj
  13 + * @date 2023/4/23
  14 + */
  15 +class ServerConfigRequest extends FormRequest
  16 +{
  17 + /**
  18 + * Determine if the user is authorized to make this request.
  19 + *
  20 + * @return bool
  21 + */
  22 + public function authorize()
  23 + {
  24 + return true;
  25 + }
  26 +
  27 + /**
  28 + * Get the validation rules that apply to the request.
  29 + *
  30 + * @return array
  31 + */
  32 + public function rules()
  33 + {
  34 + return [
  35 + 'type' => ['required', Rule::in(array_keys(ServerConfig::typeMap()))],
  36 + 'title'=>'required|max:50',
  37 + 'host'=>'required|ip',
  38 + 'user'=>'required',
  39 + 'password'=>'required',
  40 + 'port'=>'required',
  41 + ];
  42 + }
  43 +
  44 + public function messages()
  45 + {
  46 + return [
  47 + 'type.required' => '请选择配置类型',
  48 + 'title.required' => '请输入配置名称',
  49 + 'title.max' => '配置名称不能超过50个字符',
  50 + 'host.max' => '请输入IP',
  51 + 'host.ip' => 'IP格式不正确',
  52 + 'user.required' => '请输入用户名',
  53 + 'password.required' => '请输入密码',
  54 + 'port.required' => '请输入端口',
  55 + ];
  56 + }
  57 +
  58 +}
  1 +<?php
  2 +
  3 +namespace App\Models\Product;
  4 +
  5 +
  6 +use App\Helper\Arr;
  7 +use App\Models\Base;
  8 +
  9 +class CategoryRelated extends Base
  10 +{
  11 +
  12 + //设置关联表名
  13 + protected $table = 'gl_product_category_related';
  14 +
  15 + const CREATED_AT = null;
  16 + const UPDATED_AT = null;
  17 +
  18 +
  19 + /**
  20 + * 关联产品分类
  21 + * @param $product_id
  22 + * @param $cate_ids
  23 + * @author zbj
  24 + * @date 2023/4/21
  25 + */
  26 + public static function saveRelated($product_id, $cate_ids)
  27 + {
  28 + if(!is_array($cate_ids)){
  29 + $cate_ids = array_filter(Arr::splitFilterToArray($cate_ids), 'intval');
  30 + }
  31 + //先删除
  32 + self::where('product_id', $product_id)->delete();
  33 +
  34 + //批量保存
  35 + $data = [];
  36 + foreach ($cate_ids as $cate_id){
  37 + $data[] = [
  38 + 'product_id' => $product_id,
  39 + 'cate_id' => $cate_id
  40 + ];
  41 + }
  42 + self::insert($data);
  43 + }
  44 +}
@@ -38,7 +38,7 @@ class Project extends Base @@ -38,7 +38,7 @@ class Project extends Base
38 */ 38 */
39 public function serverConfig() 39 public function serverConfig()
40 { 40 {
41 - return self::hasOne(ServeConfig::class, 'id', 'serve_id'); 41 + return self::hasOne(ServerConfig::class, 'id', 'serve_id');
42 } 42 }
43 43
44 /** 44 /**
@@ -47,7 +47,7 @@ class Project extends Base @@ -47,7 +47,7 @@ class Project extends Base
47 */ 47 */
48 public function mysqlConfig() 48 public function mysqlConfig()
49 { 49 {
50 - return self::hasOne(ServeConfig::class, 'id', 'mysql_id'); 50 + return self::hasOne(ServerConfig::class, 'id', 'mysql_id');
51 } 51 }
52 52
53 /** 53 /**
@@ -56,7 +56,7 @@ class Project extends Base @@ -56,7 +56,7 @@ class Project extends Base
56 */ 56 */
57 public function redisConfig() 57 public function redisConfig()
58 { 58 {
59 - return self::hasOne(ServeConfig::class, 'id', 'redis_id'); 59 + return self::hasOne(ServerConfig::class, 'id', 'redis_id');
60 } 60 }
61 61
62 /** 62 /**
@@ -13,7 +13,7 @@ namespace App\Models; @@ -13,7 +13,7 @@ namespace App\Models;
13 * Class ServeConfig 13 * Class ServeConfig
14 * @package App\Models 14 * @package App\Models
15 */ 15 */
16 -class ServeConfig extends Base 16 +class ServerConfig extends Base
17 { 17 {
18 /** 18 /**
19 * @var string 19 * @var string
@@ -33,6 +33,19 @@ class ServeConfig extends Base @@ -33,6 +33,19 @@ class ServeConfig extends Base
33 const TYPE_REDIS = 3; 33 const TYPE_REDIS = 3;
34 34
35 /** 35 /**
  36 + * @return string[]
  37 + * @author zbj
  38 + * @date 2023/4/23
  39 + */
  40 + public static function typeMap(){
  41 + return [
  42 + self::TYPE_SERVER => '服务器',
  43 + self::TYPE_MYSQL => 'MySQL',
  44 +// self::TYPE_REDIS => 'Redis',
  45 + ];
  46 + }
  47 +
  48 + /**
36 * 用户名加密 49 * 用户名加密
37 * @param $value 50 * @param $value
38 */ 51 */
@@ -9,6 +9,8 @@ @@ -9,6 +9,8 @@
9 namespace App\Services; 9 namespace App\Services;
10 10
11 use App\Models\Project; 11 use App\Models\Project;
  12 +use Illuminate\Support\Facades\DB;
  13 +use Illuminate\Support\Facades\Schema;
12 14
13 /** 15 /**
14 * Class ProjectServer 16 * Class ProjectServer
@@ -18,7 +20,7 @@ class ProjectServer extends BaseService @@ -18,7 +20,7 @@ class ProjectServer extends BaseService
18 { 20 {
19 /** 21 /**
20 * @param $project_id 22 * @param $project_id
21 - * @return bool 23 + * @return Project|false
22 */ 24 */
23 public static function useProject($project_id) 25 public static function useProject($project_id)
24 { 26 {
@@ -32,6 +34,47 @@ class ProjectServer extends BaseService @@ -32,6 +34,47 @@ class ProjectServer extends BaseService
32 config(['database.connections.custom_mysql.username' => $project->mysqlConfig()->user]); 34 config(['database.connections.custom_mysql.username' => $project->mysqlConfig()->user]);
33 config(['database.connections.custom_mysql.password' => $project->mysqlConfig()->password]); 35 config(['database.connections.custom_mysql.password' => $project->mysqlConfig()->password]);
34 // 设置 redis 配置 36 // 设置 redis 配置
  37 + return $project;
  38 + }
  39 +
  40 +
  41 + /**
  42 + * 创建数据库
  43 + * @param $project_id
  44 + * @author zbj
  45 + * @date 2023/4/23
  46 + */
  47 + public static function createDatabase($project_id){
  48 + $project = self::useProject($project_id);
  49 + DB::connection('custom_mysql')->statement("CREATE DATABASE IF NOT EXISTS {$project->databaseName()}");
  50 + }
  51 +
  52 +
  53 + /**
  54 + * @param $project_id
  55 + * @return bool
  56 + * @throws \Doctrine\DBAL\Exception
  57 + * @author zbj
  58 + * @date 2023/4/23
  59 + */
  60 + public static function initTable($project_id){
  61 + $project = self::useProject($project_id);
  62 + $database_name = DB::connection('custom_tmp_mysql')->getDatabaseName();
  63 +
  64 + $table = Schema::connection('custom_tmp_mysql')->getAllTables();
  65 + $table = array_column($table, 'Tables_in_' . $database_name);
  66 + foreach ($table as $v) {
  67 + $has_table = Schema::connection('custom_mysql')->hasTable($v);
  68 + if ($has_table)
  69 + continue;
  70 +
  71 + $connection = DB::connection('custom_tmp_mysql');
  72 + $sql = $connection->getDoctrineSchemaManager()
  73 + ->getDatabasePlatform()
  74 + ->getCreateTableSQL($connection->getDoctrineSchemaManager()->listTableDetails($v));
  75 +
  76 + DB::connection('custom_mysql')->select($sql[0]);
  77 + }
35 return true; 78 return true;
36 } 79 }
37 } 80 }
@@ -256,6 +256,7 @@ class UploadService extends BaseService @@ -256,6 +256,7 @@ class UploadService extends BaseService
256 * @date 2023/4/20 256 * @date 2023/4/20
257 */ 257 */
258 public function url2path($url){ 258 public function url2path($url){
  259 + $this->config();
259 $upload_url = config('filesystems')['disks'][$this->config['disk']]['url']; 260 $upload_url = config('filesystems')['disks'][$this->config['disk']]['url'];
260 return str_replace($upload_url . '/', '', $url); 261 return str_replace($upload_url . '/', '', $url);
261 } 262 }
@@ -268,6 +269,7 @@ class UploadService extends BaseService @@ -268,6 +269,7 @@ class UploadService extends BaseService
268 * @date 2023/4/20 269 * @date 2023/4/20
269 */ 270 */
270 public function path2url($path){ 271 public function path2url($path){
  272 + $this->config();
271 return Storage::disk($this->config['disk'])->url($path); 273 return Storage::disk($this->config['disk'])->url($path);
272 } 274 }
273 } 275 }
1 -<!DOCTYPE html>  
2 -<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">  
3 - <head>  
4 - <meta charset="utf-8">  
5 - <meta name="viewport" content="width=device-width, initial-scale=1">  
6 -  
7 - <title>Laravel</title>  
8 -  
9 - <!-- Fonts -->  
10 - <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">  
11 -  
12 - <!-- Styles -->  
13 - <style>  
14 - /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.dark\:text-gray-500{--tw-text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--tw-text-opacity))}}  
15 - </style>  
16 -  
17 - <style>  
18 - body {  
19 - font-family: 'Nunito', sans-serif;  
20 - }  
21 - </style>  
22 - </head>  
23 - <body class="antialiased">  
24 - <div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">  
25 - @if (Route::has('login'))  
26 - <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">  
27 - @auth  
28 - <a href="{{ url('/home') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">Home</a>  
29 - @else  
30 - <a href="{{ route('login') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">Log in</a>  
31 -  
32 - @if (Route::has('register'))  
33 - <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 dark:text-gray-500 underline">Register</a>  
34 - @endif  
35 - @endauth  
36 - </div>  
37 - @endif  
38 -  
39 - <div class="max-w-6xl mx-auto sm:px-6 lg:px-8">  
40 - <div class="flex justify-center pt-8 sm:justify-start sm:pt-0">  
41 - <svg viewBox="0 0 651 192" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-16 w-auto text-gray-700 sm:h-20">  
42 - <g clip-path="url(#clip0)" fill="#EF3B2D">  
43 - <path d="M248.032 44.676h-16.466v100.23h47.394v-14.748h-30.928V44.676zM337.091 87.202c-2.101-3.341-5.083-5.965-8.949-7.875-3.865-1.909-7.756-2.864-11.669-2.864-5.062 0-9.69.931-13.89 2.792-4.201 1.861-7.804 4.417-10.811 7.661-3.007 3.246-5.347 6.993-7.016 11.239-1.672 4.249-2.506 8.713-2.506 13.389 0 4.774.834 9.26 2.506 13.459 1.669 4.202 4.009 7.925 7.016 11.169 3.007 3.246 6.609 5.799 10.811 7.66 4.199 1.861 8.828 2.792 13.89 2.792 3.913 0 7.804-.955 11.669-2.863 3.866-1.908 6.849-4.533 8.949-7.875v9.021h15.607V78.182h-15.607v9.02zm-1.431 32.503c-.955 2.578-2.291 4.821-4.009 6.73-1.719 1.91-3.795 3.437-6.229 4.582-2.435 1.146-5.133 1.718-8.091 1.718-2.96 0-5.633-.572-8.019-1.718-2.387-1.146-4.438-2.672-6.156-4.582-1.719-1.909-3.032-4.152-3.938-6.73-.909-2.577-1.36-5.298-1.36-8.161 0-2.864.451-5.585 1.36-8.162.905-2.577 2.219-4.819 3.938-6.729 1.718-1.908 3.77-3.437 6.156-4.582 2.386-1.146 5.059-1.718 8.019-1.718 2.958 0 5.656.572 8.091 1.718 2.434 1.146 4.51 2.674 6.229 4.582 1.718 1.91 3.054 4.152 4.009 6.729.953 2.577 1.432 5.298 1.432 8.162-.001 2.863-.479 5.584-1.432 8.161zM463.954 87.202c-2.101-3.341-5.083-5.965-8.949-7.875-3.865-1.909-7.756-2.864-11.669-2.864-5.062 0-9.69.931-13.89 2.792-4.201 1.861-7.804 4.417-10.811 7.661-3.007 3.246-5.347 6.993-7.016 11.239-1.672 4.249-2.506 8.713-2.506 13.389 0 4.774.834 9.26 2.506 13.459 1.669 4.202 4.009 7.925 7.016 11.169 3.007 3.246 6.609 5.799 10.811 7.66 4.199 1.861 8.828 2.792 13.89 2.792 3.913 0 7.804-.955 11.669-2.863 3.866-1.908 6.849-4.533 8.949-7.875v9.021h15.607V78.182h-15.607v9.02zm-1.432 32.503c-.955 2.578-2.291 4.821-4.009 6.73-1.719 1.91-3.795 3.437-6.229 4.582-2.435 1.146-5.133 1.718-8.091 1.718-2.96 0-5.633-.572-8.019-1.718-2.387-1.146-4.438-2.672-6.156-4.582-1.719-1.909-3.032-4.152-3.938-6.73-.909-2.577-1.36-5.298-1.36-8.161 0-2.864.451-5.585 1.36-8.162.905-2.577 2.219-4.819 3.938-6.729 1.718-1.908 3.77-3.437 6.156-4.582 2.386-1.146 5.059-1.718 8.019-1.718 2.958 0 5.656.572 8.091 1.718 2.434 1.146 4.51 2.674 6.229 4.582 1.718 1.91 3.054 4.152 4.009 6.729.953 2.577 1.432 5.298 1.432 8.162 0 2.863-.479 5.584-1.432 8.161zM650.772 44.676h-15.606v100.23h15.606V44.676zM365.013 144.906h15.607V93.538h26.776V78.182h-42.383v66.724zM542.133 78.182l-19.616 51.096-19.616-51.096h-15.808l25.617 66.724h19.614l25.617-66.724h-15.808zM591.98 76.466c-19.112 0-34.239 15.706-34.239 35.079 0 21.416 14.641 35.079 36.239 35.079 12.088 0 19.806-4.622 29.234-14.688l-10.544-8.158c-.006.008-7.958 10.449-19.832 10.449-13.802 0-19.612-11.127-19.612-16.884h51.777c2.72-22.043-11.772-40.877-33.023-40.877zm-18.713 29.28c.12-1.284 1.917-16.884 18.589-16.884 16.671 0 18.697 15.598 18.813 16.884h-37.402zM184.068 43.892c-.024-.088-.073-.165-.104-.25-.058-.157-.108-.316-.191-.46-.056-.097-.137-.176-.203-.265-.087-.117-.161-.242-.265-.345-.085-.086-.194-.148-.29-.223-.109-.085-.206-.182-.327-.252l-.002-.001-.002-.002-35.648-20.524a2.971 2.971 0 00-2.964 0l-35.647 20.522-.002.002-.002.001c-.121.07-.219.167-.327.252-.096.075-.205.138-.29.223-.103.103-.178.228-.265.345-.066.089-.147.169-.203.265-.083.144-.133.304-.191.46-.031.085-.08.162-.104.25-.067.249-.103.51-.103.776v38.979l-29.706 17.103V24.493a3 3 0 00-.103-.776c-.024-.088-.073-.165-.104-.25-.058-.157-.108-.316-.191-.46-.056-.097-.137-.176-.203-.265-.087-.117-.161-.242-.265-.345-.085-.086-.194-.148-.29-.223-.109-.085-.206-.182-.327-.252l-.002-.001-.002-.002L40.098 1.396a2.971 2.971 0 00-2.964 0L1.487 21.919l-.002.002-.002.001c-.121.07-.219.167-.327.252-.096.075-.205.138-.29.223-.103.103-.178.228-.265.345-.066.089-.147.169-.203.265-.083.144-.133.304-.191.46-.031.085-.08.162-.104.25-.067.249-.103.51-.103.776v122.09c0 1.063.568 2.044 1.489 2.575l71.293 41.045c.156.089.324.143.49.202.078.028.15.074.23.095a2.98 2.98 0 001.524 0c.069-.018.132-.059.2-.083.176-.061.354-.119.519-.214l71.293-41.045a2.971 2.971 0 001.489-2.575v-38.979l34.158-19.666a2.971 2.971 0 001.489-2.575V44.666a3.075 3.075 0 00-.106-.774zM74.255 143.167l-29.648-16.779 31.136-17.926.001-.001 34.164-19.669 29.674 17.084-21.772 12.428-43.555 24.863zm68.329-76.259v33.841l-12.475-7.182-17.231-9.92V49.806l12.475 7.182 17.231 9.92zm2.97-39.335l29.693 17.095-29.693 17.095-29.693-17.095 29.693-17.095zM54.06 114.089l-12.475 7.182V46.733l17.231-9.92 12.475-7.182v74.537l-17.231 9.921zM38.614 7.398l29.693 17.095-29.693 17.095L8.921 24.493 38.614 7.398zM5.938 29.632l12.475 7.182 17.231 9.92v79.676l.001.005-.001.006c0 .114.032.221.045.333.017.146.021.294.059.434l.002.007c.032.117.094.222.14.334.051.124.088.255.156.371a.036.036 0 00.004.009c.061.105.149.191.222.288.081.105.149.22.244.314l.008.01c.084.083.19.142.284.215.106.083.202.178.32.247l.013.005.011.008 34.139 19.321v34.175L5.939 144.867V29.632h-.001zm136.646 115.235l-65.352 37.625V148.31l48.399-27.628 16.953-9.677v33.862zm35.646-61.22l-29.706 17.102V66.908l17.231-9.92 12.475-7.182v33.841z"/>  
44 - </g>  
45 - </svg>  
46 - </div>  
47 -  
48 - <div class="mt-8 bg-white dark:bg-gray-800 overflow-hidden shadow sm:rounded-lg">  
49 - <div class="grid grid-cols-1 md:grid-cols-2">  
50 - <div class="p-6">  
51 - <div class="flex items-center">  
52 - <svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg>  
53 - <div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laravel.com/docs" class="underline text-gray-900 dark:text-white">Documentation</a></div>  
54 - </div>  
55 -  
56 - <div class="ml-12">  
57 - <div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">  
58 - Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end.  
59 - </div>  
60 - </div>  
61 - </div>  
62 -  
63 - <div class="p-6 border-t border-gray-200 dark:border-gray-700 md:border-t-0 md:border-l">  
64 - <div class="flex items-center">  
65 - <svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"></path><path d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>  
66 - <div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laracasts.com" class="underline text-gray-900 dark:text-white">Laracasts</a></div>  
67 - </div>  
68 -  
69 - <div class="ml-12">  
70 - <div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">  
71 - Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process.  
72 - </div>  
73 - </div>  
74 - </div>  
75 -  
76 - <div class="p-6 border-t border-gray-200 dark:border-gray-700">  
77 - <div class="flex items-center">  
78 - <svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"></path></svg>  
79 - <div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laravel-news.com/" class="underline text-gray-900 dark:text-white">Laravel News</a></div>  
80 - </div>  
81 -  
82 - <div class="ml-12">  
83 - <div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">  
84 - Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials.  
85 - </div>  
86 - </div>  
87 - </div>  
88 -  
89 - <div class="p-6 border-t border-gray-200 dark:border-gray-700 md:border-l">  
90 - <div class="flex items-center">  
91 - <svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>  
92 - <div class="ml-4 text-lg leading-7 font-semibold text-gray-900 dark:text-white">Vibrant Ecosystem</div>  
93 - </div>  
94 -  
95 - <div class="ml-12">  
96 - <div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">  
97 - Laravel's robust library of first-party tools and libraries, such as <a href="https://forge.laravel.com" class="underline">Forge</a>, <a href="https://vapor.laravel.com" class="underline">Vapor</a>, <a href="https://nova.laravel.com" class="underline">Nova</a>, and <a href="https://envoyer.io" class="underline">Envoyer</a> help you take your projects to the next level. Pair them with powerful open source libraries like <a href="https://laravel.com/docs/billing" class="underline">Cashier</a>, <a href="https://laravel.com/docs/dusk" class="underline">Dusk</a>, <a href="https://laravel.com/docs/broadcasting" class="underline">Echo</a>, <a href="https://laravel.com/docs/horizon" class="underline">Horizon</a>, <a href="https://laravel.com/docs/sanctum" class="underline">Sanctum</a>, <a href="https://laravel.com/docs/telescope" class="underline">Telescope</a>, and more.  
98 - </div>  
99 - </div>  
100 - </div>  
101 - </div>  
102 - </div>  
103 -  
104 - <div class="flex justify-center mt-4 sm:items-center sm:justify-between">  
105 - <div class="text-center text-sm text-gray-500 sm:text-left">  
106 - <div class="flex items-center">  
107 - <svg fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" class="-mt-px w-5 h-5 text-gray-400">  
108 - <path d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>  
109 - </svg>  
110 -  
111 - <a href="https://laravel.bigcartel.com" class="ml-1 underline">  
112 - Shop  
113 - </a>  
114 -  
115 - <svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="ml-4 -mt-px w-5 h-5 text-gray-400">  
116 - <path d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>  
117 - </svg>  
118 -  
119 - <a href="https://github.com/sponsors/taylorotwell" class="ml-1 underline">  
120 - Sponsor  
121 - </a>  
122 - </div>  
123 - </div>  
124 -  
125 - <div class="ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0">  
126 - Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})  
127 - </div>  
128 - </div>  
129 - </div>  
130 - </div>  
131 - </body>  
132 -</html>  
@@ -42,6 +42,10 @@ Route::middleware(['web'])->group(function (){ //admin用渲染默认要加上w @@ -42,6 +42,10 @@ Route::middleware(['web'])->group(function (){ //admin用渲染默认要加上w
42 }); 42 });
43 }); 43 });
44 44
  45 + //项目管理
  46 + Route::prefix('project')->group(function () {
  47 + Route::post('/save_server_config', [Aside\ProjectController::class, 'saveServerConfig'])->name('admin.project.save_server_config');
  48 + });
45 49
46 }); 50 });
47 51