mirror of
https://github.com/bitwarden/server.git
synced 2025-02-17 02:01:53 +01:00
admin logs
This commit is contained in:
parent
f2ecea0a17
commit
d4b4a2b014
70
src/Admin/Controllers/LogsController.cs
Normal file
70
src/Admin/Controllers/LogsController.cs
Normal file
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Admin.Models;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Microsoft.Azure.Documents.Linq;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public class LogsController : Controller
|
||||
{
|
||||
private const string Database = "Diagnostics";
|
||||
private const string Collection = "Logs";
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public LogsController(GlobalSettings globalSettings)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index(string cursor = null, int count = 25)
|
||||
{
|
||||
var collectionLink = UriFactory.CreateDocumentCollectionUri(Database, Collection);
|
||||
using(var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
|
||||
_globalSettings.DocumentDb.Key))
|
||||
{
|
||||
var options = new FeedOptions
|
||||
{
|
||||
MaxItemCount = count,
|
||||
RequestContinuation = cursor
|
||||
};
|
||||
|
||||
var query = client.CreateDocumentQuery(collectionLink, options)
|
||||
.OrderByDescending(l => l.Timestamp).AsDocumentQuery();
|
||||
var response = await query.ExecuteNextAsync<LogModel>();
|
||||
|
||||
return View(new CursorPagedModel<LogModel>
|
||||
{
|
||||
Items = response.ToList(),
|
||||
Count = count,
|
||||
Cursor = cursor,
|
||||
NextCursor = response.ResponseContinuation
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IActionResult> View(string id)
|
||||
{
|
||||
using(var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
|
||||
_globalSettings.DocumentDb.Key))
|
||||
{
|
||||
var uri = UriFactory.CreateDocumentUri(Database, Collection, id);
|
||||
var response = await client.ReadDocumentAsync<LogModel>(uri);
|
||||
if(response?.Document == null)
|
||||
{
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
return View(response.Document);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
src/Admin/Models/CursorPagedModel.cs
Normal file
12
src/Admin/Models/CursorPagedModel.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class CursorPagedModel<T>
|
||||
{
|
||||
public List<T> Items { get; set; }
|
||||
public int Count { get; set; }
|
||||
public string Cursor { get; set; }
|
||||
public string NextCursor { get; set; }
|
||||
}
|
||||
}
|
28
src/Admin/Models/LogModel.cs
Normal file
28
src/Admin/Models/LogModel.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Bit.Admin.Models
|
||||
{
|
||||
public class LogModel : Resource
|
||||
{
|
||||
public long EventIdHash { get; set; }
|
||||
public LogEventLevel Level { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string MessageTruncated => Message.Length > 200 ? $"{Message.Substring(0, 200)}..." : Message;
|
||||
public string MessageTemplate { get; set; }
|
||||
public Error Exception { get; set; }
|
||||
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class Error : Exception, ISerializable
|
||||
{
|
||||
[JsonProperty(PropertyName = "error")]
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
public Error() { }
|
||||
}
|
||||
}
|
||||
}
|
61
src/Admin/Views/Logs/Index.cshtml
Normal file
61
src/Admin/Views/Logs/Index.cshtml
Normal file
@ -0,0 +1,61 @@
|
||||
@model CursorPagedModel<LogModel>
|
||||
@{
|
||||
ViewData["Title"] = "Logs";
|
||||
}
|
||||
|
||||
<h1>Logs</h1>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50px;"> </th>
|
||||
<th style="width: 200px;">Timestamp</th>
|
||||
<th style="width: 100px;">Level</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if(!Model.Items.Any())
|
||||
{
|
||||
<tr>
|
||||
<td colspan="4">No results to list.</td>
|
||||
</tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach(var log in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<a asp-action="View" asp-route-id="@log.Id" title="View">
|
||||
<i class="fa fa-file-text-o fa-lg"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>@log.Timestamp.ToString()</td>
|
||||
<td>@log.Level</td>
|
||||
<td>@log.MessageTruncated</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
@if(string.IsNullOrWhiteSpace(Model.NextCursor))
|
||||
{
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" tabindex="-1">Next</a>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item">
|
||||
<a class="page-link" asp-action="Index" asp-route-cursor="@Model.NextCursor"
|
||||
asp-route-count="@Model.Count">Next</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
30
src/Admin/Views/Logs/View.cshtml
Normal file
30
src/Admin/Views/Logs/View.cshtml
Normal file
@ -0,0 +1,30 @@
|
||||
@model LogModel
|
||||
@{
|
||||
ViewData["Title"] = "Log: " + Model.Id;
|
||||
}
|
||||
|
||||
<h1>Log <small>@Model.Id</small></h1>
|
||||
|
||||
<h2>Information</h2>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-4 col-lg-3">Id</dt>
|
||||
<dd class="col-sm-8 col-lg-9"><code>@Model.Id</code></dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">Event Id Hash</dt>
|
||||
<dd class="col-sm-8 col-lg-9">@Model.EventIdHash</dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">Timestamp</dt>
|
||||
<dd class="col-sm-8 col-lg-9">@Model.Timestamp.ToString()</dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">Level</dt>
|
||||
<dd class="col-sm-8 col-lg-9">@Model.Level</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Message</h2>
|
||||
<pre style="max-height: 500px;">@Model.Message</pre>
|
||||
|
||||
@if(Model.Exception != null)
|
||||
{
|
||||
<h2>Exception</h2>
|
||||
<pre style="max-height: 500px;">@Model.Exception.ToString()</pre>
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
@inject SignInManager<IdentityUser> SignInManager
|
||||
@inject Bit.Core.GlobalSettings GlobalSettings
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@ -36,6 +37,12 @@
|
||||
<li class="nav-item" active-controller="Organizations">
|
||||
<a class="nav-link" asp-controller="Organizations" asp-action="Index">Organizations</a>
|
||||
</li>
|
||||
@if(!GlobalSettings.SelfHosted)
|
||||
{
|
||||
<li class="nav-item" active-controller="Logs">
|
||||
<a class="nav-link" asp-controller="Logs" asp-action="Index">Logs</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://help.bitwarden.com/hosting/" target="_blank">Docs</a>
|
||||
|
Loading…
Reference in New Issue
Block a user