/// <summary> /// /// </summary> /// <typeparam name="TDictionary"></typeparam> /// <param name="config"></param> /// <returns></returns> public static TDictionary GetAddresses <TDictionary>(RpcConfig config) where TDictionary : IDictionary <ServiceIdentifier, ServiceInfo[]>, new() { if (config.Client?.Clients == null) { return(new TDictionary()); } var tempDic = new TDictionary(); foreach (var item in config.Client?.Clients) { if (!string.IsNullOrWhiteSpace(item.Address)) { tempDic.Add(new ServiceIdentifier(item.Name, item.Group), new[] { new ServiceInfo { Name = item.Name, Group = item.Group, Address = item.Address } }); } } return(tempDic); }
public DaemonMySql(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string adminUsernames, string databaseName, string databaseUser, string databasePassword) : base(bitsharesConfig, bitcoinConfig, bitsharesAccount, adminUsernames) { m_dataAccess = new MySqlData(databaseName, databaseUser, databasePassword); }
internal static IRegistry GetRegistry(AppHost appHost, RpcConfig config) { var registryItem = config.Registry; if (registryItem == null) { return(new DefaultRegistry(config)); } try { var type = ReflectHelper.GetTypeByIdentifier(registryItem.Type); if (type == null) { throw new ConfigException($"can't get type '{registryItem.Type}' from registry config"); } var factory = Activator.CreateInstance(type) as IRegistryFactory; if (factory == null) { throw new ConfigException(@"registry type not implements IRegistryFactory"); } var registry = factory.CreateRegistry(appHost, config); return(registry); } catch (Exception ex) { LogHelper.Error("InitializeResolver error", ex); throw; } }
public MeropsMonitor(AppHost appHost, RpcConfig config) { //var factory = new RpcClientFactory(null, null); _client = appHost.ClientFactory.GetInstance <IMonitorService>(config?.Monitor.Address); // ReSharper disable once UnusedVariable var writeTask = WriteLogsAsync(); }
// n == 0: backtrace until meet main chain // n > 0: backtrace n blocks private List <RpcBlockInfo> BacktraceBlocks(RpcConfig rpcConfig, string tipHash, int n = 0) { var blocks = new List <RpcBlockInfo>(); var h = tipHash; while (true) { var blockInfoResult = _dataExtractor.RpcCall <RpcBlockInfo>(rpcConfig, "getblock", h); if (blockInfoResult.HasFailed()) { _logger.LogError(blockInfoResult.Messages.ToCommaSeparated()); break; } var blockInfo = blockInfoResult.Value; _logger.LogInformation("Backtrace: {0} {1}", h, blockInfo.Confirmations); if (n == 0 && blockInfo.Confirmations >= 0) { break; } blocks.Append(blockInfo); h = blockInfo.PreviousBlockHash; if (blocks.Count == n) { break; } } return(blocks); }
public MeropsRegistry(AppHost appHost, RpcConfig config) : base(config) { Config = config; _appHost = appHost; _registryClient = new Lazy <IRegistryService>(() => { var address = Config?.Registry?.Address; if (string.IsNullOrWhiteSpace(address)) { LogHelper.Error("Registry Client Config Error: not exist or path is empty"); return(null); } var client = _appHost == null ? ClientFactory.GetInstance <IRegistryService>(address) : _appHost.ClientFactory.GetInstance <IRegistryService>(address); return(client); }); InitilizeAddresses(); // ReSharper disable once VirtualMemberCallInConstructor StartUpdateRegistry(); }
private static RpcConfig GenConfig(ArgsOption option) { var logTarget = RpcLogTargetEnum.All; if (option.LogTarget == LogTargetEnum.Console) { logTarget = RpcLogTargetEnum.Console; } else if (option.LogTarget == LogTargetEnum.File) { logTarget = RpcLogTargetEnum.File; } var config = new RpcConfig() { PidFile = option.PidFile, LogTarget = logTarget, LogName = option.LogName, LogDirectory = option.LogDirectory, AgentList = option.AgentList, PluginFullName = option.PluginFullName, PluginConfiguration = option.BenchmarkConfiguration }; return(config); }
public DaemonMySql(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string bitsharesAsset, string bitcoinDespositAddress, string databaseName, string databaseUser, string databasePassword) : base(bitsharesConfig, bitcoinConfig, bitsharesAccount, bitsharesAsset, bitcoinDespositAddress) { m_database = new Database(databaseName, databaseUser, databasePassword, System.Threading.Thread.CurrentThread.ManagedThreadId); }
private void _Worker(RpcConfig rpcConfig) { if (!HttpListener.IsSupported) { throw new Exception("Your platform doesn't support [HttpListener]"); } _httpListener = new HttpListener(); foreach (var host in rpcConfig.Hosts ?? throw new InvalidOperationException()) { _httpListener.Prefixes.Add($"http://{host}:{rpcConfig.Port}/"); } _httpListener.AuthenticationSchemes = AuthenticationSchemes.Anonymous; _apiKey = rpcConfig.ApiKey ?? throw new InvalidOperationException(); _httpListener.Start(); while (_httpListener.IsListening) { try { _Handle(_httpListener.GetContext()); } catch (Exception e) { Console.Error.WriteLine(e); } } _httpListener.Stop(); }
public Result <T> RpcCall <T>(RpcConfig config, string method, params object[] parameters) where T : class { var client = new RestClient($"{config.Url}:{config.Port}"); var request = new RestRequest(string.Empty, Method.POST) { Credentials = config.Credentials }; var pars = ObjectToJArray(parameters).ToString(); request.AddParameter("text/xml", $"{{\"jsonrpc\":\"1.0\",\"id\":\"alert-bot\",\"method\":\"{method}\",\"params\":{pars}}}", ParameterType.RequestBody); var response = client.Execute(request); if (response.StatusCode != HttpStatusCode.OK) { return(Result <T> .Fail($"RPC call '{method}' at '{config.Url}/{config.Port}' returns status code '{response.StatusCode}'")); } try { var data = JsonConvert.DeserializeObject <T>(response.Content); return(Result <T> .Ok(data)); } catch (Exception ex) { return(Result <T> .Fail($"RPC serialization fail '{method}' at '{config.Url}/{config.Port}'", $"object type: '{nameof(T)}'", $"ex: '{ex}'")); } }
public ZookeeperRegistry(RpcConfig config) { _config = config; _registryAddress = config.Registry.Address; var sessionExpire = 30 * 1000; Initialize(_registryAddress, sessionExpire); }
public MetaDaemonApi(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string databaseName, string databaseUser, string databasePassword, string listenAddress, string bitcoinFeeAddress, string bitsharesFeeAccount, string adminUsernames, string masterSiteUrl, string masterSiteIp, AsyncPump scheduler) : base(bitsharesConfig, bitcoinConfig, bitsharesAccount, adminUsernames, databaseName, databaseUser, databasePassword) { m_bitshaaresFeeAccount = bitsharesFeeAccount; m_bitcoinFeeAddress = bitcoinFeeAddress; m_masterSiteUrl = masterSiteUrl.TrimEnd('/'); m_scheduler = scheduler; ServicePointManager.ServerCertificateValidationCallback = Validator; Serialisation.Defaults(); // don't ban on exception here because we'll only end up banning the webserver! m_server = new ApiServer <IDummyDaemon>(new string[] { listenAddress }, null, false, eDdosMaxRequests.Ignore, eDdosInSeconds.One); m_api = new SharedApi <IDummyDaemon>(m_dataAccess); m_server.ExceptionEvent += m_api.OnApiException; // only allow the main site to post to us m_server.SetIpLock(masterSiteIp); m_marketHandlers = new Dictionary <string, MarketBase>(); // get all market pegged assets List <BitsharesAsset> allAssets = m_bitshares.BlockchainListAssets("", int.MaxValue); m_allBitsharesAssets = allAssets.ToDictionary(a => a.id); Dictionary <int, BitsharesAsset> peggedAssets = allAssets.Where(a => a.issuer_id <= 0).ToDictionary(a => a.id); // get all active markets containing those assets m_allDexMarkets = m_bitshares.BlockchainListMarkets().Where(m => m.last_error == null && peggedAssets.ContainsKey(m.base_id) && peggedAssets.ContainsKey(m.quote_id)).ToList(); m_allCurrencies = m_dataAccess.GetAllCurrencies(); List <MarketRow> markets = GetAllMarkets(); foreach (MarketRow r in markets) { m_marketHandlers[r.symbol_pair] = CreateHandlerForMarket(r); } m_server.HandlePostRoute(Routes.kSubmitAddress, OnSubmitAddress, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); m_server.HandleGetRoute(Routes.kGetAllMarkets, OnGetAllMarkets, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); }
private static bool CheckUsage(RpcConfig argsOption, IPlugin plugin) { if (argsOption.PluginConfiguration == "?") { plugin.Help(); return(true); } return(false); }
public MetaDaemonApi( RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string databaseName, string databaseUser, string databasePassword, string listenAddress, string bitcoinFeeAddress, string bitsharesFeeAccount, string adminUsernames, string masterSiteUrl, string masterSiteIp, AsyncPump scheduler) : base(bitsharesConfig, bitcoinConfig, bitsharesAccount, adminUsernames, databaseName, databaseUser, databasePassword) { m_bitshaaresFeeAccount = bitsharesFeeAccount; m_bitcoinFeeAddress = bitcoinFeeAddress; m_masterSiteUrl = masterSiteUrl.TrimEnd('/'); m_scheduler = scheduler; ServicePointManager.ServerCertificateValidationCallback = Validator; Serialisation.Defaults(); // don't ban on exception here because we'll only end up banning the webserver! m_server = new ApiServer<IDummyDaemon>(new string[] { listenAddress }, null, false, eDdosMaxRequests.Ignore, eDdosInSeconds.One); m_api = new SharedApi<IDummyDaemon>(m_dataAccess); m_server.ExceptionEvent += m_api.OnApiException; // only allow the main site to post to us m_server.SetIpLock(masterSiteIp); m_marketHandlers = new Dictionary<string,MarketBase>(); // get all market pegged assets List<BitsharesAsset> allAssets = m_bitshares.BlockchainListAssets("", int.MaxValue); m_allBitsharesAssets = allAssets.ToDictionary(a => a.id); Dictionary<int, BitsharesAsset> peggedAssets = allAssets.Where(a => a.issuer_id <= 0).ToDictionary(a => a.id); // get all active markets containing those assets m_allDexMarkets = m_bitshares.BlockchainListMarkets().Where(m => m.last_error == null && peggedAssets.ContainsKey(m.base_id) && peggedAssets.ContainsKey(m.quote_id)).ToList(); m_allCurrencies = m_dataAccess.GetAllCurrencies(); List<MarketRow> markets = GetAllMarkets(); foreach (MarketRow r in markets) { m_marketHandlers[r.symbol_pair] = CreateHandlerForMarket(r); } m_server.HandlePostRoute(Routes.kSubmitAddress, OnSubmitAddress, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); m_server.HandleGetRoute(Routes.kGetAllMarkets, OnGetAllMarkets, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); }
public Result <string> RpcCall(RpcConfig config, string method, params object[] parameters) { var result = this.RpcCall <RpcResult>(config, method, parameters); if (result.IsSuccess()) { return(Result <string> .Ok(result.Value.Result)); } return(Result <string> .Fail(result.Messages.ToArray())); }
/// <summary> Constructor. </summary> /// /// <remarks> Paul, 17/01/2015. </remarks> /// /// <param name="bitsharesConfig"> The bitshares configuration. </param> /// <param name="bitcoinConfig"> The bitcoin configuration. </param> /// <param name="bitsharesAccount"> The bitshares account. </param> /// <param name="bitsharesAsset"> The bitshares asset. </param> /// <param name="bitcoinDespositAddress"> The bitcoin desposit address. </param> public DaemonBase(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string adminUsernames) { m_bitshares = new BitsharesWallet(bitsharesConfig.m_url, bitsharesConfig.m_rpcUser, bitsharesConfig.m_rpcPassword); m_bitcoin = new BitcoinWallet(bitcoinConfig.m_url, bitcoinConfig.m_rpcUser, bitcoinConfig.m_rpcPassword, false); m_bitsharesAccount = bitsharesAccount; m_adminUsernames = adminUsernames.Split(','); m_addressByteType = (byte)(bitcoinConfig.m_useTestnet ? AltCoinAddressTypeBytes.BitcoinTestnet : AltCoinAddressTypeBytes.Bitcoin); }
/// <summary> Constructor. </summary> /// /// <remarks> Paul, 17/01/2015. </remarks> /// /// <param name="bitsharesConfig"> The bitshares configuration. </param> /// <param name="bitcoinConfig"> The bitcoin configuration. </param> /// <param name="bitsharesAccount"> The bitshares account. </param> /// <param name="bitsharesAsset"> The bitshares asset. </param> /// <param name="bitcoinDespositAddress"> The bitcoin desposit address. </param> public DaemonBase( RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string adminUsernames) { m_bitshares = new BitsharesWallet(bitsharesConfig.m_url, bitsharesConfig.m_rpcUser, bitsharesConfig.m_rpcPassword); m_bitcoin = new BitcoinWallet(bitcoinConfig.m_url, bitcoinConfig.m_rpcUser, bitcoinConfig.m_rpcPassword, false); m_bitsharesAccount = bitsharesAccount; m_adminUsernames = adminUsernames.Split(','); m_addressByteType = (byte)(bitcoinConfig.m_useTestnet ? AltCoinAddressTypeBytes.BitcoinTestnet : AltCoinAddressTypeBytes.Bitcoin); }
public static void Bind(this IConfiguration config, RpcConfig rpcConfig) { rpcConfig.ListenEndPoint = ParseIpEndPoint(config, "listenEndPoint"); rpcConfig.Ssl = new RpcConfig.SslCert(); var sslSection = config?.GetSection("ssl"); rpcConfig.Ssl.Path = ParseString(sslSection, "path"); rpcConfig.Ssl.Password = ParseString(sslSection, "password"); rpcConfig.AclConfig = ParseAcl(config, "acl"); }
public static void Bind(this IConfiguration config, RpcConfig rpcConfig) { rpcConfig.ListenEndPoint = ParseIpEndPoint(config, "listenEndPoint"); rpcConfig.SSL = new RpcConfig.SSLCert(); var v = config?.GetSection("SSL"); rpcConfig.SSL.Path = ParseString(v, "path"); rpcConfig.SSL.Password = ParseString(v, "password"); rpcConfig.ACL = ParseACL(config, "ACL"); }
/// <summary> /// /// </summary> /// <param name="routers"></param> /// <param name="rpcConfig"></param> public static void Initialize(IRouteBuilder routers, RpcConfig rpcConfig) { RpcManager.Initialize(rpcConfig); if (rpcConfig?.Service?.Paths != null) { foreach (var path in rpcConfig.Service.Paths) { routers.MapRoute(path, context => RpcManager.ProcessAsync(new AspNetCoreServerContext(context))); } } }
private Result <BlockInfoDTO> GetBlockInfo(RpcConfig rpcConfig, IDataExtractorService dataExtractor, IMapper mapper, string blockHash) { var blockResult = dataExtractor.RpcCall <RpcBlockResult>(rpcConfig, "getblock", blockHash); if (blockResult.HasFailed()) { return(Result <BlockInfoDTO> .Fail(blockResult.Messages.ToArray())); } var block = mapper.Map <BlockInfoDTO>(blockResult.Value); return(Result <BlockInfoDTO> .Ok(block)); }
public RpcTestsBase(ITestOutputHelper output) { Output = output; Port = FreeTcpPort(); Config = new RpcConfig { Ip = "127.0.0.1", Port = Port }; Server = new RpcServer<SimpleRpcSession, RpcConfig>(Config, null); Client = new RpcClient<SimpleRpcSession, RpcConfig>(Config); }
public Config(NetworkConfig network, GenesisConfig genesis, RpcConfig rpc, VaultConfig vault, StorageConfig storage, BlockchainConfig blockchain, HardforkConfig hardfork, VersionConfig version, CacheConfig cache) { Network = network; Genesis = genesis; Rpc = rpc; Vault = vault; Storage = storage; Blockchain = blockchain; Hardfork = hardfork; ConfigVersion = version; Cache = cache; }
//GlostenMilgromSimple m_glosten; public DaemonApi(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string bitsharesAsset, string bitcoinDespositAddress, string databaseName, string databaseUser, string databasePassword, string listenAddress) : base(bitsharesConfig, bitcoinConfig, bitsharesAccount, bitsharesAsset, bitcoinDespositAddress, databaseName, databaseUser, databasePassword) { m_server = new ApiServer<IDummy>(new string[] { listenAddress }); m_server.ExceptionEvent += OnApiException; m_server.HandlePostRoute(Routes.kSubmitAddress, OnSubmitAddress, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); m_server.HandlePostRoute(Routes.kGetStats, OnGetStats, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); }
public void Start(RpcConfig rpcConfig) { Task.Factory.StartNew(() => { try { _Worker(rpcConfig); } catch (Exception e) { Console.Error.WriteLine(e); } }, TaskCreationOptions.LongRunning); }
//GlostenMilgromSimple m_glosten; public DaemonApi(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string bitsharesAsset, string bitcoinDespositAddress, string databaseName, string databaseUser, string databasePassword, string listenAddress) : base(bitsharesConfig, bitcoinConfig, bitsharesAccount, bitsharesAsset, bitcoinDespositAddress, databaseName, databaseUser, databasePassword) { m_server = new ApiServer <IDummy>(new string[] { listenAddress }); m_server.ExceptionEvent += OnApiException; m_server.HandlePostRoute(Routes.kSubmitAddress, OnSubmitAddress, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); m_server.HandlePostRoute(Routes.kGetStats, OnGetStats, eDdosMaxRequests.Ignore, eDdosInSeconds.Ignore, false); }
/// <summary> /// /// </summary> /// <param name="factory"></param> /// <param name="config"></param> public DefaultServiceMapper(RpcServiceFactory factory, RpcConfig config) { if (factory == null) { throw new ArgumentNullException(nameof(factory)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } _config = config; _factory = factory; }
/// <summary> Constructor. </summary> /// /// <remarks> Paul, 17/01/2015. </remarks> /// /// <param name="bitsharesConfig"> The bitshares configuration. </param> /// <param name="bitcoinConfig"> The bitcoin configuration. </param> /// <param name="bitsharesAccount"> The bitshares account. </param> /// <param name="bitsharesAsset"> The bitshares asset. </param> /// <param name="bitcoinDespositAddress"> The bitcoin desposit address. </param> public DaemonBase( RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string bitsharesAsset, string bitcoinDespositAddress) { m_bitshares = new BitsharesWallet(bitsharesConfig.m_url, bitsharesConfig.m_rpcUser, bitsharesConfig.m_rpcPassword); m_bitcoin = new BitcoinWallet(bitcoinConfig.m_url, bitcoinConfig.m_rpcUser, bitcoinConfig.m_rpcPassword, false); m_bitsharesAccount = bitsharesAccount; m_bitsharesAsset = bitsharesAsset; m_bitcoinDespoitAddress = bitcoinDespositAddress; m_asset = m_bitshares.BlockchainGetAsset(bitsharesAsset); m_addressByteType = (byte)(bitcoinConfig.m_useTestnet ? AltCoinAddressTypeBytes.BitcoinTestnet : AltCoinAddressTypeBytes.Bitcoin); }
/// <summary> Constructor. </summary> /// /// <remarks> Paul, 17/01/2015. </remarks> /// /// <param name="bitsharesConfig"> The bitshares configuration. </param> /// <param name="bitcoinConfig"> The bitcoin configuration. </param> /// <param name="bitsharesAccount"> The bitshares account. </param> /// <param name="bitsharesAsset"> The bitshares asset. </param> /// <param name="bitcoinDespositAddress"> The bitcoin desposit address. </param> public DaemonBase(RpcConfig bitsharesConfig, RpcConfig bitcoinConfig, string bitsharesAccount, string bitsharesAsset, string bitcoinDespositAddress) { m_bitshares = new BitsharesWallet(bitsharesConfig.m_url, bitsharesConfig.m_rpcUser, bitsharesConfig.m_rpcPassword); m_bitcoin = new BitcoinWallet(bitcoinConfig.m_url, bitcoinConfig.m_rpcUser, bitcoinConfig.m_rpcPassword, false); m_bitsharesAccount = bitsharesAccount; m_bitsharesAsset = bitsharesAsset; m_bitcoinDespoitAddress = bitcoinDespositAddress; m_asset = m_bitshares.BlockchainGetAsset(bitsharesAsset); m_addressByteType = (byte)(bitcoinConfig.m_useTestnet ? AltCoinAddressTypeBytes.BitcoinTestnet : AltCoinAddressTypeBytes.Bitcoin); }
/// <summary> /// /// </summary> /// <param name="host"></param> /// <param name="config"></param> public RpcServiceFactory(AppHost host, RpcConfig config) { _appHost = host; _config = config; Services = new ReadOnlyListWraper <RpcService>(_services); if (config?.Service?.Mapper != null) { var factory = ReflectHelper.CreateInstanceByIdentifier <IServiceMapperFactory>(config.Service.Mapper.Type); _serviceMapper = factory.CreateServiceMapper(this, config); } else { _serviceMapper = new DefaultServiceMapper(this, config); } }
/// <summary> /// /// </summary> /// <param name="config"></param> public static void Initialize(RpcConfig config) { if (AppHost != null) { return; //throw new InvalidOperationException("default service host already initialized"); } lock (InitilizeLock) { if (AppHost == null) { AppHost = new AppHost(config); AppHost.Initialize(); } } }
//private readonly IClientChannelFactory _channelFactory; /// <summary> /// /// </summary> /// <param name="appHost"></param> /// <param name="config"></param> public RpcClientFactory(AppHost appHost, RpcConfig config) { _appHost = appHost; if (config?.Client != null) { //get InvokerFactory from config } _config = config; var channelFactory = new DefaultChannelFactory(); channelFactory.Initialize(config); _invokerFactory = config?.Client?.Invoker?.Type != null ? ReflectHelper.CreateInstanceByIdentifier <IInvokerFactory>(config.Client?.Invoker?.Type) : new DefaultInvokerFactory(); _invokerFactory.Initilize(appHost?.Registry, channelFactory); }
private static RpcConfig GenAgentConfig(AgentCommandOptions option) { var logTarget = RpcLogTargetEnum.File; if (option.Verbose) { logTarget = RpcLogTargetEnum.All; } var config = new RpcConfig() { PidFile = "agent-pid.txt", LogTarget = logTarget, LogName = "agent-.log", LogDirectory = ".", RpcPort = option.Port, HostName = "0.0.0.0" // binding for external access }; return(config); }
public RpcPerformanceTest(string[] args) { var config = new RpcConfig { Ip = "127.0.0.1", Port = 2828 }; //RpcSingleProcessTest(20, 4, config, RpcTestType.LngBlock); RpcSingleProcessTest(200000, 4, config, RpcTestType.NoReturn); RpcSingleProcessTest(200000, 4, config, RpcTestType.Await); RpcSingleProcessTest(100, 4, config, RpcTestType.Block); RpcSingleProcessTest(10000, 4, config, RpcTestType.Return); RpcSingleProcessTest(10000, 4, config, RpcTestType.Exception); }
/// <summary> /// /// </summary> /// <param name="app"></param> /// <param name="rpcConfig"></param> /// <returns></returns> public static IApplicationBuilder UseRpcLite(this IApplicationBuilder app, RpcConfig rpcConfig) { AspNetCoreInitializer.Initialize(app, rpcConfig); return app; }