current state

This commit is contained in:
2026-03-06 14:42:47 -05:00
parent c675430fb9
commit b25222cc82
10 changed files with 88 additions and 19 deletions

View File

@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACorsPolicyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F92505ca94c450e3c7516c94691f62dfebf5577d7f7cca72ab4e7742727633_003FCorsPolicyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fbf9021a960b74107a7e141aa06bc9d8a0a53c929178c2fb95b1597be8af8dc_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMonitor_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fbc69852c736be69a33ab75e0444246ffeb2f8cd671d12b36b764ba5fa18f61ba_003FMonitor_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APostgreSqlBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fcdd0beaf7beaf8366c0862f34fe40da30911084d957625ab31577851ee8cae7_003FPostgreSqlBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

View File

@@ -24,12 +24,14 @@ public class EventTrackingService(
public Task TrackClickAsync(Guid workspaceId, Guid shortLinkId, HttpContext context)
{
// Fire and forget - don't block the redirect
// Extract request data before the HttpContext is disposed
var requestData = CaptureRequestData(context);
_ = Task.Run(async () =>
{
try
{
await TrackEventInternalAsync(workspaceId, shortLinkId, null, EventType.Click, context);
await TrackEventInternalAsync(workspaceId, shortLinkId, null, EventType.Click, requestData);
}
catch (Exception ex)
{
@@ -42,12 +44,13 @@ public class EventTrackingService(
public Task TrackScanAsync(Guid workspaceId, Guid shortLinkId, Guid qrCodeId, HttpContext context)
{
// Fire and forget - don't block the redirect
var requestData = CaptureRequestData(context);
_ = Task.Run(async () =>
{
try
{
await TrackEventInternalAsync(workspaceId, shortLinkId, qrCodeId, EventType.Scan, context);
await TrackEventInternalAsync(workspaceId, shortLinkId, qrCodeId, EventType.Scan, requestData);
}
catch (Exception ex)
{
@@ -58,22 +61,25 @@ public class EventTrackingService(
return Task.CompletedTask;
}
private static RequestData CaptureRequestData(HttpContext context) => new(
IpAddress: GetClientIpAddress(context),
UserAgent: context.Request.Headers.UserAgent.ToString(),
Referrer: context.Request.Headers.Referer.ToString()
);
private async Task TrackEventInternalAsync(
Guid workspaceId,
Guid shortLinkId,
Guid? qrCodeId,
EventType eventType,
HttpContext context)
RequestData requestData)
{
logger.LogInformation("About to track something");
// Create a new scope for database access (since we're in a background task)
using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var ipAddress = GetClientIpAddress(context);
var userAgent = context.Request.Headers.UserAgent.ToString();
var referrer = context.Request.Headers.Referer.ToString();
var ipAddress = requestData.IpAddress;
var userAgent = requestData.UserAgent;
var referrer = requestData.Referrer;
var ipHash = HashIpAddress(ipAddress);
var deviceType = ParseDeviceType(userAgent);
@@ -167,4 +173,6 @@ public class EventTrackingService(
return value.Length <= maxLength ? value : value[..maxLength];
}
private sealed record RequestData(string IpAddress, string UserAgent, string Referrer);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 MiB

View File

@@ -41,10 +41,9 @@ try
{
if (builder.Environment.IsDevelopment())
{
policy.SetIsOriginAllowed(origin => new Uri(origin).IsLoopback)
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
.AllowAnyMethod();
}
else
{
@@ -161,6 +160,23 @@ try
var app = builder.Build();
// In development, override Request.Host with a configured public URL
// so QR codes encode LAN-accessible URLs instead of localhost
var publicUrl = app.Configuration["App:PublicUrl"];
if (app.Environment.IsDevelopment() && !string.IsNullOrEmpty(publicUrl))
{
var publicUri = new Uri(publicUrl);
app.Use(async (context, next) =>
{
context.Request.Scheme = publicUri.Scheme;
context.Request.Host = publicUri.IsDefaultPort
? new HostString(publicUri.Host)
: new HostString(publicUri.Host, publicUri.Port);
await next();
});
Log.Information("Public URL override: {PublicUrl}", publicUrl);
}
// Global error handling middleware (must be first)
app.UseMiddleware<GlobalExceptionMiddleware>();

View File

@@ -5,7 +5,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://127.0.0.1:42001",
"applicationUrl": "https://0.0.0.0:42001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}

View File

@@ -5,6 +5,9 @@
"Microsoft.AspNetCore": "Warning"
}
},
"App": {
"PublicUrl": "https://192.168.1.17:42001"
},
"ConnectionStrings": {
"PostgresConnection": "Host=localhost;Port=5400;Database=trakqr;Username=sa;Password=P@ssword123!"
},

View File

@@ -34,7 +34,7 @@
]
},
"GeoIP": {
"DatabasePath": ""
"DatabasePath": "./GeoIP/dbip-city-lite-2026-02.mmdb"
},
"Stripe": {
"SecretKey": "",

View File

@@ -28,11 +28,20 @@
<input
id="password"
v-model="password"
type="password"
:type="showPassword ? 'text' : 'password'"
autocomplete="current-password"
placeholder="Your password"
required
/>
<button
type="button"
class="password-toggle"
:aria-label="showPassword ? 'Hide password' : 'Show password'"
:aria-pressed="showPassword"
@click="showPassword = !showPassword"
>
{{ showPassword ? 'Hide' : 'Show' }}
</button>
</div>
<div v-if="authStore.error" class="error-message">
@@ -68,6 +77,8 @@ const authStore = useAuthStore();
const email = ref('');
const password = ref('');
const showPassword = ref(false);
const handleSubmit = async () => {
const success = await authStore.login(email.value, password.value);
if (success) {
@@ -207,4 +218,33 @@ const handleSubmit = async () => {
color: var(--accent);
font-weight: 500;
}
.password-field {
position: relative;
display: flex;
align-items: center;
}
.password-field input {
width: 100%;
padding-right: 72px; /* space for the button */
}
.password-toggle {
position: absolute;
right: 10px;
height: 32px;
padding: 0 10px;
border: 1px solid var(--line);
border-radius: 10px;
background: var(--surface);
color: var(--muted);
font-size: 0.85rem;
cursor: pointer;
}
.password-toggle:hover {
color: var(--accent);
border-color: var(--accent);
}
</style>

View File

@@ -162,7 +162,6 @@
</div>
<div v-else class="empty-state">
<p>No geographic data yet</p>
<p class="hint">Country detection requires a GeoIP database</p>
</div>
</section>
</div>

View File

@@ -4,11 +4,13 @@ import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
server: {
host: '0.0.0.0',
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:42001',
target: 'https://localhost:42001',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, '')
}
}