feat: Einzelne Nachrichten als Link teilen
Backend:
- SharedMessageRecord Model + shared_messages DB-Tabelle
- SharedEndpoint: POST /api/messages/{id}/share, GET /api/shared/{token},
GET /api/shared (Liste), DELETE /api/shared/{token}
- Auth-Ausnahme für GET /api/shared/{token} (öffentlich ohne Login)
- DB-Methoden: CreateSharedMessage, GetSharedMessage, GetChainMessagesUpTo
Frontend:
- Share-Button (fa-share-alt) an jeder Nachricht
- Kontextmenü: Einzelnachricht oder mit Thread-Kontext teilen
- Link wird automatisch in Zwischenablage kopiert
- Öffentliche Shared-Page (#shared/{token}) ohne Login
- Markdown-Rendering in der geteilten Ansicht
- CSS für Share-Button, Share-Menü und Shared-Page
This commit is contained in:
1 parent
4e915f76af
commit
be29984b25
12 files changed
+591
-8
No files matched your search
@@ -228,6 +228,20 @@ public class DatabaseService
|
||||
CREATE INDEX IF NOT EXISTS idx_cronjobs_user ON cronjobs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_cronjobs_next_run ON cronjobs(next_run_at);
|
||||
");
|
||||
|
||||
conn.Execute(@"
|
||||
CREATE TABLE IF NOT EXISTS shared_messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
share_token TEXT NOT NULL UNIQUE,
|
||||
message_id INTEGER NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
include_context INTEGER NOT NULL DEFAULT 0,
|
||||
expires_at TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_shared_messages_token ON shared_messages(share_token);
|
||||
CREATE INDEX IF NOT EXISTS idx_shared_messages_message ON shared_messages(message_id);
|
||||
");
|
||||
}
|
||||
|
||||
private void TryAddColumn(SqliteConnection conn, string table, string column, string type)
|
||||
@@ -236,6 +250,65 @@ public class DatabaseService
|
||||
catch { /* column already exists */ }
|
||||
}
|
||||
|
||||
// ── Shared Messages ──
|
||||
|
||||
public async Task<SharedMessageRecord> CreateSharedMessage(long messageId, string userId, bool includeContext, string expiresAt = null)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
var record = new SharedMessageRecord
|
||||
{
|
||||
ShareToken = Guid.NewGuid().ToString("N"),
|
||||
MessageId = messageId,
|
||||
UserId = userId,
|
||||
IncludeContext = includeContext,
|
||||
ExpiresAt = expiresAt,
|
||||
CreatedAt = DateTime.UtcNow.ToString("o")
|
||||
};
|
||||
await conn.ExecuteAsync(@"
|
||||
INSERT INTO shared_messages (share_token, message_id, user_id, include_context, expires_at, created_at)
|
||||
VALUES (@ShareToken, @MessageId, @UserId, @IncludeContext, @ExpiresAt, @CreatedAt)
|
||||
", record);
|
||||
return record;
|
||||
}
|
||||
|
||||
public async Task<SharedMessageRecord> GetSharedMessage(string shareToken)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.QuerySingleOrDefaultAsync<SharedMessageRecord>(
|
||||
"SELECT * FROM shared_messages WHERE share_token = @shareToken", new { shareToken });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SharedMessageRecord>> GetSharedMessagesByUser(string userId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.QueryAsync<SharedMessageRecord>(
|
||||
"SELECT * FROM shared_messages WHERE user_id = @userId ORDER BY created_at DESC", new { userId });
|
||||
}
|
||||
|
||||
public async Task DeleteSharedMessage(string shareToken, string userId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(
|
||||
"DELETE FROM shared_messages WHERE share_token = @shareToken AND user_id = @userId",
|
||||
new { shareToken, userId });
|
||||
}
|
||||
|
||||
public async Task<MessageRecord> GetMessage(long messageId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.QuerySingleOrDefaultAsync<MessageRecord>(
|
||||
"SELECT * FROM messages WHERE id = @messageId", new { messageId });
|
||||
}
|
||||
|
||||
public async Task<List<MessageRecord>> GetChainMessagesUpTo(string chainId, long messageId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
var result = await conn.QueryAsync<MessageRecord>(
|
||||
"SELECT * FROM messages WHERE chain_id = @chainId AND id <= @messageId ORDER BY id ASC",
|
||||
new { chainId, messageId });
|
||||
return result.AsList();
|
||||
}
|
||||
|
||||
// ── Agents ──
|
||||
|
||||
public async Task<AgentRecord> CreateAgent(AgentRecord agent)
|
||||
|
||||
Reference in new issue
Block a user