2025-05-06 06:45:49 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Kurs.Platform.DynamicData;
|
|
|
|
|
|
using Kurs.Platform.Entities;
|
|
|
|
|
|
using Kurs.Platform.Extensions;
|
|
|
|
|
|
using Kurs.Platform.Queries;
|
|
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using Volo.Abp.Domain.Repositories;
|
|
|
|
|
|
using Volo.Abp.Tracing;
|
|
|
|
|
|
using Volo.Abp.Uow;
|
|
|
|
|
|
|
2025-07-27 18:57:19 +00:00
|
|
|
|
namespace Kurs.Platform.CustomEndpoints;
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
|
|
|
|
|
[Authorize]
|
2025-07-27 18:57:19 +00:00
|
|
|
|
[Route("api/app/custom-endpoint")]
|
|
|
|
|
|
public class CustomEndpointAppService : PlatformAppService
|
2025-05-06 06:45:49 +00:00
|
|
|
|
{
|
2025-07-27 18:57:19 +00:00
|
|
|
|
private readonly IRepository<CustomEndpoint, Guid> repo;
|
2025-05-06 06:45:49 +00:00
|
|
|
|
private readonly IHttpContextAccessor httpContextAccessor;
|
|
|
|
|
|
private readonly IDataSourceManager dataSourceManager;
|
|
|
|
|
|
private readonly IDynamicDataManager dynamicDataManager;
|
|
|
|
|
|
|
2025-07-27 18:57:19 +00:00
|
|
|
|
public CustomEndpointAppService(
|
|
|
|
|
|
IRepository<CustomEndpoint, Guid> repo,
|
2025-05-06 06:45:49 +00:00
|
|
|
|
IHttpContextAccessor httpContextAccessor,
|
|
|
|
|
|
IDataSourceManager dataSourceManager,
|
|
|
|
|
|
IDynamicDataManager dynamicDataManager)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.repo = repo;
|
|
|
|
|
|
this.httpContextAccessor = httpContextAccessor;
|
|
|
|
|
|
this.dataSourceManager = dataSourceManager;
|
|
|
|
|
|
this.dynamicDataManager = dynamicDataManager;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpGet("{**path}")]
|
2025-07-27 18:57:19 +00:00
|
|
|
|
[Authorize(PlatformConsts.AppCodes.CustomEndpoints.Get)]
|
2025-05-06 06:45:49 +00:00
|
|
|
|
public async Task<IActionResult> GetAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
return await Execute("GET");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[HttpPost("{**path}")]
|
2025-07-27 18:57:19 +00:00
|
|
|
|
[Authorize(PlatformConsts.AppCodes.CustomEndpoints.Post)]
|
2025-05-06 06:45:49 +00:00
|
|
|
|
public async Task<IActionResult> PostAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
return await Execute("POST");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<IActionResult> Execute(string method)
|
|
|
|
|
|
{
|
|
|
|
|
|
using var uow = UnitOfWorkManager.Begin(new AbpUnitOfWorkOptions(false), true);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-07-27 20:52:31 +00:00
|
|
|
|
// Request.Path = /api/app/custom-endpoint/yxcdfn/8
|
|
|
|
|
|
var rawPath = httpContextAccessor.HttpContext.Request.Path.ToString();
|
|
|
|
|
|
var decodedPath = Uri.UnescapeDataString(rawPath); // URL decode işlemi
|
|
|
|
|
|
|
|
|
|
|
|
var path = decodedPath
|
|
|
|
|
|
.Replace("/api/app/custom-endpoint", "", StringComparison.OrdinalIgnoreCase)
|
2025-05-06 06:45:49 +00:00
|
|
|
|
.EnsureStartsWith('/')
|
|
|
|
|
|
.EnsureEndsWith('/');
|
2025-07-27 20:52:31 +00:00
|
|
|
|
|
2025-07-27 18:57:19 +00:00
|
|
|
|
Logger.LogInformation("Custom Endpoint çağrısı. Kullanıcı:{user} Path:[{method}]{path}", CurrentUser.UserName, "GET", path);
|
2025-05-06 06:45:49 +00:00
|
|
|
|
var api = await repo.FirstOrDefaultAsync(a => path.StartsWith(a.Url) && a.Method == method);
|
|
|
|
|
|
if (api is null)
|
|
|
|
|
|
{
|
2025-07-27 18:57:19 +00:00
|
|
|
|
Logger.LogInformation("Custom Endpoint bulunamadı");
|
2025-05-06 06:45:49 +00:00
|
|
|
|
return new NotFoundResult();
|
|
|
|
|
|
}
|
2025-07-27 18:57:19 +00:00
|
|
|
|
Logger.LogInformation("Custom Endpoint bulundu. {api}", api.Name);
|
2025-05-06 06:45:49 +00:00
|
|
|
|
|
|
|
|
|
|
var canUse = api.Permissions.Any(a =>
|
|
|
|
|
|
(a.ResourceType == "User" && a.ResourceId == CurrentUser.UserName) ||
|
|
|
|
|
|
(a.ResourceType == "Role" && CurrentUser.Roles.Contains(a.ResourceId)) ||
|
|
|
|
|
|
(a.ResourceType == "Global"));
|
|
|
|
|
|
if (!canUse)
|
|
|
|
|
|
{
|
2025-07-27 18:57:19 +00:00
|
|
|
|
Logger.LogWarning("Custom Endpoint yetki yok");
|
2025-05-06 06:45:49 +00:00
|
|
|
|
return new UnauthorizedResult();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dictionary<string, object> param = [];
|
|
|
|
|
|
// Parametreler:
|
|
|
|
|
|
// 1- Statik
|
2025-07-27 18:57:19 +00:00
|
|
|
|
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Static))
|
2025-05-06 06:45:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
var value = GetDefaultValue(item.DefaultValue);
|
|
|
|
|
|
param.Add(item.Name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2- Query
|
|
|
|
|
|
var queryParams = httpContextAccessor.HttpContext.Request.Query;
|
2025-07-27 18:57:19 +00:00
|
|
|
|
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Query))
|
2025-05-06 06:45:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (queryParams.TryGetValue(item.Name, out var value))
|
|
|
|
|
|
{
|
|
|
|
|
|
param.Add(item.Name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (item.IsRequired)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Volo.Abp.UserFriendlyException(L[PlatformConsts.AppErrorCodes.ParameterNotValid, item.Name]);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
param.Add(item.Name, GetDefaultValue(item.DefaultValue));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3- Path
|
2025-07-27 18:57:19 +00:00
|
|
|
|
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Path && !a.Path.IsNullOrWhiteSpace()))
|
2025-05-06 06:45:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
var itemPath = item.Path.EnsureStartsWith('/').EnsureEndsWith('/');
|
|
|
|
|
|
var index = itemPath.IndexOf($"/:{item.Name}/");
|
|
|
|
|
|
if (index == -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Volo.Abp.UserFriendlyException(L[PlatformConsts.AppErrorCodes.ParameterNotValid, item.Name]);
|
|
|
|
|
|
}
|
|
|
|
|
|
var segmentCount = itemPath[..(index + 1)].Count(a => a == '/');
|
|
|
|
|
|
var value = path.GetSegment('/', segmentCount);
|
|
|
|
|
|
param.Add(item.Name, value ?? GetDefaultValue(item.DefaultValue));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4- Body
|
|
|
|
|
|
if (method == "POST")
|
|
|
|
|
|
{
|
|
|
|
|
|
var body = await httpContextAccessor.HttpContext.Request.ReadFormAsync();
|
2025-07-27 18:57:19 +00:00
|
|
|
|
foreach (var item in api.Parameters.Where(a => a.Type == PlatformConsts.CustomEndpointConsts.ParameterTypes.Body))
|
2025-05-06 06:45:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (body.TryGetValue(item.Name, out var value))
|
|
|
|
|
|
{
|
|
|
|
|
|
param.Add(item.Name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (item.IsRequired)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Volo.Abp.UserFriendlyException(L[PlatformConsts.AppErrorCodes.ParameterNotValid, item.Name]);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
param.Add(item.Name, GetDefaultValue(item.DefaultValue));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Logger.LogInformation("Parametreler: {param}", param);
|
|
|
|
|
|
|
|
|
|
|
|
var (dynamicDataRepository, connectionString, _) = await dynamicDataManager.GetAsync(api.DataSourceCode == "!Tenant", api.DataSourceCode);
|
|
|
|
|
|
var result = await dynamicDataRepository.QueryAsync(api.Sql, connectionString, param);
|
|
|
|
|
|
|
|
|
|
|
|
Logger.LogInformation("Sonuç başarılı");
|
|
|
|
|
|
Logger.LogInformation("{result}", result);
|
|
|
|
|
|
|
|
|
|
|
|
await uow.CompleteAsync();
|
|
|
|
|
|
|
|
|
|
|
|
return new ObjectResult(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Volo.Abp.UserFriendlyException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.LogException(ex);
|
|
|
|
|
|
await uow.RollbackAsync();
|
|
|
|
|
|
return new JsonResult(new
|
|
|
|
|
|
{
|
|
|
|
|
|
Message = ex.Message,
|
|
|
|
|
|
CorrelationId = LazyServiceProvider.GetRequiredService<ICorrelationIdProvider>().Get(),
|
|
|
|
|
|
})
|
|
|
|
|
|
{
|
|
|
|
|
|
StatusCode = 500,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.LogException(ex);
|
|
|
|
|
|
await uow.RollbackAsync();
|
|
|
|
|
|
return new JsonResult(new
|
|
|
|
|
|
{
|
|
|
|
|
|
Message = L[PlatformConsts.AppErrorCodes.InternalError].Value,
|
|
|
|
|
|
CorrelationId = LazyServiceProvider.GetRequiredService<ICorrelationIdProvider>().Get()
|
|
|
|
|
|
})
|
|
|
|
|
|
{
|
|
|
|
|
|
StatusCode = 500,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetDefaultValue(string strValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
return strValue?.Replace(PlatformConsts.DefaultValues.UserId, CurrentUser.Id.ToString())
|
|
|
|
|
|
.Replace(PlatformConsts.DefaultValues.UserName, CurrentUser.UserName)
|
|
|
|
|
|
.Replace(PlatformConsts.DefaultValues.Roles, CurrentUser.Roles.JoinAsString("','"))
|
|
|
|
|
|
.Replace(PlatformConsts.DefaultValues.Now, Clock.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture))
|
|
|
|
|
|
.Replace(PlatformConsts.DefaultValues.TenantId, CurrentTenant.Id.HasValue ? CurrentTenant.Id.ToString() : null);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-27 18:57:19 +00:00
|
|
|
|
//TODO: Custom Endpoint rol, permission seed
|
2025-05-06 06:45:49 +00:00
|
|
|
|
/*
|
|
|
|
|
|
Token İsteği Örnek:
|
|
|
|
|
|
POST /connect/token HTTP/1.1
|
|
|
|
|
|
Host: localhost:44344
|
|
|
|
|
|
Content-Type: application/x-www-form-urlencoded
|
|
|
|
|
|
|
2025-05-06 11:03:45 +00:00
|
|
|
|
username=system%40sozsoft.com
|
2025-05-06 06:45:49 +00:00
|
|
|
|
&password=...
|
|
|
|
|
|
&grant_type=password
|
|
|
|
|
|
&client_id=Platform_PublicApi
|
|
|
|
|
|
&scope=offline_access%20Platform
|
|
|
|
|
|
|
2025-07-27 18:57:19 +00:00
|
|
|
|
Custom Endpoint Seed:
|
2025-07-27 20:52:31 +00:00
|
|
|
|
SELECT * FROM PLanguage WHERE IsEnabled = @IsEnabled AND CultureName = @CultureName
|
2025-05-06 06:45:49 +00:00
|
|
|
|
INSERT INTO [dbo].[Orders]
|
|
|
|
|
|
([CustomerName]
|
|
|
|
|
|
,[ProductName]
|
|
|
|
|
|
,[OrderDate]
|
|
|
|
|
|
,[Quantity])
|
|
|
|
|
|
OUTPUT Inserted.*
|
|
|
|
|
|
VALUES
|
|
|
|
|
|
(@CustomerName, @ProductName, @OrderDate, @Quantity)
|
|
|
|
|
|
SELECT * FROM Orders WHERE Id = SCOPE_IDENTITY()
|
|
|
|
|
|
[{ "Type": "S", "Name": "CultureName", "DefaultValue": "ar" }]
|
|
|
|
|
|
|
|
|
|
|
|
[{ "Type": "Q", "Name": "CultureName", "DefaultValue": "ar" }]
|
|
|
|
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
{ "Type": "P", "Name": "IsEnabled", "DefaultValue": "en", "Path": "/yxcdfn/8/:IsEnabled/:CultureName/" },
|
|
|
|
|
|
{ "Type": "P", "Name": "CultureName", "DefaultValue": "en", "Path": "/yxcdfn/8/:IsEnabled/:CultureName/" }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
{ "Type": "B", "Name": "CustomerName", "DefaultValue": "" },
|
|
|
|
|
|
{ "Type": "B", "Name": "ProductName", "DefaultValue": "" },
|
|
|
|
|
|
{ "Type": "B", "Name": "OrderDate", "DefaultValue": "@NOW" },
|
|
|
|
|
|
{ "Type": "B", "Name": "Quantity", "DefaultValue": "" }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Guid? TenantId
|
|
|
|
|
|
string Name
|
|
|
|
|
|
string Description
|
2025-08-11 06:34:44 +00:00
|
|
|
|
string Url -> https://api.sozsoft.com/api/app/dinamik/yxfgu
|
2025-05-06 06:45:49 +00:00
|
|
|
|
string Method -> GET
|
|
|
|
|
|
string Params = [
|
|
|
|
|
|
{ Type: 'Static', Name: 'StartDate', DefaultValue: '234' },
|
|
|
|
|
|
{ Type: 'Query', Name: 'StartDate', DefaultValue: '', IsRequired: false },
|
|
|
|
|
|
{ Type: 'Path', Name: 'FaturaId', DefaultValue: '', Path: '/yxfgu/fatura/:FaturaId/kalem/357' },
|
|
|
|
|
|
{ Type: 'Path', Name: 'KalemId', DefaultValue: '', Path: '/yxfgu/fatura/xxx/kalem/:KalemId' },
|
|
|
|
|
|
{ Type: 'Body', Name: 'StartDate', DefaultValue: '' },
|
|
|
|
|
|
]
|
|
|
|
|
|
string Sql -> SELECT * FROM VSatislar WHERE MusteriId = @MusteriId AND StartDate >= @StartDate
|
|
|
|
|
|
string Permissions = [
|
2025-05-06 11:03:45 +00:00
|
|
|
|
{ ResourceType: 'User', ResourceId: 'system' },
|
2025-05-06 06:45:49 +00:00
|
|
|
|
{ ResourceType: 'User', ResourceId: 'sedat' },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
Query Parameter
|
2025-08-11 06:34:44 +00:00
|
|
|
|
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu
|
2025-05-06 06:45:49 +00:00
|
|
|
|
Method: GET
|
|
|
|
|
|
Parameters: ?StartDate=2024-12-31&EndDate=2025-12-31
|
|
|
|
|
|
|
|
|
|
|
|
Path Parameter
|
2025-08-11 06:34:44 +00:00
|
|
|
|
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu/fatura/467/kalem/357
|
2025-05-06 06:45:49 +00:00
|
|
|
|
Method: GET
|
|
|
|
|
|
Parameters: FaturaId=467
|
|
|
|
|
|
|
|
|
|
|
|
Body Parameter
|
2025-08-11 06:34:44 +00:00
|
|
|
|
URL: https://api.sozsoft.com/api/app/dinamik/yxfgu?UrunId=5
|
2025-05-06 06:45:49 +00:00
|
|
|
|
Method: POST
|
|
|
|
|
|
Parameters: ?StartDate=2024-12-31&EndDate=2025-12-31
|
|
|
|
|
|
Body: { Tutar: 2000, Tarih: '2024-12-31' }
|
|
|
|
|
|
|
|
|
|
|
|
*/
|