mirror of
https://github.com/CannedNet/CannedNet.git
synced 2026-10-25 23:18:54 -07:00
current src
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
/.idea
|
||||
/CannedNet.API/Migrations
|
||||
CannedNet.sln.DotSettings.user
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>CannedNet</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CannedNet.Data;
|
||||
|
||||
public class AppDbContext : DbContext
|
||||
{
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<Account> Accounts { get; set; }
|
||||
public DbSet<CachedLogin> CachedLogins { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// accounts
|
||||
modelBuilder.Entity<Account>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.AccountId);
|
||||
entity.Property(e => e.AccountId).ValueGeneratedNever();
|
||||
entity.Property(e => e.Username).IsRequired();
|
||||
entity.Property(e => e.DisplayName).IsRequired();
|
||||
entity.ToTable("accounts");
|
||||
});
|
||||
|
||||
// cachedlogins
|
||||
modelBuilder.Entity<CachedLogin>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
entity.HasIndex(e => new { e.Platform, e.PlatformID }).IsUnique();
|
||||
entity.ToTable("cached_logins");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace CannedNet;
|
||||
|
||||
public enum JuniorState
|
||||
{
|
||||
BasedOnBirthday = 0,
|
||||
ModerationOverride = 1,
|
||||
PendingOverride = 2
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace CannedNet;
|
||||
|
||||
public enum PlatformType
|
||||
{
|
||||
All = -1,
|
||||
Steam = 0,
|
||||
Oculus = 1,
|
||||
PlayStation = 2,
|
||||
Xbox = 3,
|
||||
RecNet = 4,
|
||||
IOS = 5,
|
||||
GooglePlay = 6,
|
||||
Standalone = 7,
|
||||
Pico = 8
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
namespace CannedNet;
|
||||
|
||||
public class JwtTokenService
|
||||
{
|
||||
private static readonly RSA _rsa = RSA.Create(2048);
|
||||
private readonly RsaSecurityKey _securityKey;
|
||||
private readonly SigningCredentials _signingCredentials;
|
||||
private const string KeyId = "7C2F041398671515B0862CB23FAF95B03";
|
||||
|
||||
public JwtTokenService()
|
||||
{
|
||||
_securityKey = new RsaSecurityKey(_rsa) { KeyId = KeyId };
|
||||
_signingCredentials = new SigningCredentials(_securityKey, SecurityAlgorithms.RsaSha256);
|
||||
}
|
||||
|
||||
public string GenerateToken(string accountId, string platformId, string platform = "Steam")
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var exp = now.AddHours(1);
|
||||
|
||||
// theres gotta be some other better way to do this, but im fucking stupid so :shrug:
|
||||
// if it works it works ig idk, someone can clean this up later or i will later
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim("iss", "https://rr.lapis.codes"),
|
||||
new Claim("nbf", ((DateTimeOffset)now).ToUnixTimeSeconds().ToString()),
|
||||
new Claim("iat", ((DateTimeOffset)now).ToUnixTimeSeconds().ToString()),
|
||||
new Claim("exp", ((DateTimeOffset)exp).ToUnixTimeSeconds().ToString()),
|
||||
new Claim("aud", "https://rr.lapis.codes/resources"),
|
||||
new Claim("amr", "cached_login"),
|
||||
new Claim("client_id", "recroom"),
|
||||
new Claim("sub", accountId),
|
||||
new Claim("auth_time", ((DateTimeOffset)now).ToUnixTimeSeconds().ToString()),
|
||||
new Claim("idp", "local"),
|
||||
new Claim("platform", platform),
|
||||
new Claim("platform_id", platformId),
|
||||
new Claim("rn.ver", "20210129"),
|
||||
new Claim("rn.plat", "0"),
|
||||
new Claim("role", "gameClient"),
|
||||
new Claim("jti", Guid.NewGuid().ToString("N").Substring(0, 32).ToUpper())
|
||||
};
|
||||
|
||||
claims.Add(new Claim("scope", "profile"));
|
||||
claims.Add(new Claim("scope", "rn"));
|
||||
claims.Add(new Claim("scope", "rn.accounts"));
|
||||
claims.Add(new Claim("scope", "rn.accounts.gc"));
|
||||
claims.Add(new Claim("scope", "rn.api"));
|
||||
claims.Add(new Claim("scope", "rn.chat"));
|
||||
claims.Add(new Claim("scope", "rn.clubs"));
|
||||
claims.Add(new Claim("scope", "rn.commerce"));
|
||||
claims.Add(new Claim("scope", "rn.match.read"));
|
||||
claims.Add(new Claim("scope", "rn.match.write"));
|
||||
claims.Add(new Claim("scope", "rn.notify"));
|
||||
claims.Add(new Claim("scope", "rn.rooms"));
|
||||
claims.Add(new Claim("scope", "rn.storage"));
|
||||
claims.Add(new Claim("scope", "offline_access"));
|
||||
|
||||
var identity = new ClaimsIdentity(claims, "jwt");
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var token = handler.CreateJwtSecurityToken(
|
||||
issuer: "https://auth.epicquest.live",
|
||||
audience: "https://auth.epicquest.live/resources",
|
||||
subject: identity,
|
||||
notBefore: now,
|
||||
issuedAt: now,
|
||||
expires: exp,
|
||||
signingCredentials: _signingCredentials);
|
||||
|
||||
return handler.WriteToken(token);
|
||||
}
|
||||
|
||||
public string? ValidateAndGetAccountId(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
handler.InboundClaimTypeMap.Clear();
|
||||
|
||||
var parameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = _securityKey,
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = "https://auth.epicquest.live",
|
||||
ValidateAudience = true,
|
||||
ValidAudience = "https://auth.epicquest.live/resources",
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
var principal = handler.ValidateToken(token, parameters, out SecurityToken validatedToken);
|
||||
|
||||
var accountIdClaim = principal.Claims.FirstOrDefault(c => c.Type == "sub");
|
||||
|
||||
return accountIdClaim?.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CannedNet;
|
||||
|
||||
public class Account
|
||||
{
|
||||
[JsonPropertyName("accountId")] public int? AccountId { get; set; }
|
||||
[JsonPropertyName("profileImage")] public string? ProfileImage { get; set; }
|
||||
[JsonPropertyName("isJunior")] public bool IsJunior { get; set; }
|
||||
[JsonPropertyName("platforms")] public int? Platforms { get; set; }
|
||||
[JsonPropertyName("personalPronouns")] public int? PersonalPronouns { get; set; }
|
||||
[JsonPropertyName("identityFlags")] public int? IdentityFlags { get; set; }
|
||||
[JsonPropertyName("username")] public string? Username { get; set; }
|
||||
[JsonPropertyName("displayName")] public string? DisplayName { get; set; }
|
||||
[JsonPropertyName("createdAt")] public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CannedNet;
|
||||
|
||||
public class CachedLogin
|
||||
{
|
||||
[JsonIgnore]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("accountId")]
|
||||
public int AccountId { get; set; }
|
||||
|
||||
[JsonPropertyName("platform")]
|
||||
public PlatformType Platform { get; set; }
|
||||
|
||||
[JsonPropertyName("platformId")]
|
||||
public string PlatformID { get; set; }
|
||||
|
||||
[JsonPropertyName("lastLoginTime")]
|
||||
public DateTime LastLoginTime { get; set; }
|
||||
|
||||
[JsonPropertyName("requirePassword")]
|
||||
public bool RequirePassword { get; set; } = false;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CannedNet;
|
||||
|
||||
public class RecNetResultWithValue<T> : RecNetResult
|
||||
{
|
||||
[JsonPropertyName("value")] public required T? Value { get; set; }
|
||||
}
|
||||
public class RecNetResult
|
||||
{
|
||||
public static RecNetResult Ok() => new RecNetResult { Success = true };
|
||||
public static RecNetResult Err(string error) => new RecNetResult { Success = false, Error = error };
|
||||
public static RecNetResultWithValue<T> Ok<T>(T value) => new RecNetResultWithValue<T> { Success = true, Value = value };
|
||||
public static RecNetResultWithValue<T> Err<T>(string error) => new RecNetResultWithValue<T> { Success = false, Error = error, Value = default };
|
||||
|
||||
[JsonPropertyName("success")] public bool Success { get; set; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonPropertyName("error")] public string? Error { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CannedNet;
|
||||
|
||||
public class SelfAccount : Account
|
||||
{
|
||||
[JsonPropertyName("email")] public string? Email { get; set; }
|
||||
[JsonPropertyName("phone")] public string? Phone { get; set; }
|
||||
[JsonPropertyName("juniorState")] public JuniorState? JuniorState { get; set; }
|
||||
[JsonPropertyName("birthday")] public DateTime? Birthday { get; set; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[JsonPropertyName("parentAccountId")] public string? ParentAccountId { get; set; }
|
||||
[JsonPropertyName("availableUsernameChanges")] public int? AvailableUsernameChanges { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
using CannedNet;
|
||||
using CannedNet.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// TODO: organize this into multiple files
|
||||
// TODO: cdn stuff soon so i can not just have 0 images for this lmao
|
||||
// TODO: setup signalr and get websockets running, currently should focus on notifications as logging in is being blocked by that
|
||||
|
||||
// needed so it won't try to auto capitalize random stuff that may be case-sensitive
|
||||
builder.Services.ConfigureHttpJsonOptions(options =>
|
||||
{
|
||||
options.SerializerOptions.PropertyNamingPolicy = null;
|
||||
});
|
||||
|
||||
// postgresql db, will probably have it configurable later on but for now just hardcode it in
|
||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
||||
?? "Host=localhost;Port=5432;Database=cannednet;Username=postgres;Password=postgres";
|
||||
|
||||
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||
options.UseNpgsql(connectionString));
|
||||
|
||||
builder.Services.AddScoped<JwtTokenService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// apply db migrations on startup so we don't have to worry about it later bc i hate doing db things manually lol
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
db.Database.Migrate();
|
||||
}
|
||||
|
||||
var jwtService = app.Services.GetRequiredService<JwtTokenService>();
|
||||
|
||||
// request logging
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var request = context.Request;
|
||||
Console.WriteLine($"\n{request.Method} {request.Path}{request.QueryString}");
|
||||
|
||||
foreach (var header in request.Headers)
|
||||
Console.WriteLine($" {header.Key}: {string.Join(", ", header.Value)}");
|
||||
|
||||
if (request.ContentLength > 0)
|
||||
{
|
||||
request.EnableBuffering();
|
||||
using (var reader = new StreamReader(request.Body, leaveOpen: true))
|
||||
{
|
||||
var body = await reader.ReadToEndAsync();
|
||||
Console.WriteLine($" Body: {body}");
|
||||
}
|
||||
request.Body.Position = 0;
|
||||
}
|
||||
|
||||
await next(context);
|
||||
});
|
||||
|
||||
app.MapGet("/", () =>
|
||||
{
|
||||
var json = File.ReadAllText("endpoints.json");
|
||||
return Results.Content(json, "application/json");
|
||||
});
|
||||
|
||||
app.MapGet("/api/config/v1/amplitude", () =>
|
||||
{
|
||||
var config = new
|
||||
{
|
||||
AmplitudeKey = "fd846cd67066d19eec62fbdd00be9258",
|
||||
StatSigKey = "client-mnew4Ggjuu3Fbh86Ed3tRgWUxapp0D7PPB0i3MXUEao",
|
||||
RudderStackKey = "37qVlzZyXqdmZckXJ61vQeEJp8Y",
|
||||
UseRudderStack = true
|
||||
};
|
||||
|
||||
return Results.Ok(config);
|
||||
});
|
||||
|
||||
app.MapGet("/api/config/v2", () =>
|
||||
{
|
||||
var json = File.ReadAllText("configv2.json");
|
||||
return Results.Content(json, "application/json");
|
||||
});
|
||||
|
||||
app.MapGet("/api/versioncheck/v4", () =>
|
||||
{
|
||||
return Results.Content("{\"VersionStatus\":0}","application/json");
|
||||
});
|
||||
|
||||
app.MapGet("/api/gameconfigs/v1/all", () =>
|
||||
{
|
||||
var json = File.ReadAllText("gameconfigs.json");
|
||||
return Results.Content(json, "application/json");
|
||||
});
|
||||
|
||||
app.MapGet("/photon", () =>
|
||||
{
|
||||
var json = File.ReadAllText("photonsettings.json");
|
||||
return Results.Content(json, "application/json");
|
||||
});
|
||||
|
||||
app.MapGet("/eac/challenge", () =>
|
||||
{
|
||||
var file = File.ReadAllText("eacchallenge.txt");
|
||||
return Results.Content(file,"text/plain");
|
||||
});
|
||||
|
||||
app.MapGet("/api/playerReputation/v1/{id}", (string id) =>
|
||||
{
|
||||
// TODO: make rep actually work
|
||||
return Results.Content($"{{\"AccountId\":{id},\"Noteriety\":0,\"CheerGeneral\":0,\"CheerHelpful\":0,\"CheerCreative\":0,\"CheerGreatHost\":0,\"CheerSportsman\":0,\"CheerCredit\":20,\"SelectedCheer\":null}}","application/json");
|
||||
});
|
||||
|
||||
app.MapGet("/api/players/v1/progression/{id}", (string id) =>
|
||||
{
|
||||
// TODO: make progression actually work
|
||||
return Results.Content($"{{\"PlayerId\":{id},\"Level\":1,\"XP\":0}}","application/json");
|
||||
});
|
||||
|
||||
app.MapGet("/api/PlayerReporting/v1/moderationBlockDetails", () =>
|
||||
{
|
||||
// TODO: make this actually work
|
||||
return Results.Content($"{{\"ReportCategory\":0,\"Duration\":0,\"GameSessionId\":0,\"IsHostKick\":false,\"Message\":\"\",\"PlayerIdReporter\":null,\"IsBan\":false}}","application/json");
|
||||
});
|
||||
|
||||
app.MapPost("/player/login", () =>
|
||||
{
|
||||
return Results.Ok();
|
||||
});
|
||||
|
||||
app.MapGet("/cachedlogin/forplatformid/{platform}/{id}", async (string platform, string id, AppDbContext db) =>
|
||||
{
|
||||
var platformType = int.Parse(platform);
|
||||
var logins = await db.CachedLogins
|
||||
.Where(c => c.Platform == (PlatformType)platformType && c.PlatformID == id)
|
||||
.ToListAsync();
|
||||
|
||||
return Results.Json(logins.Any() ? logins : new List<object>());
|
||||
});
|
||||
|
||||
app.MapGet("/account/me", (HttpRequest request, AppDbContext db) =>
|
||||
{
|
||||
var authHeader = request.Headers.Authorization.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
|
||||
return Results.Unauthorized();
|
||||
|
||||
var token = authHeader.Substring("Bearer ".Length);
|
||||
var accountId = jwtService.ValidateAndGetAccountId(token);
|
||||
|
||||
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId, out var id))
|
||||
return Results.Unauthorized();
|
||||
|
||||
var selfAccount = new SelfAccount
|
||||
{
|
||||
AccountId = id,
|
||||
ProfileImage = "hdqeamlcmatc6qzoi2ybgf0ddijjcf.jpg",
|
||||
IsJunior = false,
|
||||
Platforms = 0,
|
||||
PersonalPronouns = 0,
|
||||
IdentityFlags = 0,
|
||||
Username = $"Player{id}",
|
||||
DisplayName = $"Player{id}",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Email = null,
|
||||
Phone = null,
|
||||
JuniorState = null,
|
||||
Birthday = null,
|
||||
ParentAccountId = null,
|
||||
AvailableUsernameChanges = 1
|
||||
};
|
||||
|
||||
return Results.Ok(selfAccount);
|
||||
});
|
||||
|
||||
app.MapGet("/account/bulk", (HttpRequest request) =>
|
||||
{
|
||||
var ids = request.Query["id"];
|
||||
var accounts = new List<Account>();
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
if (int.TryParse(id, out var accountId))
|
||||
{
|
||||
var account = new Account
|
||||
{
|
||||
AccountId = accountId,
|
||||
ProfileImage = "hdqeamlcmatc6qzoi2ybgf0ddijjcf.jpg",
|
||||
IsJunior = false,
|
||||
Platforms = 0,
|
||||
PersonalPronouns = 0,
|
||||
IdentityFlags = 0,
|
||||
Username = $"Player{accountId}",
|
||||
DisplayName = $"Player{accountId}",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
accounts.Add(account);
|
||||
}
|
||||
}
|
||||
|
||||
return Results.Json(accounts);
|
||||
});
|
||||
|
||||
app.MapPost("/account/create", async (HttpRequest httpRequest, AppDbContext db) =>
|
||||
{
|
||||
int platform = 0;
|
||||
string platformId = "";
|
||||
|
||||
if (httpRequest.ContentLength.HasValue && httpRequest.ContentLength > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var contentType = httpRequest.ContentType ?? "";
|
||||
httpRequest.EnableBuffering();
|
||||
|
||||
using (var reader = new StreamReader(httpRequest.Body, leaveOpen: true))
|
||||
{
|
||||
var body = await reader.ReadToEndAsync();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(body))
|
||||
{
|
||||
if (contentType.Contains("application/x-www-form-urlencoded"))
|
||||
{
|
||||
var pairs = body.Split('&');
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
var keyValue = pair.Split('=');
|
||||
if (keyValue.Length == 2)
|
||||
{
|
||||
var key = Uri.UnescapeDataString(keyValue[0]);
|
||||
var value = Uri.UnescapeDataString(keyValue[1]);
|
||||
|
||||
if (key == "platform" && int.TryParse(value, out var parsedPlatform))
|
||||
platform = parsedPlatform;
|
||||
else if (key == "platformId")
|
||||
platformId = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
httpRequest.Body.Position = 0;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
var accountId = new Random().Next(10000, 99999);
|
||||
var account = new Account
|
||||
{
|
||||
AccountId = accountId,
|
||||
ProfileImage = "hdqeamlcmatc6qzoi2ybgf0ddijjcf.jpg",
|
||||
IsJunior = false,
|
||||
Platforms = 0,
|
||||
PersonalPronouns = 0,
|
||||
IdentityFlags = 0,
|
||||
Username = $"Player{accountId}",
|
||||
DisplayName = $"Player{accountId}",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
db.Accounts.Add(account);
|
||||
|
||||
if (!string.IsNullOrEmpty(platformId))
|
||||
{
|
||||
var loginRecord = new CachedLogin
|
||||
{
|
||||
AccountId = accountId,
|
||||
Platform = (PlatformType)platform,
|
||||
PlatformID = platformId,
|
||||
LastLoginTime = DateTime.UtcNow,
|
||||
RequirePassword = false
|
||||
};
|
||||
|
||||
db.CachedLogins.Add(loginRecord);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return Results.Ok(RecNetResultWithValue<Account>.Ok(account));
|
||||
});
|
||||
|
||||
app.MapPost("/connect/token", async (HttpRequest httpRequest) =>
|
||||
{
|
||||
string accountId = "";
|
||||
string platformId = "";
|
||||
string platform = "";
|
||||
|
||||
if (httpRequest.ContentLength.HasValue && httpRequest.ContentLength > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
httpRequest.EnableBuffering();
|
||||
using (var reader = new StreamReader(httpRequest.Body, leaveOpen: true))
|
||||
{
|
||||
var body = await reader.ReadToEndAsync();
|
||||
if (!string.IsNullOrWhiteSpace(body))
|
||||
{
|
||||
var pairs = body.Split('&');
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
var keyValue = pair.Split('=');
|
||||
if (keyValue.Length == 2)
|
||||
{
|
||||
var key = Uri.UnescapeDataString(keyValue[0]);
|
||||
var value = Uri.UnescapeDataString(keyValue[1]);
|
||||
|
||||
if (key == "account_id")
|
||||
accountId = value;
|
||||
else if (key == "platform_id")
|
||||
platformId = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
httpRequest.Body.Position = 0;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
var accessToken = jwtService.GenerateToken(accountId, platformId, platform);
|
||||
|
||||
var response = new
|
||||
{
|
||||
access_token = accessToken,
|
||||
expires_in = 3600,
|
||||
token_type = "Bearer",
|
||||
refresh_token = Guid.NewGuid().ToString("N").ToUpper() + "-1",
|
||||
scope = "offline_access profile rn rn.accounts rn.accounts.gc rn.api rn.chat rn.clubs rn.commerce rn.match.read rn.match.write rn.notify rn.rooms rn.storage",
|
||||
key = "8oQ+e+WQaOBPbEcakhqs3dwZZdOmmyDUmJSD9u4AHMY="
|
||||
};
|
||||
|
||||
return Results.Json(response);
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Host=localhost;Port=5432;Database=cannednet;Username=postgres;Password=postgres"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Data Source=game.db;Cache=Shared"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
{
|
||||
"LevelProgressionMaps": [
|
||||
{
|
||||
"Level": 0,
|
||||
"RequiredXp": 0,
|
||||
"GiftRarity": -1
|
||||
},
|
||||
{
|
||||
"Level": 1,
|
||||
"RequiredXp": 10,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 2,
|
||||
"RequiredXp": 10,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 3,
|
||||
"RequiredXp": 10,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 4,
|
||||
"RequiredXp": 20,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 5,
|
||||
"RequiredXp": 20,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 6,
|
||||
"RequiredXp": 20,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 7,
|
||||
"RequiredXp": 20,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 8,
|
||||
"RequiredXp": 20,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 9,
|
||||
"RequiredXp": 20,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 10,
|
||||
"RequiredXp": 20,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 11,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 12,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 13,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 14,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 10
|
||||
},
|
||||
{
|
||||
"Level": 15,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 16,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 17,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 18,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 19,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 20,
|
||||
"RequiredXp": 45,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 21,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 22,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 23,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 24,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 25,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 26,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 27,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 28,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 29,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 30,
|
||||
"RequiredXp": 115,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 31,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 32,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 33,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 34,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 35,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 36,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 37,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 38,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 39,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 20
|
||||
},
|
||||
{
|
||||
"Level": 40,
|
||||
"RequiredXp": 360,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 41,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 42,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 43,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 44,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 45,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 46,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 47,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 48,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 49,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 30
|
||||
},
|
||||
{
|
||||
"Level": 50,
|
||||
"RequiredXp": 1080,
|
||||
"GiftRarity": 50
|
||||
}
|
||||
],
|
||||
"DailyObjectives": [
|
||||
[
|
||||
{
|
||||
"type": 1030,
|
||||
"score": 1
|
||||
},
|
||||
{
|
||||
"type": 1033,
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"type": 2000,
|
||||
"score": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": 601,
|
||||
"score": 2
|
||||
},
|
||||
{
|
||||
"type": 31,
|
||||
"score": 1
|
||||
},
|
||||
{
|
||||
"type": 6,
|
||||
"score": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": 301,
|
||||
"score": 2
|
||||
},
|
||||
{
|
||||
"type": 300,
|
||||
"score": 2
|
||||
},
|
||||
{
|
||||
"type": 6,
|
||||
"score": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": 700,
|
||||
"score": 2
|
||||
},
|
||||
{
|
||||
"type": 701,
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"type": 31,
|
||||
"score": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": 1010,
|
||||
"score": 1
|
||||
},
|
||||
{
|
||||
"type": 1013,
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"type": 6,
|
||||
"score": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": 500,
|
||||
"score": 2
|
||||
},
|
||||
{
|
||||
"type": 502,
|
||||
"score": 20
|
||||
},
|
||||
{
|
||||
"type": 2000,
|
||||
"score": 1
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"type": 1020,
|
||||
"score": 1
|
||||
},
|
||||
{
|
||||
"type": 1023,
|
||||
"score": 10
|
||||
},
|
||||
{
|
||||
"type": 31,
|
||||
"score": 1
|
||||
}
|
||||
]
|
||||
],
|
||||
"ServerMaintenance": {
|
||||
"StartsInMinutes": 0
|
||||
},
|
||||
"AutoMicMutingConfig": {
|
||||
"MicSpamVolumeThreshold": 0.75,
|
||||
"MicVolumeSampleInterval": 0.25,
|
||||
"MicVolumeSampleRollingWindowLength": 10,
|
||||
"MicSpamSamplePercentageForWarning": 0.8,
|
||||
"MicSpamSamplePercentageForWarningToEnd": 0.2,
|
||||
"MicSpamSamplePercentageForForceMute": 0.8,
|
||||
"MicSpamSamplePercentageForForceMuteToEnd": 0.2,
|
||||
"MicSpamWarningStateVolumeMultiplier": 0.25
|
||||
},
|
||||
"ShareBaseUrl": "http://localhost:5000/{0}"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
"hi :3"
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"Auth": "https://rr.lapis.codes",
|
||||
"API": "https://rr.lapis.codes",
|
||||
"WWW": "https://www.lapis.codes",
|
||||
"Notifications": "https://rr.lapis.codes",
|
||||
"Images": "https://rr.lapis.codes",
|
||||
"CDN": "https://rr.lapis.codes",
|
||||
"Commerce": "https://rr.lapis.codes",
|
||||
"Matchmaking": "https://rr.lapis.codes",
|
||||
"Storage": "https://rr.lapis.codes",
|
||||
"Chat": "https://rr.lapis.codes",
|
||||
"Leaderboard": "https://rr.lapis.codes",
|
||||
"Accounts": "https://rr.lapis.codes",
|
||||
"Link": "https://rr.lapis.codes",
|
||||
"RoomComments": "https://rr.lapis.codes",
|
||||
"Clubs": "https://rr.lapis.codes",
|
||||
"Rooms": "https://rr.lapis.codes",
|
||||
"PlatformNotifications": "https://rr.lapis.codes",
|
||||
"Moderation": "https://rr.lapis.codes",
|
||||
"DataCollection": "https://rr.lapis.codes",
|
||||
"BugReporting": "https://rr.lapis.codes",
|
||||
"Discovery": "https://rr.lapis.codes",
|
||||
"PlayerSettings": "https://rr.lapis.codes",
|
||||
"Rendering": "https://rr.lapis.codes",
|
||||
"Launcher": "https://rr.lapis.codes"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"Auth": "https://auth.epicquest.live",
|
||||
"API": "https://api.epicquest.live",
|
||||
"WWW": "https://www.epicquest.live",
|
||||
"Notifications": "https://notify.epicquest.live",
|
||||
"Images": "https://img.epicquest.live",
|
||||
"CDN": "https://cdn.epicquest.live",
|
||||
"Commerce": "https://commerce.epicquest.live",
|
||||
"Matchmaking": "https://match.epicquest.live",
|
||||
"Storage": "https://storage.epicquest.live",
|
||||
"Chat": "https://chat.epicquest.live",
|
||||
"Leaderboard": "https://leaderboard.epicquest.live",
|
||||
"Accounts": "https://accounts.epicquest.live",
|
||||
"Link": "https://link.epicquest.live",
|
||||
"RoomComments": "https://roomcomments.epicquest.live",
|
||||
"Clubs": "https://clubs.epicquest.live",
|
||||
"Rooms": "https://rooms.epicquest.live",
|
||||
"PlatformNotifications": "https://api.epicquest.live",
|
||||
"Moderation": "https://api.epicquest.live",
|
||||
"DataCollection": "https://api.epicquest.live",
|
||||
"BugReporting": "https://api.epicquest.live",
|
||||
"Discovery": "https://api.epicquest.live",
|
||||
"PlayerSettings": "https://api.epicquest.live",
|
||||
"Rendering": "https://api.epicquest.live",
|
||||
"Launcher": "https://launcher.epicquest.live"
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
[
|
||||
{
|
||||
"Key": "Debug.Watchdog.AdditionalTraceFlagsForLoading",
|
||||
"Value": "None"
|
||||
},
|
||||
{
|
||||
"Key": "Debug.Watchdog.CorruptRoomDetectionProbability",
|
||||
"Value": "0"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Creative.Query",
|
||||
"Value": "#horror"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Creative.Title",
|
||||
"Value": "HORROR"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Featured.Query",
|
||||
"Value": ""
|
||||
},
|
||||
{
|
||||
"Key": "Door.Featured.Title",
|
||||
"Value": "Door"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Quests.Query",
|
||||
"Value": "#quest #recroomoriginal"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Quests.Title",
|
||||
"Value": "QUESTS"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Shooters.Query",
|
||||
"Value": "#shooters"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Shooters.Title",
|
||||
"Value": "SHOOTERS"
|
||||
},
|
||||
{
|
||||
"Key": "Door.Sports.Query",
|
||||
"Value": "#recroomoriginal #sports | #pvp #recroomoriginal "
|
||||
},
|
||||
{
|
||||
"Key": "Door.Sports.Title",
|
||||
"Value": "SPORTS & PVP"
|
||||
},
|
||||
{
|
||||
"Key": "Door.StuntRunner.Query",
|
||||
"Value": "#stuntrunner #community"
|
||||
},
|
||||
{
|
||||
"Key": "Door.StuntRunner.Title",
|
||||
"Value": "COMMUNITY"
|
||||
},
|
||||
{
|
||||
"Key": "EnableRoomResetLogs",
|
||||
"Value": "1"
|
||||
},
|
||||
{
|
||||
"Key": "FentReactor.Enabled",
|
||||
"Value": "True"
|
||||
},
|
||||
{
|
||||
"Key": "Friends.PostGamePromptUnderFriendCount",
|
||||
"Value": "10"
|
||||
},
|
||||
{
|
||||
"Key": "Friends.SuggestFriendCodeOnFriendsScreenCount",
|
||||
"Value": "25"
|
||||
},
|
||||
{
|
||||
"Key": "Growth.ShouldPromptForCreatorEmail",
|
||||
"Value": "true"
|
||||
},
|
||||
{
|
||||
"Key": "loadingNetworkTimeout",
|
||||
"Value": "180"
|
||||
},
|
||||
{
|
||||
"Key": "Marquee",
|
||||
"Value": "CannedNet"
|
||||
},
|
||||
{
|
||||
"Key": "RAC:CurrentChallenge",
|
||||
"Value": "{\"Challenge\":\"AQJCIIXfWaPue2PO75i5nM9nVCPTCbhjnQDUU94I+v//SrxkmVSoLfdEoY5Dp/BwBB+wgIh3kbNL5SPZk2KRQeDDVn+LC/EQoajdcxE59qSDYLM8G1bXued1vRv/Wlk0p5M+Ov2LrnNxS6r8uL2o7V3prYZzEOTrdculBU8rbq4=\",\"LastUpdated\":\"2026-01-15T01:18:34.167596Z\"}"
|
||||
},
|
||||
{
|
||||
"Key": "Rendering.DisableSrpBatcher",
|
||||
"Value": "true"
|
||||
},
|
||||
{
|
||||
"Key": "Rewards.SelectionTimeout",
|
||||
"Value": "1800"
|
||||
},
|
||||
{
|
||||
"Key": "Rewards.UseRewardSelection",
|
||||
"Value": "false"
|
||||
},
|
||||
{
|
||||
"Key": "RoomDetails.PhotoRollEnabled",
|
||||
"Value": "true"
|
||||
},
|
||||
{
|
||||
"Key": "RoomScaleMotionCheatDetector.MaxAllowedAverageHmdSpeed",
|
||||
"Value": "1024"
|
||||
},
|
||||
{
|
||||
"Key": "RoomScaleMotionCheatDetector.MeasurementPeriodInSeconds",
|
||||
"Value": "5"
|
||||
},
|
||||
{
|
||||
"Key": "RoomScaleMotionCheatDetector.MinHmdSpeedAboveTriggerCount",
|
||||
"Value": "1024"
|
||||
},
|
||||
{
|
||||
"Key": "RoomScaleMotionCheatDetector.ShouldBootToDormWhenDetected.Oculus",
|
||||
"Value": "false"
|
||||
},
|
||||
{
|
||||
"Key": "RoomScaleMotionCheatDetector.ShouldBootToDormWhenDetected.Steam",
|
||||
"Value": "false"
|
||||
},
|
||||
{
|
||||
"Key": "RoomScaleMotionCheatDetector.TriggerHmdSpeed",
|
||||
"Value": "1024"
|
||||
},
|
||||
{
|
||||
"Key": "runningNetworkTimeout",
|
||||
"Value": "30"
|
||||
},
|
||||
{
|
||||
"Key": "Screens.ForceVerification",
|
||||
"Value": "1"
|
||||
},
|
||||
{
|
||||
"Key": "splitTestHardOverrides",
|
||||
"Value": "GottaGoFastNUX_2020_09_04=Off;PlayMenuRRUI_2021_01_11=Off;RoomRecommendations_2020_05_06=On;"
|
||||
},
|
||||
{
|
||||
"Key": "splitTestSegmentProbabilities",
|
||||
"Value": ""
|
||||
},
|
||||
{
|
||||
"Key": "splitTestSoftOverrides",
|
||||
"Value": "FentReactorDebugGuiEnabled_2025_11_20=DeveloperOnly;"
|
||||
},
|
||||
{
|
||||
"Key": "SynchronizedField.RemoveDefaultEntries",
|
||||
"Value": "true"
|
||||
},
|
||||
{
|
||||
"Key": "UGC.ScreenSharing.HorizontalResolution",
|
||||
"Value": "360"
|
||||
},
|
||||
{
|
||||
"Key": "UGC.ScreenSharing.ImageCompressionQuality",
|
||||
"Value": "50"
|
||||
},
|
||||
{
|
||||
"Key": "UGC.ScreenSharing.RefreshHz",
|
||||
"Value": "15"
|
||||
},
|
||||
{
|
||||
"Key": "VR.ForceVerification",
|
||||
"Value": "1"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"RealtimeAppId": "038c1828-7979-4735-8d30-50c3276c30a7",
|
||||
"VoiceAppId": "f6a30374-c1ce-4ea5-8919-45bc29203bc9",
|
||||
"ChatAppId": "f6a30374-c1ce-4ea5-8919-45bc29203bc9",
|
||||
"HostingOption": 1,
|
||||
"Protocol": 0,
|
||||
"ServerAddress": "127.0.0.1",
|
||||
"ServerPort": -1,
|
||||
"VoiceServerPort": -1,
|
||||
"ChatServerPort": -1,
|
||||
"NetworkLogging": 0,
|
||||
"PunLogging": 0
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<Solution>
|
||||
<Folder Name="/Solution Items/" />
|
||||
<Project Path="CannedNet.API/CannedNet.API.csproj" />
|
||||
</Solution>
|
||||
Reference in New Issue
Block a user