180 lines
5.8 KiB
C#
180 lines
5.8 KiB
C#
using API.Data;
|
|
using API.GraphQL;
|
|
using API.Services;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Model;
|
|
using Npgsql;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Register HTTP Context Accessor and Token Service
|
|
builder.Services.AddHttpContextAccessor();
|
|
builder.Services.AddSingleton<ITokenService, TokenService>();
|
|
|
|
// Configure CORS for Web / Blazor WebAssembly client
|
|
builder.Services.AddCors(options =>
|
|
{
|
|
options.AddDefaultPolicy(policy =>
|
|
{
|
|
policy.AllowAnyOrigin()
|
|
.AllowAnyHeader()
|
|
.AllowAnyMethod();
|
|
});
|
|
});
|
|
|
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
|
?? builder.Configuration.GetConnectionString("Postgres")
|
|
?? Environment.GetEnvironmentVariable("POSTGRES_CONNECTION_STRING");
|
|
|
|
var envPassword = Environment.GetEnvironmentVariable("PostgreSQL_Password")
|
|
?? (OperatingSystem.IsWindows()
|
|
? Environment.GetEnvironmentVariable("PostgreSQL_Password", EnvironmentVariableTarget.User)
|
|
: null)
|
|
?? (OperatingSystem.IsWindows()
|
|
? Environment.GetEnvironmentVariable("PostgreSQL_Password", EnvironmentVariableTarget.Machine)
|
|
: null)
|
|
?? Environment.GetEnvironmentVariable("POSTGRESQL_PASSWORD")
|
|
?? Environment.GetEnvironmentVariable("POSTGRES_PASSWORD");
|
|
|
|
if (!string.IsNullOrEmpty(connectionString))
|
|
{
|
|
if (!string.IsNullOrEmpty(envPassword))
|
|
try
|
|
{
|
|
var csb = new NpgsqlConnectionStringBuilder(connectionString)
|
|
{
|
|
Password = envPassword
|
|
};
|
|
connectionString = csb.ConnectionString;
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
else if (!string.IsNullOrEmpty(envPassword))
|
|
{
|
|
var csb = new NpgsqlConnectionStringBuilder
|
|
{
|
|
Host = "localhost",
|
|
Database = "chrono",
|
|
Username = "postgres",
|
|
Password = envPassword
|
|
};
|
|
connectionString = csb.ConnectionString;
|
|
}
|
|
|
|
builder.Services.AddDbContext<AppDbContext>(options =>
|
|
{
|
|
if (!string.IsNullOrEmpty(connectionString))
|
|
options.UseNpgsql(connectionString);
|
|
else
|
|
options.UseInMemoryDatabase("chrono");
|
|
});
|
|
|
|
// Register Hot Chocolate GraphQL Server with Nitro GraphQL IDE support
|
|
builder.Services
|
|
.AddGraphQLServer()
|
|
.AddQueryType<Query>()
|
|
.AddMutationType<Mutation>()
|
|
.ModifyRequestOptions(opt => opt.IncludeExceptionDetails = builder.Environment.IsDevelopment());
|
|
|
|
var app = builder.Build();
|
|
|
|
using (var scope = app.Services.CreateScope())
|
|
{
|
|
try
|
|
{
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
if (!string.IsNullOrEmpty(connectionString) && db.Database.IsNpgsql())
|
|
db.Database.Migrate();
|
|
else
|
|
db.Database.EnsureCreated();
|
|
|
|
// Seed default users if empty
|
|
if (!db.Users.Any())
|
|
{
|
|
db.Users.AddRange(
|
|
new User { Username = "admin", Password = "password123", CreatedAt = DateTime.UtcNow },
|
|
new User { Username = "testuser", Password = "password123", CreatedAt = DateTime.UtcNow },
|
|
new User { Username = "player1", Password = "password123", CreatedAt = DateTime.UtcNow }
|
|
);
|
|
db.SaveChanges();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[WARNING] Database initialization failed: {ex.Message}. Continuing without DB connection.");
|
|
}
|
|
}
|
|
|
|
app.UseCors();
|
|
|
|
// Map Hot Chocolate GraphQL endpoint and Nitro GraphQL Debugger
|
|
app.MapGraphQL();
|
|
|
|
app.MapGet("/", () => "Chrono CCG GraphQL API");
|
|
|
|
// REST Endpoints for Decks
|
|
app.MapGet("/api/decks", async (AppDbContext db) =>
|
|
{
|
|
var decks = await db.UserDecks.OrderByDescending(d => d.UpdatedAt).ToListAsync();
|
|
return Results.Ok(decks);
|
|
});
|
|
|
|
app.MapGet("/api/decks/{id:guid}", async (AppDbContext db, Guid id) =>
|
|
{
|
|
var deck = await db.UserDecks.FindAsync(id);
|
|
return deck != null ? Results.Ok(deck) : Results.NotFound();
|
|
});
|
|
|
|
app.MapPost("/api/decks", async (AppDbContext db, UserDeck input) =>
|
|
{
|
|
var existing = await db.UserDecks.FindAsync(input.Id);
|
|
if (existing == null)
|
|
{
|
|
input.UpdatedAt = DateTime.UtcNow;
|
|
db.UserDecks.Add(input);
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(input);
|
|
}
|
|
existing.Name = input.Name;
|
|
existing.Cards = input.Cards ?? [];
|
|
existing.Divers = input.Divers ?? [];
|
|
existing.Notes = input.Notes;
|
|
existing.Season = input.Season;
|
|
existing.UpdatedAt = DateTime.UtcNow;
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(existing);
|
|
});
|
|
|
|
app.MapDelete("/api/decks/{id:guid}", async (AppDbContext db, Guid id) =>
|
|
{
|
|
var existing = await db.UserDecks.FindAsync(id);
|
|
if (existing != null)
|
|
{
|
|
db.UserDecks.Remove(existing);
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
return Results.NotFound();
|
|
});
|
|
|
|
// Authentication endpoints
|
|
app.MapPost("/api/auth/dev-login", async (AppDbContext db, ITokenService tokenService, HttpContext httpContext) =>
|
|
{
|
|
var user = await db.Users.FirstOrDefaultAsync(u => u.Username == "testuser");
|
|
if (user == null)
|
|
{
|
|
user = new User { Username = "testuser", Password = "password123", CreatedAt = DateTime.UtcNow };
|
|
db.Users.Add(user);
|
|
await db.SaveChangesAsync();
|
|
}
|
|
var token = tokenService.GenerateToken(user.Username);
|
|
httpContext.Response.Cookies.Append("chrono_auth_token", token, new CookieOptions { Path = "/", SameSite = SameSiteMode.Lax });
|
|
httpContext.Response.Cookies.Append("chrono_auth_user", user.Username, new CookieOptions { Path = "/", SameSite = SameSiteMode.Lax });
|
|
return Results.Ok(new { success = true, token, username = user.Username });
|
|
});
|
|
|
|
app.MapDelete("/api/auth/credentials", () => Results.Ok(new { success = true }));
|
|
|
|
app.Run(); |