-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
164 lines (135 loc) · 6.42 KB
/
Program.cs
File metadata and controls
164 lines (135 loc) · 6.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
using Microsoft.EntityFrameworkCore;
using BetterX_API.Data;
using BetterX_API.Models;
using BetterX_API.Services;
// Records declared before the main code
namespace BetterX_API;
public record UserCreate(string Username, string Token);
public record AppUpdate(string Version, bool UsedMoreThanHour);
public record LocationUpdate(string Country);
public record StatusUpdate(UserStatus Status);
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Configure Kestrel to listen on all interfaces
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5000);
});
// Configure SQLite with absolute path to ensure location
var folder = Path.Combine(Environment.CurrentDirectory, "Data");
Directory.CreateDirectory(folder);
var dbPath = Path.Combine(folder, "betterx.db");
builder.Services.AddDbContext<ApiDbContext>(opt =>
opt.UseSqlite($"Data Source={dbPath}"));
// Add heartbeat service
builder.Services.AddHostedService<HeartbeatService>();
// Add TwitterAuthService to DI container
builder.Services.AddScoped<TwitterAuthService>();
var app = builder.Build();
// Add support for static files
app.UseStaticFiles();
// Create database on startup if it doesn't exist
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<ApiDbContext>();
db.Database.EnsureCreated();
}
// Check if user exists
app.MapGet("/users/{username}", async (string username, ApiDbContext db) =>
await db.Users.AnyAsync(u => u.Username == username)
? Results.Ok(new { exists = true })
: Results.Ok(new { exists = false }));
// Add a new user
app.MapPost("/users", async (UserCreate user, ApiDbContext db) =>
{
if (await db.Users.AnyAsync(u => u.Username == user.Username))
return Results.BadRequest("User already exists");
var newUser = new User { Username = user.Username, Token = user.Token };
db.Users.Add(newUser);
await db.SaveChangesAsync();
return Results.Created($"/users/{user.Username}", newUser);
});
// Get user status
app.MapGet("/users/{username}/status", async (string username, ApiDbContext db) =>
{
var user = await db.Users.FirstOrDefaultAsync(u => u.Username == username);
return user == null ? Results.NotFound() : Results.Ok(new { status = user.Status });
});
// Update user status
app.MapPut("/users/{username}/status", async (string username, string token, StatusUpdate update, ApiDbContext db) =>
{
var user = await db.Users.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return Results.NotFound();
if (user.Token != token) return Results.Unauthorized();
user.Status = update.Status;
await db.SaveChangesAsync();
return Results.Ok();
});
// Update app version and usage
app.MapPut("/users/{username}/app-update", async (string username, string token, AppUpdate update, ApiDbContext db) =>
{
var user = await db.Users.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return Results.NotFound();
if (user.Token != token) return Results.Unauthorized();
user.AppVersion = update.Version;
user.UsedMoreThanHour = update.UsedMoreThanHour;
await db.SaveChangesAsync();
return Results.Ok();
});
// Save installation country
app.MapPut("/users/{username}/location", async (string username, string token, LocationUpdate location, ApiDbContext db) =>
{
var user = await db.Users.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return Results.NotFound();
if (user.Token != token) return Results.Unauthorized();
user.Country = location.Country;
await db.SaveChangesAsync();
return Results.Ok();
});
// Add heartbeat endpoint
app.MapPost("/users/{username}/heartbeat", async (string username, string token, ApiDbContext db) =>
{
var user = await db.Users
.AsNoTracking() // Optimisation pour la lecture seule
.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return Results.NotFound();
if (user.Token != token) return Results.Unauthorized();
// Mise à jour avec suivi explicite
var userToUpdate = await db.Users.FindAsync(user.Id);
if (userToUpdate != null)
{
userToUpdate.LastHeartbeat = DateTime.UtcNow;
await db.SaveChangesAsync();
}
return Results.Ok();
});
// Add callback endpoint
app.MapGet("/callback", (string oauth_token, string oauth_verifier) =>
{
return Results.Content(
File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "wwwroot", "callback.html"))
.Replace("{{VERIFIER}}", oauth_verifier),
"text/html");
});
// Get connect request URL
app.MapGet("/connect-request", async (TwitterAuthService twitter) =>
{
var result = await twitter.GetRequestTokenAsync();
return result == null
? Results.BadRequest("Failed to get authorization URL")
: Results.Ok(new { auth_url = result.Value.AuthUrl, token = result.Value.Token });
});
// Get access token
app.MapGet("/get-token", async (string oauth_token, string oauth_verifier, TwitterAuthService twitter) =>
{
var accessToken = await twitter.GetAccessTokenAsync(oauth_token, oauth_verifier);
return accessToken == null
? Results.BadRequest("Failed to get access token")
: Results.Ok(accessToken);
});
app.Run();
}
}