mirror of
https://github.com/chylex/Discord-History-Tracker.git
synced 2024-11-25 05:42:45 +01:00
Compare commits
1 Commits
4b1afa7aa3
...
0d5e01532e
Author | SHA1 | Date | |
---|---|---|---|
0d5e01532e |
@ -36,69 +36,18 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
gui.scrollMessagesToTop();
|
||||
});
|
||||
|
||||
async function fetchUrl(path, contentType) {
|
||||
const response = await fetch("/" + path + "?token=" + encodeURIComponent(window.DHT_SERVER_TOKEN) + "&session=" + encodeURIComponent(window.DHT_SERVER_SESSION), {
|
||||
async function loadData() {
|
||||
try {
|
||||
const response = await fetch("/get-viewer-data?token=" + encodeURIComponent(window.DHT_SERVER_TOKEN) + "&session=" + encodeURIComponent(window.DHT_SERVER_SESSION), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": contentType,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "omit",
|
||||
redirect: "error",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw "Unexpected response status: " + response.statusText;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function processLines(response, callback) {
|
||||
let body = "";
|
||||
|
||||
for await (const chunk of response.body.pipeThrough(new TextDecoderStream("utf-8"))) {
|
||||
body += chunk;
|
||||
|
||||
let startIndex = 0;
|
||||
|
||||
while (true) {
|
||||
const endIndex = body.indexOf("\n", startIndex);
|
||||
if (endIndex === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
callback(body.substring(startIndex, endIndex));
|
||||
startIndex = endIndex + 1;
|
||||
}
|
||||
|
||||
body = body.substring(startIndex);
|
||||
}
|
||||
|
||||
if (body !== "") {
|
||||
callback(body);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
const metadataResponse = await fetchUrl("get-viewer-metadata", "application/json");
|
||||
const metadataJson = await metadataResponse.json();
|
||||
|
||||
const messagesResponse = await fetchUrl("get-viewer-messages", "application/x-ndjson");
|
||||
const messages = {};
|
||||
|
||||
await processLines(messagesResponse, line => {
|
||||
const message = JSON.parse(line);
|
||||
const channel = message.c;
|
||||
|
||||
const channelMessages = messages[channel] || (messages[channel] = {});
|
||||
channelMessages[message.id] = message;
|
||||
|
||||
delete message.id;
|
||||
delete message.c;
|
||||
});
|
||||
|
||||
state.uploadFile(metadataJson, messages);
|
||||
state.uploadFile(await response.json());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert("Could not load data, see console for details.");
|
||||
|
@ -6,7 +6,8 @@ export default (function() {
|
||||
/**
|
||||
* @type {{}}
|
||||
* @property {{}} users
|
||||
* @property {{}} servers
|
||||
* @property {String[]} userindex
|
||||
* @property {{}[]} servers
|
||||
* @property {{}} channels
|
||||
*/
|
||||
let loadedFileMeta;
|
||||
@ -19,16 +20,20 @@ export default (function() {
|
||||
let currentPage;
|
||||
let messagesPerPage;
|
||||
|
||||
const getUser = function(id) {
|
||||
return loadedFileMeta.users[id] || { "name": "<unknown>" };
|
||||
const getUser = function(index) {
|
||||
return loadedFileMeta.users[loadedFileMeta.userindex[index]] || { "name": "<unknown>" };
|
||||
};
|
||||
|
||||
const getUserId = function(index) {
|
||||
return loadedFileMeta.userindex[index];
|
||||
};
|
||||
|
||||
const getUserList = function() {
|
||||
return loadedFileMeta ? loadedFileMeta.users : [];
|
||||
};
|
||||
|
||||
const getServer = function(id) {
|
||||
return loadedFileMeta.servers[id] || { "name": "<unknown>", "type": "unknown" };
|
||||
const getServer = function(index) {
|
||||
return loadedFileMeta.servers[index] || { "name": "<unknown>", "type": "unknown" };
|
||||
};
|
||||
|
||||
const generateChannelHierarchy = function() {
|
||||
@ -202,7 +207,7 @@ export default (function() {
|
||||
*/
|
||||
const message = messages[key];
|
||||
const user = getUser(message.u);
|
||||
const avatar = user.avatar ? { id: message.u, path: user.avatar } : null;
|
||||
const avatar = user.avatar ? { id: getUserId(message.u), path: user.avatar } : null;
|
||||
|
||||
const obj = {
|
||||
user,
|
||||
@ -230,7 +235,7 @@ export default (function() {
|
||||
if ("r" in message) {
|
||||
const replyMessage = getMessageById(message.r);
|
||||
const replyUser = replyMessage ? getUser(replyMessage.u) : null;
|
||||
const replyAvatar = replyUser && replyUser.avatar ? { id: replyMessage.u, path: replyUser.avatar } : null;
|
||||
const replyAvatar = replyUser && replyUser.avatar ? { id: getUserId(replyMessage.u), path: replyUser.avatar } : null;
|
||||
|
||||
obj["reply"] = replyMessage ? {
|
||||
"id": message.r,
|
||||
@ -288,17 +293,20 @@ export default (function() {
|
||||
eventOnUsersRefreshed = callback;
|
||||
},
|
||||
|
||||
uploadFile(meta, data) {
|
||||
/**
|
||||
* @param {{ meta, data }} file
|
||||
*/
|
||||
uploadFile(file) {
|
||||
if (loadedFileMeta != null) {
|
||||
throw "A file is already loaded!";
|
||||
}
|
||||
|
||||
if (typeof meta !== "object" || typeof data !== "object") {
|
||||
if (!file || typeof file.meta !== "object" || typeof file.data !== "object") {
|
||||
throw "Invalid file format!";
|
||||
}
|
||||
|
||||
loadedFileMeta = meta;
|
||||
loadedFileData = data;
|
||||
loadedFileMeta = file.meta;
|
||||
loadedFileData = file.data;
|
||||
loadedMessages = null;
|
||||
|
||||
selectedChannel = null;
|
||||
|
@ -1,5 +1,3 @@
|
||||
namespace DHT.Server.Database.Export;
|
||||
|
||||
readonly record struct Snowflake(ulong Id) {
|
||||
public static implicit operator Snowflake(ulong id) => new (id);
|
||||
}
|
||||
readonly record struct Snowflake(ulong Id);
|
||||
|
@ -3,10 +3,14 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace DHT.Server.Database.Export;
|
||||
|
||||
static class ViewerJson {
|
||||
sealed class ViewerJson {
|
||||
public required JsonMeta Meta { get; init; }
|
||||
public required Dictionary<Snowflake, Dictionary<Snowflake, JsonMessage>> Data { get; init; }
|
||||
|
||||
public sealed class JsonMeta {
|
||||
public required Dictionary<Snowflake, JsonUser> Users { get; init; }
|
||||
public required Dictionary<Snowflake, JsonServer> Servers { get; init; }
|
||||
public required List<Snowflake> Userindex { get; init; }
|
||||
public required List<JsonServer> Servers { get; init; }
|
||||
public required Dictionary<Snowflake, JsonChannel> Channels { get; init; }
|
||||
}
|
||||
|
||||
@ -26,7 +30,7 @@ static class ViewerJson {
|
||||
}
|
||||
|
||||
public sealed class JsonChannel {
|
||||
public required Snowflake Server { get; init; }
|
||||
public required int Server { get; init; }
|
||||
public required string Name { get; init; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
@ -43,9 +47,7 @@ static class ViewerJson {
|
||||
}
|
||||
|
||||
public sealed class JsonMessage {
|
||||
public required Snowflake Id { get; init; }
|
||||
public required Snowflake C { get; init; }
|
||||
public required Snowflake U { get; init; }
|
||||
public required int U { get; init; }
|
||||
public required long T { get; init; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
|
@ -7,5 +7,5 @@ namespace DHT.Server.Database.Export;
|
||||
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
||||
GenerationMode = JsonSourceGenerationMode.Default
|
||||
)]
|
||||
[JsonSerializable(typeof(ViewerJson.JsonMeta))]
|
||||
sealed partial class ViewerJsonMetadataContext : JsonSerializerContext;
|
||||
[JsonSerializable(typeof(ViewerJson))]
|
||||
sealed partial class ViewerJsonContext : JsonSerializerContext;
|
@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -15,90 +14,106 @@ namespace DHT.Server.Database.Export;
|
||||
static class ViewerJsonExport {
|
||||
private static readonly Log Log = Log.ForType(typeof(ViewerJsonExport));
|
||||
|
||||
public static async Task GetMetadata(Stream stream, IDatabaseFile db, MessageFilter? filter = null, CancellationToken cancellationToken = default) {
|
||||
public static async Task Generate(Stream stream, IDatabaseFile db, MessageFilter? filter = null, CancellationToken cancellationToken = default) {
|
||||
var perf = Log.Start();
|
||||
|
||||
var includedChannels = new List<Channel>();
|
||||
var includedUserIds = new HashSet<ulong>();
|
||||
var includedChannelIds = new HashSet<ulong>();
|
||||
var includedServerIds = new HashSet<ulong>();
|
||||
|
||||
var channelIdFilter = filter?.ChannelIds;
|
||||
var includedMessages = await db.Messages.Get(filter, cancellationToken).ToListAsync(cancellationToken);
|
||||
var includedChannels = new List<Channel>();
|
||||
|
||||
foreach (var message in includedMessages) {
|
||||
includedUserIds.Add(message.Sender);
|
||||
includedChannelIds.Add(message.Channel);
|
||||
}
|
||||
|
||||
await foreach (var channel in db.Channels.Get(cancellationToken)) {
|
||||
if (channelIdFilter == null || channelIdFilter.Contains(channel.Id)) {
|
||||
if (includedChannelIds.Contains(channel.Id)) {
|
||||
includedChannels.Add(channel);
|
||||
includedServerIds.Add(channel.Server);
|
||||
}
|
||||
}
|
||||
|
||||
var users = await GenerateUserList(db, cancellationToken);
|
||||
var servers = await GenerateServerList(db, includedServerIds, cancellationToken);
|
||||
var channels = GenerateChannelList(includedChannels);
|
||||
|
||||
var meta = new ViewerJson.JsonMeta {
|
||||
Users = users,
|
||||
Servers = servers,
|
||||
Channels = channels
|
||||
};
|
||||
var (users, userIndex, userIndices) = await GenerateUserList(db, includedUserIds);
|
||||
var (servers, serverIndices) = await GenerateServerList(db, includedServerIds);
|
||||
var channels = GenerateChannelList(includedChannels, serverIndices);
|
||||
|
||||
perf.Step("Collect database data");
|
||||
|
||||
await JsonSerializer.SerializeAsync(stream, meta, ViewerJsonMetadataContext.Default.JsonMeta, cancellationToken);
|
||||
var value = new ViewerJson {
|
||||
Meta = new ViewerJson.JsonMeta {
|
||||
Users = users,
|
||||
Userindex = userIndex,
|
||||
Servers = servers,
|
||||
Channels = channels
|
||||
},
|
||||
Data = GenerateMessageList(includedMessages, userIndices)
|
||||
};
|
||||
|
||||
perf.Step("Generate value object");
|
||||
|
||||
await JsonSerializer.SerializeAsync(stream, value, ViewerJsonContext.Default.ViewerJson, cancellationToken);
|
||||
|
||||
perf.Step("Serialize to JSON");
|
||||
perf.End();
|
||||
}
|
||||
|
||||
public static async Task GetMessages(Stream stream, IDatabaseFile db, MessageFilter? filter = null, CancellationToken cancellationToken = default) {
|
||||
var perf = Log.Start();
|
||||
|
||||
ReadOnlyMemory<byte> newLine = "\n"u8.ToArray();
|
||||
|
||||
await foreach(var message in GenerateMessageList(db, filter, cancellationToken)) {
|
||||
await JsonSerializer.SerializeAsync(stream, message, ViewerJsonMessageContext.Default.JsonMessage, cancellationToken);
|
||||
await stream.WriteAsync(newLine, cancellationToken);
|
||||
}
|
||||
|
||||
perf.Step("Generate and serialize messages to JSON");
|
||||
perf.End();
|
||||
}
|
||||
|
||||
private static async Task<Dictionary<Snowflake, ViewerJson.JsonUser>> GenerateUserList(IDatabaseFile db, CancellationToken cancellationToken) {
|
||||
private static async Task<(Dictionary<Snowflake, ViewerJson.JsonUser> Users, List<Snowflake> UserIndex, Dictionary<ulong, int> UserIndices)> GenerateUserList(IDatabaseFile db, HashSet<ulong> userIds) {
|
||||
var users = new Dictionary<Snowflake, ViewerJson.JsonUser>();
|
||||
var userIndex = new List<Snowflake>();
|
||||
var userIndices = new Dictionary<ulong, int>();
|
||||
|
||||
await foreach (var user in db.Users.Get(cancellationToken)) {
|
||||
users[user.Id] = new ViewerJson.JsonUser {
|
||||
await foreach (var user in db.Users.Get()) {
|
||||
var id = user.Id;
|
||||
if (!userIds.Contains(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var idSnowflake = new Snowflake(id);
|
||||
userIndices[id] = users.Count;
|
||||
userIndex.Add(idSnowflake);
|
||||
|
||||
users[idSnowflake] = new ViewerJson.JsonUser {
|
||||
Name = user.Name,
|
||||
Avatar = user.AvatarUrl,
|
||||
Tag = user.Discriminator
|
||||
};
|
||||
}
|
||||
|
||||
return users;
|
||||
return (users, userIndex, userIndices);
|
||||
}
|
||||
|
||||
private static async Task<Dictionary<Snowflake, ViewerJson.JsonServer>> GenerateServerList(IDatabaseFile db, HashSet<ulong> serverIds, CancellationToken cancellationToken) {
|
||||
var servers = new Dictionary<Snowflake, ViewerJson.JsonServer>();
|
||||
private static async Task<(List<ViewerJson.JsonServer> Servers, Dictionary<ulong, int> ServerIndices)> GenerateServerList(IDatabaseFile db, HashSet<ulong> serverIds) {
|
||||
var servers = new List<ViewerJson.JsonServer>();
|
||||
var serverIndices = new Dictionary<ulong, int>();
|
||||
|
||||
await foreach (var server in db.Servers.Get(cancellationToken)) {
|
||||
if (!serverIds.Contains(server.Id)) {
|
||||
await foreach (var server in db.Servers.Get()) {
|
||||
var id = server.Id;
|
||||
if (!serverIds.Contains(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
servers[server.Id] = new ViewerJson.JsonServer {
|
||||
serverIndices[id] = servers.Count;
|
||||
|
||||
servers.Add(new ViewerJson.JsonServer {
|
||||
Name = server.Name,
|
||||
Type = ServerTypes.ToJsonViewerString(server.Type)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return servers;
|
||||
return (servers, serverIndices);
|
||||
}
|
||||
|
||||
private static Dictionary<Snowflake, ViewerJson.JsonChannel> GenerateChannelList(List<Channel> includedChannels) {
|
||||
private static Dictionary<Snowflake, ViewerJson.JsonChannel> GenerateChannelList(List<Channel> includedChannels, Dictionary<ulong, int> serverIndices) {
|
||||
var channels = new Dictionary<Snowflake, ViewerJson.JsonChannel>();
|
||||
|
||||
foreach (var channel in includedChannels) {
|
||||
channels[channel.Id] = new ViewerJson.JsonChannel {
|
||||
Server = channel.Server,
|
||||
var channelIdSnowflake = new Snowflake(channel.Id);
|
||||
|
||||
channels[channelIdSnowflake] = new ViewerJson.JsonChannel {
|
||||
Server = serverIndices[channel.Server],
|
||||
Name = channel.Name,
|
||||
Parent = channel.ParentId?.ToString(),
|
||||
Position = channel.Position,
|
||||
@ -110,12 +125,18 @@ static class ViewerJsonExport {
|
||||
return channels;
|
||||
}
|
||||
|
||||
private static async IAsyncEnumerable<ViewerJson.JsonMessage> GenerateMessageList(IDatabaseFile db, MessageFilter? filter, [EnumeratorCancellation] CancellationToken cancellationToken) {
|
||||
await foreach (var message in db.Messages.Get(filter, cancellationToken)) {
|
||||
yield return new ViewerJson.JsonMessage {
|
||||
Id = message.Id,
|
||||
C = message.Channel,
|
||||
U = message.Sender,
|
||||
private static Dictionary<Snowflake, Dictionary<Snowflake, ViewerJson.JsonMessage>> GenerateMessageList(List<Message> includedMessages, Dictionary<ulong, int> userIndices) {
|
||||
var data = new Dictionary<Snowflake, Dictionary<Snowflake, ViewerJson.JsonMessage>>();
|
||||
|
||||
foreach (var grouping in includedMessages.GroupBy(static message => message.Channel)) {
|
||||
var channelIdSnowflake = new Snowflake(grouping.Key);
|
||||
var channelData = new Dictionary<Snowflake, ViewerJson.JsonMessage>();
|
||||
|
||||
foreach (var message in grouping) {
|
||||
var messageIdSnowflake = new Snowflake(message.Id);
|
||||
|
||||
channelData[messageIdSnowflake] = new ViewerJson.JsonMessage {
|
||||
U = userIndices[message.Sender],
|
||||
T = message.Timestamp,
|
||||
M = string.IsNullOrEmpty(message.Text) ? null : message.Text,
|
||||
Te = message.EditTimestamp,
|
||||
@ -145,5 +166,10 @@ static class ViewerJsonExport {
|
||||
}).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
data[channelIdSnowflake] = channelData;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DHT.Server.Database.Export;
|
||||
|
||||
[JsonSourceGenerationOptions(
|
||||
Converters = [typeof(SnowflakeJsonSerializer)],
|
||||
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
|
||||
GenerationMode = JsonSourceGenerationMode.Default
|
||||
)]
|
||||
[JsonSerializable(typeof(ViewerJson.JsonMessage))]
|
||||
sealed partial class ViewerJsonMessageContext : JsonSerializerContext;
|
@ -14,7 +14,7 @@ public interface IServerRepository {
|
||||
|
||||
Task<long> Count(CancellationToken cancellationToken = default);
|
||||
|
||||
IAsyncEnumerable<Data.Server> Get(CancellationToken cancellationToken = default);
|
||||
IAsyncEnumerable<Data.Server> Get();
|
||||
|
||||
internal sealed class Dummy : IServerRepository {
|
||||
public IObservable<long> TotalCount { get; } = Observable.Return(0L);
|
||||
@ -27,7 +27,7 @@ public interface IServerRepository {
|
||||
return Task.FromResult(0L);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<Data.Server> Get(CancellationToken cancellationToken) {
|
||||
public IAsyncEnumerable<Data.Server> Get() {
|
||||
return AsyncEnumerable.Empty<Data.Server>();
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public interface IUserRepository {
|
||||
|
||||
Task<long> Count(CancellationToken cancellationToken = default);
|
||||
|
||||
IAsyncEnumerable<User> Get(CancellationToken cancellationToken = default);
|
||||
IAsyncEnumerable<User> Get();
|
||||
|
||||
internal sealed class Dummy : IUserRepository {
|
||||
public IObservable<long> TotalCount { get; } = Observable.Return(0L);
|
||||
@ -28,7 +28,7 @@ public interface IUserRepository {
|
||||
return Task.FromResult(0L);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<User> Get(CancellationToken cancellationToken) {
|
||||
public IAsyncEnumerable<User> Get() {
|
||||
return AsyncEnumerable.Empty<User>();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DHT.Server.Data;
|
||||
@ -47,13 +46,13 @@ sealed class SqliteServerRepository : BaseSqliteRepository, IServerRepository {
|
||||
return await conn.ExecuteReaderAsync("SELECT COUNT(*) FROM servers", static reader => reader?.GetInt64(0) ?? 0L, cancellationToken);
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<Data.Server> Get([EnumeratorCancellation] CancellationToken cancellationToken) {
|
||||
public async IAsyncEnumerable<Data.Server> Get() {
|
||||
await using var conn = await pool.Take();
|
||||
|
||||
await using var cmd = conn.Command("SELECT id, name, type FROM servers");
|
||||
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
||||
await using var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
while (await reader.ReadAsync(cancellationToken)) {
|
||||
while (await reader.ReadAsync()) {
|
||||
yield return new Data.Server {
|
||||
Id = reader.GetUint64(0),
|
||||
Name = reader.GetString(1),
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DHT.Server.Data;
|
||||
@ -59,13 +58,13 @@ sealed class SqliteUserRepository : BaseSqliteRepository, IUserRepository {
|
||||
return await conn.ExecuteReaderAsync("SELECT COUNT(*) FROM users", static reader => reader?.GetInt64(0) ?? 0L, cancellationToken);
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<User> Get([EnumeratorCancellation] CancellationToken cancellationToken) {
|
||||
public async IAsyncEnumerable<User> Get() {
|
||||
await using var conn = await pool.Take();
|
||||
|
||||
await using var cmd = conn.Command("SELECT id, name, avatar_url, discriminator FROM users");
|
||||
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
||||
await using var reader = await cmd.ExecuteReaderAsync();
|
||||
|
||||
while (await reader.ReadAsync(cancellationToken)) {
|
||||
while (await reader.ReadAsync()) {
|
||||
yield return new User {
|
||||
Id = reader.GetUint64(0),
|
||||
Name = reader.GetString(1),
|
||||
|
@ -47,13 +47,4 @@ abstract class BaseEndpoint(IDatabaseFile db) {
|
||||
throw new HttpException(HttpStatusCode.UnsupportedMediaType, "This endpoint only accepts JSON.");
|
||||
}
|
||||
}
|
||||
|
||||
protected static Guid GetSessionId(HttpRequest request) {
|
||||
if (request.Query.TryGetValue("session", out var sessionIdValue) && sessionIdValue.Count == 1 && Guid.TryParse(sessionIdValue[0], out Guid sessionId)) {
|
||||
return sessionId;
|
||||
}
|
||||
else {
|
||||
throw new HttpException(HttpStatusCode.BadRequest, "Invalid session ID.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
app/Server/Endpoints/GetViewerDataEndpoint.cs
Normal file
25
app/Server/Endpoints/GetViewerDataEndpoint.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DHT.Server.Database;
|
||||
using DHT.Server.Database.Export;
|
||||
using DHT.Server.Service.Viewer;
|
||||
using DHT.Utils.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace DHT.Server.Endpoints;
|
||||
|
||||
sealed class GetViewerDataEndpoint(IDatabaseFile db, ViewerSessions viewerSessions) : BaseEndpoint(db) {
|
||||
protected override Task Respond(HttpRequest request, HttpResponse response, CancellationToken cancellationToken) {
|
||||
if (!request.Query.TryGetValue("session", out var sessionIdValue) || sessionIdValue.Count != 1 || !Guid.TryParse(sessionIdValue[0], out Guid sessionId)) {
|
||||
throw new HttpException(HttpStatusCode.BadRequest, "Invalid session ID.");
|
||||
}
|
||||
|
||||
response.ContentType = MediaTypeNames.Application.Json;
|
||||
|
||||
var session = viewerSessions.Get(sessionId);
|
||||
return ViewerJsonExport.Generate(response.Body, Db, session.MessageFilter, cancellationToken);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DHT.Server.Database;
|
||||
using DHT.Server.Database.Export;
|
||||
using DHT.Server.Service.Viewer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace DHT.Server.Endpoints;
|
||||
|
||||
sealed class GetViewerMessagesEndpoint(IDatabaseFile db, ViewerSessions viewerSessions) : BaseEndpoint(db) {
|
||||
protected override Task Respond(HttpRequest request, HttpResponse response, CancellationToken cancellationToken) {
|
||||
var sessionId = GetSessionId(request);
|
||||
var session = viewerSessions.Get(sessionId);
|
||||
|
||||
response.ContentType = "application/x-ndjson";
|
||||
return ViewerJsonExport.GetMessages(response.Body, Db, session.MessageFilter, cancellationToken);
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DHT.Server.Database;
|
||||
using DHT.Server.Database.Export;
|
||||
using DHT.Server.Service.Viewer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace DHT.Server.Endpoints;
|
||||
|
||||
sealed class GetViewerMetadataEndpoint(IDatabaseFile db, ViewerSessions viewerSessions) : BaseEndpoint(db) {
|
||||
protected override Task Respond(HttpRequest request, HttpResponse response, CancellationToken cancellationToken) {
|
||||
var sessionId = GetSessionId(request);
|
||||
var session = viewerSessions.Get(sessionId);
|
||||
|
||||
response.ContentType = MediaTypeNames.Application.Json;
|
||||
return ViewerJsonExport.GetMetadata(response.Body, Db, session.MessageFilter, cancellationToken);
|
||||
}
|
||||
}
|
@ -51,8 +51,7 @@ sealed class Startup {
|
||||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints => {
|
||||
endpoints.MapGet("/get-tracking-script", new GetTrackingScriptEndpoint(db, parameters, resources).Handle);
|
||||
endpoints.MapGet("/get-viewer-metadata", new GetViewerMetadataEndpoint(db, viewerSessions).Handle);
|
||||
endpoints.MapGet("/get-viewer-messages", new GetViewerMessagesEndpoint(db, viewerSessions).Handle);
|
||||
endpoints.MapGet("/get-viewer-data", new GetViewerDataEndpoint(db, viewerSessions).Handle);
|
||||
endpoints.MapGet("/get-downloaded-file/{url}", new GetDownloadedFileEndpoint(db).Handle);
|
||||
endpoints.MapPost("/track-channel", new TrackChannelEndpoint(db).Handle);
|
||||
endpoints.MapPost("/track-users", new TrackUsersEndpoint(db).Handle);
|
||||
|
Loading…
Reference in New Issue
Block a user