3cx Tapi Server

This commit is contained in:
2026-03-18 11:27:11 +01:00
parent 7a99b1ab55
commit fe8fcdf45b
12 changed files with 363 additions and 0 deletions

25
server/3cx_Tapi.sln Normal file
View File

@@ -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

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.78" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="2.1.2" />
</ItemGroup>
</Project>

View File

@@ -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<IEnumerable<string>> GetUsers()
{
await using var con = new SqlConnection(_config["Db:ConnectionStringZc"]);
await con.OpenAsync();
return await con.QueryAsync<string>(@"
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<object> 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 };
}
}
}

View File

@@ -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<TapiContact> 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<TapiContact>(@"
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;
}
}
}

View File

@@ -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<ContactController> _logger;
private readonly IConfiguration _config;
public ContactController(ILogger<ContactController> logger, IConfiguration config)
{
_logger = logger;
_config = config;
}
[HttpGet]
public async Task<IEnumerable<TapiContact>> GetAsync()
{
await using var con = new SqlConnection(_config["Db:ConnectionString"]);
await con.OpenAsync();
var contacts = await con.QueryAsync<TapiContact>(@"
SELECT
TD_ID,
TD_NAME,
TD_NUMBER,
TD_NUMBER_TAPI,
TD_MEDIUM
FROM dbo.CP_TAPI_DIRECTORY");
return contacts;
}
}
}

View File

@@ -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<IEnumerable<TapiContact>> SearchAsync([FromQuery] string query)
{
if (query == null)
{
return new List<TapiContact>();
}
var args = Regex.Split(query, "\\s").Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray();
if (args.Length == 0)
{
return new List<TapiContact>();
}
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<TapiContact>(sql.ToString(), dp);
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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<Startup>();
});
}
}

View File

@@ -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"
}
}
}

View File

@@ -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();
});
}
}
}

View File

@@ -0,0 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Db": {
"ConnectionString": "",
"ConnectionStringZc": ""
},
"AllowedHosts": "*"
}