Initial commit.

This commit is contained in:
2025-12-11 10:20:10 +09:00
commit 057481803f
21 changed files with 1327 additions and 0 deletions

105
Services/AppDbService.cs Normal file
View File

@@ -0,0 +1,105 @@
using Microsoft.EntityFrameworkCore;
using Serilog;
using TriliumMind.Data;
using TriliumMind.Data.Entities;
namespace TriliumMind.Services;
public class AppDbService
{
private readonly Serilog.ILogger _log;
private readonly IAppDbContext _db;
public AppDbService(IAppDbContext dbContext)
{
_log = Log.ForContext<AppDbService>();
_db = dbContext;
}
public async Task InitializeDatabaseAsync()
{
try
{
_log.Information("Checking database...");
// Create db if not exists
var created = await _db.Database.EnsureCreatedAsync();
if (created)
{
_log.Information("Database created successfully");
}
else
{
_log.Information("Database already exists");
}
// Migration
var pendingMigrations = await _db.Database.GetPendingMigrationsAsync();
if (pendingMigrations.Any())
{
_log.Information("Applying pending migrations...");
await _db.Database.MigrateAsync();
_log.Information("Migrations applied successfully");
}
}
catch (Exception ex)
{
_log.Error(ex, "Failed to initialize database");
throw;
}
}
public async Task<Dictionary<string, string>> LoadConfigAsync()
{
try
{
_log.Information("Loading configuration from database...");
var configs = await _db.Configs.ToDictionaryAsync(c => c.Key, c => c.Value);
_log.Information("Loaded {count} configuration entries", configs.Count);
return configs;
}
catch (Exception ex)
{
_log.Error(ex, "Failed to load configuration from database");
throw;
}
}
public async Task<string?> GetConfigAsync(string key)
{
try
{
var config = await _db.Configs.FindAsync(key);
return config?.Value;
}
catch (Exception ex)
{
_log.Error(ex, "Failed to get config value for key: {key}", key);
throw;
}
}
public async Task SetConfigAsync(string key, string value)
{
try
{
var config = await _db.Configs.FindAsync(key);
if (config == null)
{
config = new Config { Key = key, Value = value };
_db.Configs.Add(config);
}
else
{
config.Value = value;
_db.Configs.Update(config);
}
await _db.SaveChangesAsync();
_log.Debug("Config saved: {key} = {value}", key, value);
}
catch (Exception ex)
{
_log.Error(ex, "Failed to set config value for key: {key}", key);
throw;
}
}
}

59
Services/JiraService.cs Normal file
View File

@@ -0,0 +1,59 @@
using Serilog;
using Microsoft.Extensions.Options;
using System.Text.Json;
using TriliumMind.Models;
namespace TriliumMind.Services;
public class JiraService
{
private readonly AppConfigs _configs;
private readonly Serilog.ILogger _log;
public JiraService(AppConfigs configs)
{
_configs = configs;
_log = Log.ForContext<JiraService>();
}
public async Task<JiraResponse> FetchJiraIssuesAsync(
DateTimeOffset lastFetchTime, string targetProject,int startAt, CancellationToken ct)
{
var uri = $"{_configs.AppSettings.Jira.ApiBaseUrl}/"
+ $"search?jql=project={targetProject} "
+ $"AND updated >= \"{lastFetchTime.ToString("yyyy-MM-dd HH:mm")}\" "
+ $"&fields={_configs.AppSettings.Jira.SearchFields}"
+ $"&startAt={startAt}&maxResults=50";
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", $"Bearer {_configs.AppSettings.Jira.AccessToken}");
Log.Debug("Fetch Jira issues ready. Fetch the issues updated after {last_fetch_time} in Project {target_project}.",
lastFetchTime.ToString("yyyy-MM-dd HH:mm"), targetProject);
using var client = new HttpClient();
var response = await client.SendAsync(request, ct).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
var jiraResponse = JsonSerializer.Deserialize<JiraResponse>(jsonResponse);
return jiraResponse;
}
public async Task<Issue> FetchJiraIssueAsync(string targetIssueKey, CancellationToken ct)
{
var uri = $"{_configs.AppSettings.Jira.ApiBaseUrl}/issue/{targetIssueKey}";
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", $"Bearer {_configs.AppSettings.Jira.AccessToken}");
Log.Debug("Fetch a Jira issue({issue_key}) ready.", targetIssueKey);
using var client = new HttpClient();
var response = await client.SendAsync(request, ct).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
var issue = JsonSerializer.Deserialize<Issue>(jsonResponse);
return issue;
}
}

View File

@@ -0,0 +1,37 @@
using Microsoft.Extensions.Options;
using Serilog;
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using TriliumMind.Models;
namespace TriliumMind.Services;
public class TriliumService
{
private readonly Serilog.ILogger _log;
private readonly AppConfigs _configs;
public TriliumService(AppConfigs configs)
{
_log = Log.ForContext<TriliumService>();
_configs = configs;
}
public async Task<string> FettchPageContentsAsync(Issue issue)
{
var baseUrl = _configs.AppSettings.Trilium.EtapiBaseUrl;
var pageId = "QxxFqCNAtIOy";
var requstUrl = $"{baseUrl}/notes/{pageId}/content";
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, requstUrl);
request.Headers.Add("Authorization", "");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
// Return new page ID after creating a new page
return "";
}
}