πŸ–₯️ Login Kodu β€” C# / ASP.NET Core (.NET 10)

Aşağıdaki kod, /login sayfasının C# muadilidir (Razor Pages)

πŸ“„ Login.cs (Program.cs'ye eklenecek Razor Pages kodu)

// ═══════════════════════════════════════════════════════════════════════
// Osman Website Login β€” C# / ASP.NET Core (.NET 10) sΓΌrΓΌmΓΌ
// Python (FastAPI) muadili: /opt/hava-durumu/app/main.py iΓ§indeki auth bΓΆlΓΌmΓΌ
// ═══════════════════════════════════════════════════════════════════════
//
// Program.cs iΓ§ine eklenecek kod. Gereksinimler:
//   builder.Services.AddDistributedMemoryCache();
//   builder.Services.AddSession(o => { o.Cookie.Name = "osman_session"; });
//   app.UseSession();
//
using System.Collections.Concurrent;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

public class LoginModel : PageModel
{
    // ── Ayarlar ─────────────────────────────────────────────────────────
    private const string SessionCookie = "osman_session";
    private const string TrustedIp = "90.247.57.179";   // ΓΆzel IP: kalΔ±cΔ± cookie + otomatik tanΔ±ma

    private static readonly Dictionary<string, string> Users = new()
    {
        ["u1"] = "p1",
        ["u2"] = "p2",
    };

    // token -> (username, expiresUtc). Thread-safe.
    private static readonly ConcurrentDictionary<string, (string Username, DateTime Expires)> Sessions = new();

    // ── Bağlam ──────────────────────────────────────────────────────────
    private readonly IHttpContextAccessor _http;
    public LoginModel(IHttpAccessor accessor) => _http = accessor;

    // ── View'a bağlanan ΓΆzellikler ──────────────────────────────────────
    public string? Username      { get; private set; }
    public string? ClientIp      { get; private set; }
    public string? IpSource      { get; private set; }
    public string? Error         { get; private set; }
    public string? RemainingText { get; private set; }
    public bool OneShot          { get; private set; }

    // ── YardΔ±mcΔ±lar ─────────────────────────────────────────────────────
    private (string Ip, string Source) GetClientIp()
    {
        var ctx = _http.HttpContext!;
        var xff = ctx.Request.Headers["X-Forwarded-For"].ToString();
        if (!string.IsNullOrWhiteSpace(xff))
            return (xff.Split(',')[0].Trim(), "X-Forwarded-For (Traefik proxy ΓΌzerinden)");
        return (ctx.Connection.RemoteIpAddress?.ToString() ?? "?", "Doğrudan bağlantı");
    }

    private static (TimeSpan Remaining, DateTime ExpiresLocal) UntilMidnight()
    {
        var tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Istanbul");
        var nowTr = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tz);
        var nextMidnight = nowTr.Date.AddDays(nowTr.TimeOfDay == TimeSpan.Zero ? 0 : 1);
        var remaining = nextMidnight - nowTr.TimeOfDay;
        return (remaining, nowTr.Date.Add(remaining));
    }

    private static string Fmt(TimeSpan t) => $"{(int)t.TotalHours} saat {t.Minutes} dakika";

    private string? CurrentUser()
    {
        var ctx = _http.HttpContext!;
        if (ctx.Request.Cookies.TryGetValue(SessionCookie, out var token) &&
            Sessions.TryGetValue(token, out var sess))
        {
            if (sess.Expires > DateTime.UtcNow)
                return sess.Username;
            Sessions.TryRemove(token, out _);   // sΓΌresi doldu, temizle
        }
        return null;
    }

    private string? SessionRemainingText()
    {
        var ctx = _http.HttpContext!;
        if (ctx.Request.Cookies.TryGetValue(SessionCookie, out var token) &&
            Sessions.TryGetValue(token, out var sess) && sess.Expires > DateTime.UtcNow)
        {
            return Fmt(sess.Expires - DateTime.UtcNow);
        }
        return null;
    }

    // ── GET /login ──────────────────────────────────────────────────────
    public void OnGet()
    {
        var (ip, source) = GetClientIp();
        ClientIp = ip;
        IpSource = source;
        Username = CurrentUser();
        RemainingText = Username is null ? null : SessionRemainingText();
    }

    // ── POST /login ─────────────────────────────────────────────────────
    public IActionResult OnPost(string username, string password)
    {
        var (ip, source) = GetClientIp();
        ClientIp = ip;
        IpSource = source;

        if (!Users.TryGetValue((username ?? "").Trim(), out var _p) || _p != password)
        {
            Error = "Kullanıcı adı veya şifre hatalı.";
            return Page();
        }

        var (remaining, _) = UntilMidnight();
        var token = Convert.ToHexString(RandomNumberGenerator.GetBytes(16));
        Sessions[token] = ((username ?? "").Trim(), DateTime.UtcNow + remaining);

        if (ip == TrustedIp)
        {
            // KalΔ±cΔ± cookie: gece yarΔ±sΔ±na kadar, tarayΔ±cΔ± kapansa bile otomatik tanΔ±nΔ±r
            Response.Cookies.Append(SessionCookie, token, new CookieOptions
            {
                HttpOnly = true,
                SameSite = SameSiteMode.Lax,
                MaxAge = remaining,
            });
            Console.WriteLine($"[AUTH] Trusted IP {ip} iΓ§in kalΔ±cΔ± cookie (user={username}, {remaining})");
            return RedirectToPage();
        }
        else
        {
            // Diğer IP'ler: cookie YOK, kalan sΓΌre YOK β€” tek seferlik giriş ekranΔ±
            Username = (username ?? "").Trim();
            OneShot = true;
            Console.WriteLine($"[AUTH] {ip} trusted değil, cookie yok, tek seferlik giriş ekranı (user={username})");
            return Page();
        }
    }

    // ── POST /logout ────────────────────────────────────────────────────
    public IActionResult OnPostLogout()
    {
        if (Request.Cookies.TryGetValue(SessionCookie, out var token))
            Sessions.TryRemove(token, out _);
        Response.Cookies.Delete(SessionCookie);
        return RedirectToPage();
    }
}

βš™οΈ Kurulum notlarΔ±

  • builder.Services.AddDistributedMemoryCache(); ve AddSession() gerekli
  • Cookie mantığı Python sΓΌrΓΌmΓΌyle birebir: ΓΆzel IP'ye kalΔ±cΔ± cookie, diğerlerine tek seferlik ekran
  • Oturumlar bellek iΓ§i (restart'ta sΔ±fΔ±rlanΔ±r)