From fe8fcdf45bfca1bcace86c23bc1dca42b789efcd Mon Sep 17 00:00:00 2001 From: Daniel Triendl Date: Wed, 18 Mar 2026 11:27:11 +0100 Subject: [PATCH] 3cx Tapi Server --- .gitignore | 2 + server/3cx_Tapi.sln | 25 ++++++++ .../src/CPATapi.Server/CPATapi.Server.csproj | 13 ++++ .../Controllers/AvailabilityController.cs | 47 ++++++++++++++ .../Controllers/CallerIdController.cs | 42 ++++++++++++ .../Controllers/ContactController.cs | 43 +++++++++++++ .../Controllers/SearchController.cs | 64 +++++++++++++++++++ .../src/CPATapi.Server/Models/TapiContact.cs | 11 ++++ server/src/CPATapi.Server/Program.cs | 26 ++++++++ .../Properties/launchSettings.json | 30 +++++++++ server/src/CPATapi.Server/Startup.cs | 46 +++++++++++++ server/src/CPATapi.Server/appsettings.json | 14 ++++ 12 files changed, 363 insertions(+) create mode 100644 server/3cx_Tapi.sln create mode 100644 server/src/CPATapi.Server/CPATapi.Server.csproj create mode 100644 server/src/CPATapi.Server/Controllers/AvailabilityController.cs create mode 100644 server/src/CPATapi.Server/Controllers/CallerIdController.cs create mode 100644 server/src/CPATapi.Server/Controllers/ContactController.cs create mode 100644 server/src/CPATapi.Server/Controllers/SearchController.cs create mode 100644 server/src/CPATapi.Server/Models/TapiContact.cs create mode 100644 server/src/CPATapi.Server/Program.cs create mode 100644 server/src/CPATapi.Server/Properties/launchSettings.json create mode 100644 server/src/CPATapi.Server/Startup.cs create mode 100644 server/src/CPATapi.Server/appsettings.json diff --git a/.gitignore b/.gitignore index 8fc5d4a..d687fc7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ client/node_modules client/dist +.vs + # Build results [Dd]ebug/ [Dd]ebugPublic/ diff --git a/server/3cx_Tapi.sln b/server/3cx_Tapi.sln new file mode 100644 index 0000000..6343ddc --- /dev/null +++ b/server/3cx_Tapi.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11612.153 insiders +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPATapi.Server", "src\CPATapi.Server\CPATapi.Server.csproj", "{7879A024-A074-FE67-0546-8668213BFA99}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7879A024-A074-FE67-0546-8668213BFA99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7879A024-A074-FE67-0546-8668213BFA99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7879A024-A074-FE67-0546-8668213BFA99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7879A024-A074-FE67-0546-8668213BFA99}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E2CF5765-9038-4ED6-AC5C-1A3E569ABC6C} + EndGlobalSection +EndGlobal diff --git a/server/src/CPATapi.Server/CPATapi.Server.csproj b/server/src/CPATapi.Server/CPATapi.Server.csproj new file mode 100644 index 0000000..81ddb06 --- /dev/null +++ b/server/src/CPATapi.Server/CPATapi.Server.csproj @@ -0,0 +1,13 @@ + + + + net10.0 + + + + + + + + + diff --git a/server/src/CPATapi.Server/Controllers/AvailabilityController.cs b/server/src/CPATapi.Server/Controllers/AvailabilityController.cs new file mode 100644 index 0000000..c876800 --- /dev/null +++ b/server/src/CPATapi.Server/Controllers/AvailabilityController.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Dapper; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; + +namespace CPATapi.Server.Controllers +{ + [Route("[controller]")] + [ApiController] + public class AvailabilityController : ControllerBase + { + private readonly IConfiguration _config; + public AvailabilityController(IConfiguration config) => _config = config; + + [HttpGet, Route("users")] + public async Task> GetUsers() + { + await using var con = new SqlConnection(_config["Db:ConnectionStringZc"]); + await con.OpenAsync(); + return await con.QueryAsync(@" +SELECT DISTINCT MA_USER_NAME +FROM dbo.MA_DATEN +WHERE MA_USER_NAME IS NOT NULL AND MA_USER_AKTIV = 1 +ORDER BY MA_USER_NAME"); + } + + [HttpGet, Route("{user}")] + public async Task GetAvailability(string user) + { + await using var con = new SqlConnection(_config["Db:ConnectionStringZc"]); + await con.OpenAsync(); + var stampCount = (await con.QueryAsync(@" +SELECT * +FROM dbo.BU +INNER JOIN dbo.MA_DATEN on MA_NR = BU_MA_NR +WHERE + MA_USER_NAME = @user AND + BU_BU >= @date AND BU_BU < @tomorrow", new { user, date = DateTime.Now.Date, tomorrow = DateTime.Now.AddDays(1).Date })).Count(); + return new { user, loggedIn = stampCount % 2 != 0 }; + } + } +} diff --git a/server/src/CPATapi.Server/Controllers/CallerIdController.cs b/server/src/CPATapi.Server/Controllers/CallerIdController.cs new file mode 100644 index 0000000..e8eb430 --- /dev/null +++ b/server/src/CPATapi.Server/Controllers/CallerIdController.cs @@ -0,0 +1,42 @@ +using System.Threading.Tasks; +using CPATapi.Server.Models; +using Dapper; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; + +namespace CPATapi.Server.Controllers +{ + [Route("[controller]")] + [ApiController] + public class CallerIdController : ControllerBase + { + private readonly IConfiguration _config; + public CallerIdController(IConfiguration config) => _config = config; + + + [HttpGet, Route("{number}")] + public async Task CallerIdAsync([FromRoute] string number) + { + if (number.Length >= 5) + { + var minMatch = number[^5..]; + number = "%" + minMatch; + } + + await using var con = new SqlConnection(_config["Db:ConnectionString"]); + await con.OpenAsync(); + + var result = await con.QueryFirstOrDefaultAsync(@" +SELECT +TD_ID, +TD_NAME, +TD_NUMBER, +TD_NUMBER_TAPI, +TD_MEDIUM +FROM dbo.CP_TAPI_DIRECTORY +WHERE TD_NUMBER_TAPI LIKE @number", new {number}); + return result; + } + } +} diff --git a/server/src/CPATapi.Server/Controllers/ContactController.cs b/server/src/CPATapi.Server/Controllers/ContactController.cs new file mode 100644 index 0000000..5c4c111 --- /dev/null +++ b/server/src/CPATapi.Server/Controllers/ContactController.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using CPATapi.Server.Models; +using Dapper; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace CPATapi.Server.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ContactController : ControllerBase + { + + private readonly ILogger _logger; + private readonly IConfiguration _config; + + public ContactController(ILogger logger, IConfiguration config) + { + _logger = logger; + _config = config; + } + + [HttpGet] + public async Task> GetAsync() + { + await using var con = new SqlConnection(_config["Db:ConnectionString"]); + await con.OpenAsync(); + + var contacts = await con.QueryAsync(@" +SELECT +TD_ID, +TD_NAME, +TD_NUMBER, +TD_NUMBER_TAPI, +TD_MEDIUM +FROM dbo.CP_TAPI_DIRECTORY"); + return contacts; + } + } +} diff --git a/server/src/CPATapi.Server/Controllers/SearchController.cs b/server/src/CPATapi.Server/Controllers/SearchController.cs new file mode 100644 index 0000000..4526197 --- /dev/null +++ b/server/src/CPATapi.Server/Controllers/SearchController.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CPATapi.Server.Models; +using Dapper; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; + +namespace CPATapi.Server.Controllers +{ + [Route("[controller]")] + [ApiController] + public class SearchController : ControllerBase + { + private readonly IConfiguration _config; + public SearchController(IConfiguration config) => _config = config; + + [HttpGet] + public async Task> SearchAsync([FromQuery] string query) + { + if (query == null) + { + return new List(); + } + + var args = Regex.Split(query, "\\s").Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray(); + + if (args.Length == 0) + { + return new List(); + } + + await using var con = new SqlConnection(_config["Db:ConnectionString"]); + await con.OpenAsync(); + var sql = new StringBuilder(@" +SELECT TOP 10 +TD_ID, +TD_NAME, +TD_NUMBER, +TD_NUMBER_TAPI, +TD_MEDIUM +FROM dbo.CP_TAPI_DIRECTORY +WHERE "); + var first = true; + var dp = new DynamicParameters(); + for (var i = 1; i <= args.Length; i++) + { + if (!first) + { + sql.AppendLine("AND"); + } + first = false; + sql.AppendLine($"(TD_NAME LIKE @s{i} OR"); + sql.AppendLine($" TD_NUMBER_TAPI LIKE @s{i})"); + dp.Add($"s{i}", $"%{args[i-1]}%"); + } + + return await con.QueryAsync(sql.ToString(), dp); + } + } +} diff --git a/server/src/CPATapi.Server/Models/TapiContact.cs b/server/src/CPATapi.Server/Models/TapiContact.cs new file mode 100644 index 0000000..94bb0cc --- /dev/null +++ b/server/src/CPATapi.Server/Models/TapiContact.cs @@ -0,0 +1,11 @@ +namespace CPATapi.Server.Models +{ + public class TapiContact + { + public string TD_ID { get; set; } + public string TD_NAME { get; set; } + public string TD_NUMBER { get; set; } + public string TD_NUMBER_TAPI { get; set; } + public string TD_MEDIUM { get; set; } + } +} \ No newline at end of file diff --git a/server/src/CPATapi.Server/Program.cs b/server/src/CPATapi.Server/Program.cs new file mode 100644 index 0000000..bbc9dc4 --- /dev/null +++ b/server/src/CPATapi.Server/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace CPATapi.Server +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/server/src/CPATapi.Server/Properties/launchSettings.json b/server/src/CPATapi.Server/Properties/launchSettings.json new file mode 100644 index 0000000..37cfe29 --- /dev/null +++ b/server/src/CPATapi.Server/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:62406", + "sslPort": 0 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "contact", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "CPATapiServer": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/server/src/CPATapi.Server/Startup.cs b/server/src/CPATapi.Server/Startup.cs new file mode 100644 index 0000000..3d9d506 --- /dev/null +++ b/server/src/CPATapi.Server/Startup.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace CPATapi.Server +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services + .AddControllers(); + //.AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + //app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/server/src/CPATapi.Server/appsettings.json b/server/src/CPATapi.Server/appsettings.json new file mode 100644 index 0000000..7caaae0 --- /dev/null +++ b/server/src/CPATapi.Server/appsettings.json @@ -0,0 +1,14 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "Db": { + "ConnectionString": "", + "ConnectionStringZc": "" + }, + "AllowedHosts": "*" +}