aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docker/crupest-api/CrupestApi/Program.cs181
-rw-r--r--docker/crupest-nginx/sites/www/src/main.js62
-rw-r--r--template/docker-compose.yaml.template14
-rwxr-xr-xtool/test-crupest-api.py55
4 files changed, 123 insertions, 189 deletions
diff --git a/docker/crupest-api/CrupestApi/Program.cs b/docker/crupest-api/CrupestApi/Program.cs
index c62bf4d..7fd6675 100644
--- a/docker/crupest-api/CrupestApi/Program.cs
+++ b/docker/crupest-api/CrupestApi/Program.cs
@@ -9,47 +9,40 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Console;
using Microsoft.AspNetCore.Http;
using CrupestApi.Config;
-public class TodoItem
+namespace CrupestApi
{
- public string Status { get; set; } = default!;
- public string Title { get; set; } = default!;
-}
+ public class TodoItem
+ {
+ public string Status { get; set; } = default!;
+ public string Title { get; set; } = default!;
+ public bool Closed { get; set; }
+ public string color { get; set; } = default!;
+ }
-internal class Program
-{
- private static void Main(string[] args)
+ internal class Program
{
- using var httpClient = new HttpClient();
+ private static void Main(string[] args)
+ {
+ using var httpClient = new HttpClient();
- var builder = WebApplication.CreateBuilder(args);
+ var builder = WebApplication.CreateBuilder(args);
- string configFilePath = Environment.GetEnvironmentVariable("CRUPEST_API_CONFIG_FILE") ?? "/config.json";
+ string configFilePath = Environment.GetEnvironmentVariable("CRUPEST_API_CONFIG_FILE") ?? "/config.json";
- builder.Configuration.AddJsonFile(configFilePath, optional: false, reloadOnChange: true);
+ builder.Configuration.AddJsonFile(configFilePath, optional: false, reloadOnChange: true);
- string? logFilePath = Environment.GetEnvironmentVariable("CRUPEST_API_LOG_FILE");
- if (logFilePath is not null)
- {
- // TODO: Log to file.
- builder.Logging.AddSimpleConsole(logger =>
- {
- logger.ColorBehavior = LoggerColorBehavior.Disabled;
- });
- }
+ var app = builder.Build();
- var app = builder.Build();
-
- app.MapGet("/api/todos", async ([FromServices] IConfiguration configuration, [FromServices] ILoggerFactory loggerFactory) =>
- {
- var logger = loggerFactory.CreateLogger("CrupestApi.Todos");
-
- static string CreateGraphQLQuery(TodoConfiguration todoConfiguration)
+ app.MapGet("/api/todos", async ([FromServices] IConfiguration configuration, [FromServices] ILoggerFactory loggerFactory) =>
{
- return $$"""
+ var logger = loggerFactory.CreateLogger("CrupestApi.Todos");
+
+ static string CreateGraphQLQuery(TodoConfiguration todoConfiguration)
+ {
+ return $$"""
{
user(login: "{{todoConfiguration.Username}}") {
projectV2(number: {{todoConfiguration.ProjectNumber}}) {
@@ -76,85 +69,89 @@ internal class Program
}
}
""";
- }
+ }
- var todoConfiguration = configuration.GetSection("Todos").Get<TodoConfiguration>();
- if (todoConfiguration is null)
- {
- throw new Exception("Fail to get todos configuration.");
- }
+ var todoConfiguration = configuration.GetSection("Todos").Get<TodoConfiguration>();
+ if (todoConfiguration is null)
+ {
+ throw new Exception("Fail to get todos configuration.");
+ }
- using var requestContent = new StringContent(JsonSerializer.Serialize(new
- {
- query = CreateGraphQLQuery(todoConfiguration)
- }));
- requestContent.Headers.ContentType = new MediaTypeHeaderValue(MediaTypeNames.Application.Json, Encoding.UTF8.WebName);
+ using var requestContent = new StringContent(JsonSerializer.Serialize(new
+ {
+ query = CreateGraphQLQuery(todoConfiguration)
+ }));
+ requestContent.Headers.ContentType = new MediaTypeHeaderValue(MediaTypeNames.Application.Json, Encoding.UTF8.WebName);
- using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.github.com/graphql");
- request.Content = requestContent;
- request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", todoConfiguration.Token);
+ using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.github.com/graphql");
+ request.Content = requestContent;
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", todoConfiguration.Token);
+ request.Headers.TryAddWithoutValidation("User-Agent", "crupest");
- using var response = await httpClient.SendAsync(request);
- var responseBody = await response.Content.ReadAsStringAsync();
- logger.LogInformation(response.StatusCode.ToString());
- logger.LogInformation(responseBody);
+ using var response = await httpClient.SendAsync(request);
+ var responseBody = await response.Content.ReadAsStringAsync();
+ logger.LogInformation(response.StatusCode.ToString());
+ logger.LogInformation(responseBody);
- if (response.IsSuccessStatusCode)
- {
- using var responseJson = JsonSerializer.Deserialize<JsonDocument>(responseBody);
- if (responseJson is null)
+ if (response.IsSuccessStatusCode)
{
- throw new Exception("Fail to deserialize response body.");
- }
+ using var responseJson = JsonSerializer.Deserialize<JsonDocument>(responseBody);
+ if (responseJson is null)
+ {
+ throw new Exception("Fail to deserialize response body.");
+ }
- var nodes = responseJson.RootElement.GetProperty("data").GetProperty("user").GetProperty("projectV2").GetProperty("items").GetProperty("nodes").EnumerateArray();
+ var nodes = responseJson.RootElement.GetProperty("data").GetProperty("user").GetProperty("projectV2").GetProperty("items").GetProperty("nodes").EnumerateArray();
- var result = new List<TodoItem>();
+ var result = new List<TodoItem>();
- foreach (var node in nodes)
- {
- var content = node.GetProperty("content");
- var title = content.GetProperty("title").GetString();
- if (title is null)
- {
- throw new Exception("Fail to get title.");
- }
- JsonElement closedElement;
- bool closed;
- if (content.TryGetProperty("closed", out closedElement))
- {
- closed = closedElement.GetBoolean();
- }
- else
+ foreach (var node in nodes)
{
- closed = false;
+ var content = node.GetProperty("content");
+ var title = content.GetProperty("title").GetString();
+ if (title is null)
+ {
+ throw new Exception("Fail to get title.");
+ }
+ JsonElement closedElement;
+ bool closed;
+ if (content.TryGetProperty("closed", out closedElement))
+ {
+ closed = closedElement.GetBoolean();
+ }
+ else
+ {
+ closed = false;
+ }
+
+ result.Add(new TodoItem
+ {
+ Title = title,
+ Status = closed ? "Done" : "Todo",
+ Closed = closed,
+ color = closed ? "green" : "blue"
+ });
}
- result.Add(new TodoItem
+ return Results.Json(result, new JsonSerializerOptions
{
- Title = title,
- Status = closed ? "Done" : "Todo"
- });
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ }, statusCode: 200);
}
-
- return Results.Json(result, new JsonSerializerOptions
+ else
{
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase
- }, statusCode: 200);
- }
- else
- {
- const string message = "Fail to get todos from GitHub.";
- logger.LogError(message);
+ const string message = "Fail to get todos from GitHub.";
+ logger.LogError(message);
- return Results.Json(new
- {
- message
- }, statusCode: StatusCodes.Status503ServiceUnavailable);
- }
- });
+ return Results.Json(new
+ {
+ message
+ }, statusCode: StatusCodes.Status503ServiceUnavailable);
+ }
+ });
- app.Run();
+ app.Run();
+ }
}
-} \ No newline at end of file
+}
diff --git a/docker/crupest-nginx/sites/www/src/main.js b/docker/crupest-nginx/sites/www/src/main.js
index bbdecab..111b0bd 100644
--- a/docker/crupest-nginx/sites/www/src/main.js
+++ b/docker/crupest-nginx/sites/www/src/main.js
@@ -1,4 +1,3 @@
-import { Octokit } from "https://cdn.skypack.dev/octokit";
import "./style.css";
const colorStripContainer = document.getElementById("color-strip-container");
@@ -21,63 +20,28 @@ document.addEventListener("DOMContentLoaded", async () => {
const todoMessage = document.getElementById("todo-message");
const todoContainer = document.getElementById("todo-container");
- // TODO: we need another way to do this!
- const octokit = new Octokit({
- auth: "xxx",
- });
+ const res = await fetch("/api/todos");
+ const body = res.json();
- try {
- const res = await octokit.graphql(
- `
- {
- user(login: "crupest") {
- projectV2(number: 2) {
- items(last: 100) {
- nodes {
- __typename
- fieldValueByName(name: "Status")
- content {
- __typename
- ... on Issue {
- title
- closed
- }
- ... on PullRequest {
- title
- closed
- }
- ... on DraftIssue {
- title
- }
- }
- }
- }
- }
- }
- }
- `
+ if (res.status !== 200) {
+ todoMessage.style.color = "red";
+ todoMessage.textContent =
+ "Failed to fetch TODOs. (Maybe due to rate limit. Please try later.)";
+ console.log(
+ `Failed to get GitHub project info. Status: ${res.status}. Body: ${body}`
);
-
- const items = res.user.projectV2.items.nodes.map((node) => node.content);
-
- items.forEach((item) => {
- if (item.__typename == "DraftIssue") {
- item.closed = false;
- }
- const { title, closed } = item;
+ } else {
+ body.forEach((item) => {
+ const { status, title, color } = item;
const li = document.createElement("li");
const span = document.createElement("span");
- span.textContent = closed ? "Done:" : "Todo:";
- span.style.color = closed ? "green" : "blue";
+ span.textContent = status;
+ span.style.color = color;
li.appendChild(span);
li.append(title);
todoContainer.appendChild(li);
});
todoMessage.parentElement.removeChild(todoMessage);
- } catch (e) {
- todoMessage.style.color = "red";
- todoMessage.textContent = "Failed to fetch TODOs.";
- console.log("Failed to get GitHub project info.", e);
}
});
diff --git a/template/docker-compose.yaml.template b/template/docker-compose.yaml.template
index 3df0a18..8c854c5 100644
--- a/template/docker-compose.yaml.template
+++ b/template/docker-compose.yaml.template
@@ -76,6 +76,19 @@ services:
- timeline-network
- code-server-network
- auto-certbot-network
+ - crupest-api-network
+
+ crupest-api:
+ pull_policy: build
+ build:
+ context: ./docker/crupest-api
+ dockerfile: Dockerfile
+ pull: true
+ container_name: crupest-api
+ volumes:
+ - "./crupest-api-config.json:/config.json:ro"
+ networks:
+ - crupest-api-network
auto-certbot:
pull_policy: build
@@ -158,3 +171,4 @@ networks:
timeline-network:
code-server-network:
auto-certbot-network:
+ crupest-api-network:
diff --git a/tool/test-crupest-api.py b/tool/test-crupest-api.py
index 5cd461d..c89a0f9 100755
--- a/tool/test-crupest-api.py
+++ b/tool/test-crupest-api.py
@@ -1,45 +1,23 @@
#!/usr/bin/env python3
-import subprocess
-import os
-import sys
-from os.path import *
-import signal
-from modules.path import *
+import json
import time
-from rich.console import Console
+from os.path import *
from urllib.request import urlopen
from http.client import *
-import json
+from rich.console import Console
+from modules.path import *
console = console = Console()
-ensure_log_dir()
-
-dotnet_project = join(project_dir, "docker", "crupest-api", "CrupestApi")
-dotnet_log_path = abspath(join(log_dir, "crupest-api-log"))
-dotnet_config_path = abspath(join(project_dir, "crupest-api-config.json"))
-
-os.environ["CRUPEST_API_CONFIG_FILE"] = dotnet_log_path
-os.environ["CRUPEST_API_LOG_FILE"] = dotnet_config_path
-
-popen = subprocess.Popen(
- ["dotnet", "run", "--project", dotnet_project, "--launch-profile", "dev"]
-)
-
-console.print("Sleep for 3s to wait for server startup.")
-time.sleep(3)
-
def do_the_test():
res: HTTPResponse = urlopen("http://localhost:5188/api/todos")
- console.print(res)
body = res.read()
- console.print(body)
if res.status != 200:
raise Exception("Status code is not 200.")
- result = json.load(body)
+ result = json.loads(body)
if not isinstance(result, list):
raise Exception("Result is not an array.")
if len(result) == 0:
@@ -52,29 +30,10 @@ def do_the_test():
raise Exception("Result[0].status is not a string.")
-for i in range(0, 2):
- console.print(f"Test begin with attempt {i + 1}", style="cyan")
- try:
- do_the_test()
- console.print("Test passed.", style="green")
- popen.send_signal(signal.SIGTERM)
- popen.wait()
- exit(0)
- except Exception as e:
- console.print(e)
- console.print(
- "Test failed. Try again after sleep for 1s.", style="red")
- time.sleep(1)
-
try:
- console.print(
- f"Test begin with attempt {i + 2}, also the final one.", style="cyan")
do_the_test()
- console.print("Test passed.", style="green")
- popen.send_signal(signal.SIGTERM)
- popen.wait()
+ console.print("Test passed!", style="green")
exit(0)
except Exception as e:
console.print(e)
- console.print("Final test failed.", style="red")
- exit(1)
+ console.print("Test failed!", style="red")