Files
squad-rain-ops-mini/Apis/VipApi.cs

208 lines
9.6 KiB
C#

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using SqlSugar;
using RainOpsMini.Helpers;
using RainOpsMini.Models;
using RainOpsMini.Services;
using System.Collections.Generic;
using System.Linq;
using System;
using System.IO;
using RainOpsMini.Models;
namespace RainOpsMini.Apis
{
public static class VipApi
{
public static void MapVipApis(this IEndpointRouteBuilder app)
{
// VIP Management APIs
app.MapGet("/api/vip", (HttpContext ctx) =>
{
if (!ctx.User.HasClaim("Permission", "vips.view") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
return Results.Json(VipStorage.GetAll());
});
app.MapPost("/api/vip", (VipMember member, HttpContext ctx) =>
{
if (!ctx.User.HasClaim("Permission", "vips.add") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
if (string.IsNullOrWhiteSpace(member.SteamId)) return Results.BadRequest(new { error = "SteamID 不能为空" });
if (VipStorage.GetBySteamId(member.SteamId) != null) return Results.BadRequest(new { error = "该 SteamID 已存在" });
// Validate Group
if (member.Group != "VIP_RainOpsMini" && member.Group != "SVIP_RainOpsMini")
{
return Results.BadRequest(new { error = "无效的会员组" });
}
member.CreatedAt = DateTime.Now;
VipStorage.Add(member);
return Results.Ok();
});
app.MapPut("/api/vip", (VipMember member, HttpContext ctx) =>
{
if (!ctx.User.HasClaim("Permission", "vips.edit") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
if (string.IsNullOrWhiteSpace(member.SteamId)) return Results.BadRequest(new { error = "SteamID 不能为空" });
var existing = VipStorage.GetBySteamId(member.SteamId);
if (existing == null) return Results.NotFound(new { error = "会员不存在" });
// Validate Group
if (member.Group != "VIP_RainOpsMini" && member.Group != "SVIP_RainOpsMini")
{
return Results.BadRequest(new { error = "无效的会员组" });
}
VipStorage.Update(member);
return Results.Ok();
});
app.MapDelete("/api/vip/{steamId}", (string steamId, HttpContext ctx) =>
{
if (!ctx.User.HasClaim("Permission", "vips.delete") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
VipStorage.Remove(steamId);
return Results.Ok();
});
app.MapPost("/api/vip/sync", (HttpContext ctx) =>
{
if (!ctx.User.HasClaim("Permission", "vips.edit") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
try
{
string cfgPath = Path.Combine(Directory.GetCurrentDirectory(), "SquadGame", "ServerConfig", "Admins.cfg");
// Ensure directory exists just in case
Directory.CreateDirectory(Path.GetDirectoryName(cfgPath)!);
if (!File.Exists(cfgPath))
{
File.WriteAllText(cfgPath, "// Created by RainOpsMini\n");
}
VipStorage.SyncToAdminConfig(cfgPath);
return Results.Ok();
}
catch (Exception ex)
{
return Results.BadRequest(new { error = "写入配置文件失败: " + ex.Message });
}
});
// Points Management APIs
app.MapGet("/api/points", async (HttpContext ctx, int? page, int? pageSize, string? search) =>
{
if (!ctx.User.HasClaim("Permission", "points.view") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
int p = page ?? 1;
int ps = pageSize ?? 20;
using var db = DbHelper.GetInstance();
var query = db.Queryable<UserIntegral>();
if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(u => u.SteamId.Contains(search));
}
int total = await query.CountAsync();
var list = await query.OrderBy(u => u.LastUpdated, OrderByType.Desc)
.ToPageListAsync(p, ps);
return Results.Ok(new { total, list });
});
app.MapPost("/api/points/update", async (HttpContext ctx, UserIntegral req) =>
{
if (!ctx.User.HasClaim("Permission", "points.edit") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
if (string.IsNullOrWhiteSpace(req.SteamId)) return Results.BadRequest(new { error = "SteamID 不能为空" });
using var db = DbHelper.GetInstance();
var existing = await db.Queryable<UserIntegral>().FirstAsync(u => u.SteamId == req.SteamId);
if (existing == null)
{
req.LastUpdated = DateTime.UtcNow;
await db.Insertable(req).ExecuteCommandAsync();
}
else
{
existing.Points = req.Points;
existing.LastUpdated = DateTime.UtcNow;
await db.Updateable(existing).ExecuteCommandAsync();
}
return Results.Ok();
});
// CDK Management APIs
app.MapPost("/api/cdk/generate", async (HttpContext ctx, GenerateCdkRequest req) =>
{
if (!ctx.User.HasClaim("Permission", "cdks.add") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
if (req.Count <= 0 || req.Count > 100) return Results.BadRequest(new { error = "数量必须在 1-100 之间" });
if (req.DurationDays <= 0) return Results.BadRequest(new { error = "时长必须大于 0" });
if (req.Type != "VIP" && req.Type != "SVIP") return Results.BadRequest(new { error = "类型必须是 VIP 或 SVIP" });
string creator = ctx.User.Identity?.Name ?? "Admin";
var list = await CdkService.GenerateCdksAsync(req.Type, req.DurationDays, req.Count, req.Reason, creator);
return Results.Ok(new { count = list.Count, keys = list.Select(c => c.Code).ToList() });
});
app.MapGet("/api/cdk/list", async (HttpContext ctx, int? page, int? pageSize, string? type, string? status, string? search) =>
{
if (!ctx.User.HasClaim("Permission", "cdks.view") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
int p = page ?? 1;
int ps = pageSize ?? 20;
using var db = DbHelper.GetInstance();
var query = db.Queryable<CdkInfo>();
if (!string.IsNullOrWhiteSpace(type)) query = query.Where(c => c.Type == type);
if (!string.IsNullOrWhiteSpace(status)) query = query.Where(c => c.Status == status);
if (!string.IsNullOrWhiteSpace(search))
{
query = query.Where(c => c.Code.Contains(search) || c.Reason.Contains(search) || c.Creator.Contains(search) || c.RedeemedBy.Contains(search));
}
int total = await query.CountAsync();
var list = await query.OrderBy(c => c.CreatedAt, OrderByType.Desc)
.ToPageListAsync(p, ps);
return Results.Ok(new { total, list });
});
app.MapPost("/api/cdk/delete", async (DeleteCdkRequest req, HttpContext ctx) =>
{
if (!ctx.User.HasClaim("Permission", "cdks.delete") && !ctx.User.HasClaim("Permission", "all") && ctx.User.FindFirst("IsSuperAdmin")?.Value != "true") return Results.Unauthorized();
if (req.Codes == null || req.Codes.Count == 0) return Results.BadRequest(new { error = "No codes provided" });
using var db = DbHelper.GetInstance();
await db.Deleteable<CdkInfo>().Where(c => req.Codes.Contains(c.Code)).ExecuteCommandAsync();
return Results.Ok();
});
app.MapPost("/api/cdk/redeem", async (RedeemCdkRequest req) =>
{
// Public endpoint, no auth required (or maybe simple rate limit?)
var result = await CdkService.RedeemCdkAsync(req.Code, req.SteamId);
if (result.success)
{
return Results.Ok(new { message = result.message });
}
else
{
return Results.BadRequest(new { error = result.message });
}
});
}
}
}