发布节点可能有自定义名字
智能大石头 authored at 2024-09-27 00:15:10
5.58 KiB
Stardust
using Microsoft.AspNetCore.Mvc;
using NewLife;
using NewLife.Remoting.Extensions;
using NewLife.Serialization;
using Stardust.Data.Deployment;
using Stardust.Data.Nodes;
using Stardust.Models;
using Stardust.Server.Services;
using TokenService = Stardust.Server.Services.TokenService;

namespace Stardust.Server.Controllers;

/// <summary>发布中心服务</summary>
[ApiFilter]
[Route("[controller]/[action]")]
public class DeployController : BaseController
{
    private Node _node;
    private String _clientId;
    private readonly NodeService _nodeService;
    private readonly DeployService _deployService;
    private readonly TokenService _tokenService;
    private readonly StarServerSetting _setting;

    public DeployController(DeployService deployService, NodeService nodeService, TokenService tokenService, StarServerSetting setting)
    {
        _deployService = deployService;
        _nodeService = nodeService;
        _tokenService = tokenService;
        _setting = setting;
    }

    #region 令牌验证
    protected override Boolean OnAuthorize(String token)
    {
        var (jwt, node, ex) = _nodeService.DecodeToken(token, _setting.TokenSecret);
        _node = node;
        _clientId = jwt.Id;
        if (ex != null) throw ex;

        return node != null;
    }

    protected override void OnWriteError(String action, String message) => WriteHistory(0, action, false, message);
    #endregion

    /// <summary>获取分配到本节点的应用服务信息</summary>
    /// <returns></returns>
    public DeployInfo[] GetAll()
    {
        var list = AppDeployNode.FindAllByNodeId(_node.ID);

        var rs = new List<DeployInfo>();
        foreach (var item in list)
        {
            // 不返回未启用的发布集,如果需要在客户端删除,则通过指令下发来实现
            if (!item.Enable) continue;

            var app = item.Deploy;
            if (app == null || !app.Enable) continue;

            // 消除缓存,解决版本更新后不能及时更新缓存的问题
            app = AppDeploy.FindByKey(app.Id);
            if (app == null || !app.Enable) continue;

            //var ver = AppDeployVersion.FindByDeployIdAndVersion(app.Id, app.Version);
            var ver = _deployService.GetDeployVersion(app, _node);
            if (ver == null) continue;

            var inf = new DeployInfo
            {
                Name = app.Name,
                Version = app.Version,
                Url = ver?.Url,
                Hash = ver?.Hash,
                Overwrite = ver?.Overwrite,
                Mode = ver.Mode,

                Service = item.ToService(app),
            };
            rs.Add(inf);

            // 修正Url
            if (inf.Url.StartsWithIgnoreCase("/cube/file/")) inf.Url = inf.Url.Replace("/cube/file/", "/cube/file?id=");

            WriteHistory(app.Id, nameof(GetAll), true, inf.ToJson());
        }

        return rs.ToArray();
    }

    /// <summary>上传本节点的所有应用服务信息</summary>
    /// <param name="services"></param>
    /// <returns></returns>
    [HttpPost]
    public Int32 Upload([FromBody] ServiceInfo[] services)
    {
        if (services == null || services.Length == 0) return 0;

        // 本节点所有发布
        var list = AppDeployNode.FindAllByNodeId(_node.ID);

        var rs = 0;
        foreach (var svc in services)
        {
            if (svc.Name.IsNullOrEmpty()) continue;

            AppDeploy app = null;

            // 发布节点可能有自定义名字
            var dn = list.FirstOrDefault(e => e.DeployName.EqualIgnoreCase(svc.Name));
            app = dn?.Deploy;

            app ??= AppDeploy.FindByName(svc.Name);
            app ??= new AppDeploy { Name = svc.Name/*, Enable = svc.Enable*/ };

            //// 仅可用应用
            //if (app.Enable)
            {
                if (app.FileName.IsNullOrEmpty()) app.FileName = svc.FileName;
                if (app.Arguments.IsNullOrEmpty()) app.Arguments = svc.Arguments;
                if (app.WorkingDirectory.IsNullOrEmpty()) app.WorkingDirectory = svc.WorkingDirectory;
                if (app.UserName.IsNullOrEmpty()) app.UserName = svc.UserName;
                if (app.Environments.IsNullOrEmpty()) app.Environments = svc.Environments;
                if (app.Mode < 0) app.Mode = svc.Mode;

                app.MaxMemory = svc.MaxMemory;
            }

            // 先保存,可能有插入,需要取得应用发布Id
            var rs2 = app.Save();

            if (rs2 > 0) WriteHistory(app.Id, nameof(Upload), true, svc.ToJson());

            rs += rs2;

            dn ??= list.FirstOrDefault(e => e.DeployId == app.Id);
            if (dn == null)
                dn = new AppDeployNode { DeployId = app.Id, NodeId = _node.ID, Enable = svc.Enable };
            else
                list.Remove(dn);

            if (svc.Enable && app.Enable) dn.Enable = true;
            dn.LastUpload = DateTime.Now;

            dn.Save();
        }

        // 没有匹配的应用,本次禁用
        foreach (var item in list)
        {
            item.Enable = false;
            item.LastUpload = DateTime.Now;
            item.Update();
        }

        return rs;
    }

    /// <summary>应用心跳。上报应用信息</summary>
    /// <param name="inf"></param>
    /// <returns></returns>
    [HttpPost]
    public Int32 Ping([FromBody] AppInfo inf) => _deployService.Ping(_node, inf, UserHost);

    #region 辅助
    private void WriteHistory(Int32 appId, String action, Boolean success, String remark) => _deployService.WriteHistory(appId, _node?.ID ?? 0, action, success, remark, UserHost);
    #endregion
}