aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-02-19 02:05:39 +0800
committerYuqian Yang <crupest@crupest.life>2025-02-19 02:42:42 +0800
commit4c5df72057fe02257e243de37930a47425a84722 (patch)
treef782d0e85c4a784b659e0da29f0bbf4fc25fc827
parent89e31c19bb8fca91c54a73ff7a7f4e837d1dbf93 (diff)
downloadcrupest-4c5df72057fe02257e243de37930a47425a84722.tar.gz
crupest-4c5df72057fe02257e243de37930a47425a84722.tar.bz2
crupest-4c5df72057fe02257e243de37930a47425a84722.zip
chore(docker): remove crupest-api, forgejo and move dropped.
-rw-r--r--crupest-words.txt1
-rw-r--r--dropped/docker/crupest-api/CrupestApi/.dockerignore2
-rw-r--r--dropped/docker/crupest-api/CrupestApi/.gitignore4
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/ColumnTypeInfoTest.cs39
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudIntegratedTest.cs200
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudServiceTest.cs77
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/SqlCompareHelper.cs85
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TableInfoTest.cs35
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TestEntity.cs23
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj29
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs1
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Config.cs23
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs236
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs188
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs218
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs132
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs34
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs101
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs75
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbNullValue.cs9
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs206
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/IClause.cs24
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/InsertClause.cs77
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/DatabaseMigrator.cs44
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/SqliteDatabaseMigrator.cs175
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/OrderByClause.cs50
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ParamMap.cs73
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md47
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs628
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UpdateClause.cs77
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UserException.cs15
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs182
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/CrupestApi.Commons.csproj16
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/EntityNotExistException.cs8
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/HttpContextExtensions.cs113
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/ISecretService.cs8
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretInfo.cs48
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretService.cs48
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretServiceCollectionExtensions.cs12
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretsConstants.cs6
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/CrupestApi.Files.csproj20
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/FilesService.cs6
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/CrupestApi.Secrets.csproj20
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsExtensions.cs19
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/CrupestApi.Todos.csproj15
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosConfiguration.cs14
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosService.cs163
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosServiceCollectionExtensions.cs21
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosWebApplicationExtensions.cs32
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi.sln46
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi/CrupestApi.csproj17
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi/Program.cs24
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi/Properties/launchSettings.json15
-rw-r--r--dropped/docker/crupest-api/CrupestApi/CrupestApi/appsettings.json8
-rw-r--r--dropped/docker/crupest-api/Dockerfile13
-rw-r--r--dropped/template/crupest-api-config.json.template10
-rw-r--r--dropped/template/v2ray-client-config.json.template46
-rw-r--r--templates/disabled/docker-compose.yaml (renamed from dropped/template/docker-compose.yaml.template)12
-rw-r--r--templates/disabled/nginx/code.conf.template (renamed from dropped/template/nginx/code.conf.template)0
-rw-r--r--templates/disabled/nginx/timeline.conf.template (renamed from dropped/template/nginx/timeline.conf.template)0
-rw-r--r--templates/docker-compose.yaml.template14
-rw-r--r--templates/forgejo.app.ini.init.template42
-rw-r--r--templates/nginx/conf.d/git.conf.template20
-rw-r--r--tools/cru-py/cru/service/_config.py6
64 files changed, 0 insertions, 3952 deletions
diff --git a/crupest-words.txt b/crupest-words.txt
index 47ed6f8..56e7cf7 100644
--- a/crupest-words.txt
+++ b/crupest-words.txt
@@ -4,7 +4,6 @@ yuqian
# self-hosted services
2fauth
-forgejo
rspamd
certbot
roundcube
diff --git a/dropped/docker/crupest-api/CrupestApi/.dockerignore b/dropped/docker/crupest-api/CrupestApi/.dockerignore
deleted file mode 100644
index f1c182d..0000000
--- a/dropped/docker/crupest-api/CrupestApi/.dockerignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*/obj
-*/bin
diff --git a/dropped/docker/crupest-api/CrupestApi/.gitignore b/dropped/docker/crupest-api/CrupestApi/.gitignore
deleted file mode 100644
index 371ea59..0000000
--- a/dropped/docker/crupest-api/CrupestApi/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-.vs
-obj
-bin
-dev-config.json
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/ColumnTypeInfoTest.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/ColumnTypeInfoTest.cs
deleted file mode 100644
index b9ec03e..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/ColumnTypeInfoTest.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Data;
-
-namespace CrupestApi.Commons.Crud.Tests;
-
-public class ColumnTypeInfoTest
-{
- private ColumnTypeProvider _provider = new ColumnTypeProvider();
-
- [Theory]
- [InlineData(typeof(int), DbType.Int32, 123)]
- [InlineData(typeof(long), DbType.Int64, 456)]
- [InlineData(typeof(sbyte), DbType.SByte, 789)]
- [InlineData(typeof(short), DbType.Int16, 101)]
- [InlineData(typeof(float), DbType.Single, 1.0f)]
- [InlineData(typeof(double), DbType.Double, 1.0)]
- [InlineData(typeof(string), DbType.String, "Hello world!")]
- [InlineData(typeof(byte[]), DbType.Binary, new byte[] { 1, 2, 3 })]
- public void BasicColumnTypeTest(Type type, DbType dbType, object? value)
- {
- var typeInfo = _provider.Get(type);
- Assert.True(typeInfo.IsSimple);
- Assert.Equal(dbType, typeInfo.DbType);
- Assert.Equal(value, typeInfo.ConvertFromDatabase(value));
- Assert.Equal(value, typeInfo.ConvertToDatabase(value));
- }
-
- [Fact]
- public void DateTimeColumnTypeTest()
- {
- var dateTimeColumnTypeInfo = _provider.Get(typeof(DateTime));
- Assert.Equal(typeof(DateTime), dateTimeColumnTypeInfo.ClrType);
- Assert.Equal(typeof(string), dateTimeColumnTypeInfo.DatabaseClrType);
-
- var dateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
- var dateTimeString = "2000-01-01T00:00:00Z";
- Assert.Equal(dateTimeString, dateTimeColumnTypeInfo.ConvertToDatabase(dateTime));
- Assert.Equal(dateTime, dateTimeColumnTypeInfo.ConvertFromDatabase(dateTimeString));
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudIntegratedTest.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudIntegratedTest.cs
deleted file mode 100644
index bd07c70..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudIntegratedTest.cs
+++ /dev/null
@@ -1,200 +0,0 @@
-using System.Net;
-using System.Net.Http.Headers;
-using CrupestApi.Commons.Secrets;
-using Microsoft.AspNetCore.TestHost;
-
-namespace CrupestApi.Commons.Crud.Tests;
-
-public class CrudIntegratedTest : IAsyncLifetime
-{
- private readonly WebApplication _app;
- private HttpClient _httpClient = default!;
- private HttpClient _authorizedHttpClient = default!;
- private string _token = default!;
-
- public CrudIntegratedTest()
- {
- var builder = WebApplication.CreateBuilder();
- builder.Logging.ClearProviders();
- builder.Services.AddSingleton<IDbConnectionFactory, SqliteMemoryConnectionFactory>();
- builder.Services.AddCrud<TestEntity>();
- builder.WebHost.UseTestServer();
- _app = builder.Build();
- _app.UseCrudCore();
- _app.MapCrud<TestEntity>("/test", "test-perm");
- }
-
- public async Task InitializeAsync()
- {
- await _app.StartAsync();
- _httpClient = _app.GetTestClient();
-
- using (var scope = _app.Services.CreateScope())
- {
- var secretService = (SecretService)scope.ServiceProvider.GetRequiredService<ISecretService>();
- var key = secretService.Create(new SecretInfo
- {
- Key = "test-perm"
- });
- _token = secretService.GetByKey(key).Secret;
- }
-
- _authorizedHttpClient = _app.GetTestClient();
- _authorizedHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token);
- }
-
- public async Task DisposeAsync()
- {
- await _app.StopAsync();
- }
-
-
- [Fact]
- public async Task EmptyTest()
- {
- using var response = await _authorizedHttpClient.GetAsync("/test");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var body = await response.Content.ReadFromJsonAsync<List<TestEntity>>();
- Assert.NotNull(body);
- Assert.Empty(body);
- }
-
- [Fact]
- public async Task CrudTest()
- {
- {
- using var response = await _authorizedHttpClient.PostAsJsonAsync("/test", new TestEntity
- {
- Name = "test",
- Age = 22
- });
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var body = await response.Content.ReadFromJsonAsync<TestEntity>();
- Assert.NotNull(body);
- Assert.Equal("test", body.Name);
- Assert.Equal(22, body.Age);
- Assert.Null(body.Height);
- Assert.NotEmpty(body.Secret);
- }
-
- {
- using var response = await _authorizedHttpClient.GetAsync("/test");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var body = await response.Content.ReadFromJsonAsync<List<TestEntity>>();
- Assert.NotNull(body);
- var entity = Assert.Single(body);
- Assert.Equal("test", entity.Name);
- Assert.Equal(22, entity.Age);
- Assert.Null(entity.Height);
- Assert.NotEmpty(entity.Secret);
- }
-
- {
- using var response = await _authorizedHttpClient.GetAsync("/test/test");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var body = await response.Content.ReadFromJsonAsync<TestEntity>();
- Assert.NotNull(body);
- Assert.Equal("test", body.Name);
- Assert.Equal(22, body.Age);
- Assert.Null(body.Height);
- Assert.NotEmpty(body.Secret);
- }
-
- {
- using var response = await _authorizedHttpClient.PatchAsJsonAsync("/test/test", new TestEntity
- {
- Name = "test-2",
- Age = 23,
- Height = 188.0f
- });
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var body = await response.Content.ReadFromJsonAsync<TestEntity>();
- Assert.NotNull(body);
- Assert.Equal("test-2", body.Name);
- Assert.Equal(23, body.Age);
- Assert.Equal(188.0f, body.Height);
- Assert.NotEmpty(body.Secret);
- }
-
- {
- using var response = await _authorizedHttpClient.GetAsync("/test/test-2");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var body = await response.Content.ReadFromJsonAsync<TestEntity>();
- Assert.NotNull(body);
- Assert.Equal("test-2", body.Name);
- Assert.Equal(23, body.Age);
- Assert.Equal(188.0f, body.Height);
- Assert.NotEmpty(body.Secret);
- }
-
- {
- using var response = await _authorizedHttpClient.DeleteAsync("/test/test-2");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- }
-
- {
- using var response = await _authorizedHttpClient.GetAsync("/test");
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
- var body = await response.Content.ReadFromJsonAsync<List<TestEntity>>();
- Assert.NotNull(body);
- Assert.Empty(body);
- }
- }
-
- [Fact]
- public async Task UnauthorizedTest()
- {
- {
- using var response = await _httpClient.GetAsync("/test");
- Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
- }
-
- {
- using var response = await _httpClient.GetAsync("/test/test");
- Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
- }
-
- {
- using var response = await _httpClient.PostAsJsonAsync("/test", new TestEntity
- {
- Name = "test",
- Age = 22
- });
- Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
- }
-
- {
- using var response = await _httpClient.PatchAsJsonAsync("/test/test", new TestEntity
- {
- Name = "test-2",
- Age = 23,
- Height = 188.0f
- });
- Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
- }
-
- {
- using var response = await _httpClient.DeleteAsync("/test/test");
- Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
- }
- }
-
- [Fact]
- public async Task NotFoundTest()
- {
- {
- using var response = await _authorizedHttpClient.GetAsync("/test/test");
- Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
- }
-
- {
- using var response = await _authorizedHttpClient.PatchAsJsonAsync("/test/test", new TestEntity
- {
- Name = "test-2",
- Age = 23,
- Height = 188.0f
- });
- Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
- }
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudServiceTest.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudServiceTest.cs
deleted file mode 100644
index ad0d34c..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/CrudServiceTest.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using CrupestApi.Commons.Crud.Migrations;
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace CrupestApi.Commons.Crud.Tests;
-
-public class CrudServiceTest
-{
- private readonly SqliteMemoryConnectionFactory _memoryConnectionFactory = new SqliteMemoryConnectionFactory();
-
- private readonly CrudService<TestEntity> _crudService;
-
- public CrudServiceTest()
- {
- var columnTypeProvider = new ColumnTypeProvider();
- var tableInfoFactory = new TableInfoFactory(columnTypeProvider, NullLoggerFactory.Instance);
- var dbConnectionFactory = new SqliteMemoryConnectionFactory();
-
- _crudService = new CrudService<TestEntity>(
- tableInfoFactory, dbConnectionFactory, new SqliteDatabaseMigrator(), NullLoggerFactory.Instance);
- }
-
- [Fact]
- public void CrudTest()
- {
- var key = _crudService.Create(new TestEntity()
- {
- Name = "crupest",
- Age = 18,
- });
-
- Assert.Equal("crupest", key);
-
- var entity = _crudService.GetByKey(key);
- Assert.Equal("crupest", entity.Name);
- Assert.Equal(18, entity.Age);
- Assert.Null(entity.Height);
- Assert.NotEmpty(entity.Secret);
-
- var list = _crudService.GetAll();
- entity = Assert.Single(list);
- Assert.Equal("crupest", entity.Name);
- Assert.Equal(18, entity.Age);
- Assert.Null(entity.Height);
- Assert.NotEmpty(entity.Secret);
-
- var count = _crudService.GetCount();
- Assert.Equal(1, count);
-
- _crudService.UpdateByKey(key, new TestEntity()
- {
- Name = "crupest2.0",
- Age = 22,
- Height = 180,
- });
-
- entity = _crudService.GetByKey("crupest2.0");
- Assert.Equal("crupest2.0", entity.Name);
- Assert.Equal(22, entity.Age);
- Assert.Equal(180, entity.Height);
- Assert.NotEmpty(entity.Secret);
-
- _crudService.DeleteByKey("crupest2.0");
-
- count = _crudService.GetCount();
- Assert.Equal(0, count);
- }
-
- [Fact]
- public void EntityNotExistTest()
- {
- Assert.Throws<EntityNotExistException>(() => _crudService.GetByKey("KeyNotExist"));
- Assert.Throws<EntityNotExistException>(() => _crudService.UpdateByKey("KeyNotExist", new TestEntity
- {
- Name = "crupest"
- }));
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/SqlCompareHelper.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/SqlCompareHelper.cs
deleted file mode 100644
index 72b6218..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/SqlCompareHelper.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System.Text;
-
-namespace CrupestApi.Commons.Crud.Tests;
-
-public class SqlCompareHelper
-{
- private static List<char> SymbolTokens = new List<char>() { '(', ')', ';' };
-
- public static List<string> SqlExtractWords(string? sql, bool toLower = true)
- {
- var result = new List<string>();
-
- if (string.IsNullOrEmpty(sql))
- {
- return result;
- }
-
- var current = 0;
-
- StringBuilder? wordBuilder = null;
-
- while (current < sql.Length)
- {
- if (char.IsWhiteSpace(sql[current]))
- {
- if (wordBuilder is not null)
- {
- result.Add(wordBuilder.ToString());
- wordBuilder = null;
- }
- }
- else if (SymbolTokens.Contains(sql[current]))
- {
- if (wordBuilder is not null)
- {
- result.Add(wordBuilder.ToString());
- wordBuilder = null;
- }
- result.Add(sql[current].ToString());
- }
- else
- {
- if (wordBuilder is not null)
- {
- wordBuilder.Append(sql[current]);
- }
- else
- {
- wordBuilder = new StringBuilder();
- wordBuilder.Append(sql[current]);
- }
- }
- current++;
- }
-
- if (wordBuilder is not null)
- {
- result.Add(wordBuilder.ToString());
- }
-
- if (toLower)
- {
- for (int i = 0; i < result.Count; i++)
- {
- result[i] = result[i].ToLower();
- }
- }
-
- return result;
- }
-
- public static bool SqlEqual(string left, string right)
- {
- return SqlExtractWords(left) == SqlExtractWords(right);
- }
-
- [Fact]
- public void TestSqlExtractWords()
- {
- var sql = "SELECT * FROM TableName WHERE (id = @abcd);";
- var words = SqlExtractWords(sql);
-
- Assert.Equal(new List<string> { "select", "*", "from", "tablename", "where", "(", "id", "=", "@abcd", ")", ";" }, words);
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TableInfoTest.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TableInfoTest.cs
deleted file mode 100644
index b0aa702..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TableInfoTest.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace CrupestApi.Commons.Crud.Tests;
-
-public class TableInfoTest
-{
- private static TableInfoFactory TableInfoFactory = new TableInfoFactory(new ColumnTypeProvider(), NullLoggerFactory.Instance);
-
- private TableInfo _tableInfo;
-
- public TableInfoTest()
- {
- _tableInfo = TableInfoFactory.Get(typeof(TestEntity));
- }
-
- [Fact]
- public void TestColumnCount()
- {
- Assert.Equal(5, _tableInfo.Columns.Count);
- Assert.Equal(4, _tableInfo.PropertyColumns.Count);
- Assert.Equal(4, _tableInfo.ColumnProperties.Count);
- Assert.Equal(1, _tableInfo.NonColumnProperties.Count);
- }
-
- [Fact]
- public void GenerateSelectSqlTest()
- {
- var (sql, parameters) = _tableInfo.GenerateSelectSql(null, WhereClause.Create().Eq("Name", "Hello"));
- var parameterName = parameters.First().Name;
-
- // TODO: Is there a way to auto detect parameters?
- SqlCompareHelper.SqlEqual($"SELECT * FROM TestEntity WHERE (Name = @{parameterName})", sql);
- Assert.Equal("Hello", parameters.Get<string>(parameterName));
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TestEntity.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TestEntity.cs
deleted file mode 100644
index c15334c..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Crud/TestEntity.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace CrupestApi.Commons.Crud.Tests;
-
-public class TestEntity
-{
- [Column(ActAsKey = true, NotNull = true)]
- public string Name { get; set; } = default!;
-
- [Column(NotNull = true)]
- public int Age { get; set; }
-
- [Column]
- public float? Height { get; set; }
-
- [Column(OnlyGenerated = true, NotNull = true, NoUpdate = true)]
- public string Secret { get; set; } = default!;
-
- public static string SecretDefaultValueGenerator()
- {
- return "secret";
- }
-
- public string NonColumn { get; set; } = "Not A Column";
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj
deleted file mode 100644
index 0360ee1..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj
+++ /dev/null
@@ -1,29 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
- <PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
- <Nullable>enable</Nullable>
-
- <IsPackable>false</IsPackable>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.1" />
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
- <PackageReference Include="xunit" Version="2.4.2" />
- <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- <PrivateAssets>all</PrivateAssets>
- </PackageReference>
- <PackageReference Include="coverlet.collector" Version="3.2.0">
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- <PrivateAssets>all</PrivateAssets>
- </PackageReference>
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\CrupestApi.Commons\CrupestApi.Commons.csproj" />
- </ItemGroup>
-
-</Project> \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs
deleted file mode 100644
index 8c927eb..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs
+++ /dev/null
@@ -1 +0,0 @@
-global using Xunit; \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Config.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Config.cs
deleted file mode 100644
index 0ca3547..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Config.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace CrupestApi.Commons;
-
-public class CrupestApiConfig
-{
- public string DataDir { get; set; } = string.Empty;
-}
-
-public static class CrupestApiConfigExtensions
-{
- public static IServiceCollection AddCrupestApiConfig(this IServiceCollection services)
- {
- services.AddOptions<CrupestApiConfig>().BindConfiguration("CrupestApi");
- services.PostConfigure<CrupestApiConfig>(config =>
- {
- if (config.DataDir is null || config.DataDir.Length == 0)
- {
- config.DataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "crupest-api");
- }
- });
-
- return services;
- }
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs
deleted file mode 100644
index e8d3c2e..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs
+++ /dev/null
@@ -1,236 +0,0 @@
-using System.Diagnostics;
-using System.Reflection;
-using System.Text;
-
-namespace CrupestApi.Commons.Crud;
-
-public class ColumnInfo
-{
- private readonly AggregateColumnMetadata _metadata = new AggregateColumnMetadata();
- private ILogger<ColumnInfo> _logger;
-
- /// <summary>
- /// Initialize a column without corresponding property.
- /// </summary>
- public ColumnInfo(TableInfo table, IColumnMetadata metadata, Type clrType, IColumnTypeProvider typeProvider, ILoggerFactory loggerFactory)
- {
- _logger = loggerFactory.CreateLogger<ColumnInfo>();
- if (metadata is null)
- throw new ArgumentException("You must specify metadata for non-property column.");
- if (metadata.TryGetValue(ColumnMetadataKeys.ColumnName, out var columnName))
- _logger.LogInformation("Create column without property.", columnName);
- else
- throw new ArgumentException("You must specify name in metadata for non-property column.");
-
- Table = table;
- _metadata.Add(metadata);
- ColumnType = typeProvider.Get(clrType);
- }
-
- /// <summary>
- /// Initialize a column with corresponding property.
- /// </summary>
- public ColumnInfo(TableInfo table, PropertyInfo propertyInfo, IColumnTypeProvider typeProvider, ILoggerFactory loggerFactory)
- {
- _logger = loggerFactory.CreateLogger<ColumnInfo>();
- _logger.LogInformation("Create column with property {}.", propertyInfo.Name);
-
- Table = table;
- PropertyInfo = propertyInfo;
- ColumnType = typeProvider.Get(propertyInfo.PropertyType);
-
- var columnAttribute = propertyInfo.GetCustomAttribute<ColumnAttribute>();
- if (columnAttribute is not null)
- {
- _metadata.Add(columnAttribute);
- }
- }
-
- public TableInfo Table { get; }
-
- public Type EntityType => Table.EntityType;
-
- // If null, there is no corresponding property.
- public PropertyInfo? PropertyInfo { get; } = null;
-
- public IColumnMetadata Metadata => _metadata;
-
- public IColumnTypeInfo ColumnType { get; }
-
- public bool IsPrimaryKey => Metadata.GetValueOrDefault(ColumnMetadataKeys.IsPrimaryKey) is true;
- public bool IsAutoIncrement => IsPrimaryKey;
- public bool IsNotNull => IsPrimaryKey || Metadata.GetValueOrDefault(ColumnMetadataKeys.NotNull) is true;
- public bool IsOnlyGenerated => Metadata.GetValueOrDefault(ColumnMetadataKeys.OnlyGenerated) is true;
- public bool IsNoUpdate => Metadata.GetValueOrDefault(ColumnMetadataKeys.NoUpdate) is true;
- public object? DefaultValue => Metadata.GetValueOrDefault(ColumnMetadataKeys.DefaultValue);
- /// <summary>
- /// This only returns metadata value. It doesn't not fall back to primary column. If you want to get the real key column, go to table info.
- /// </summary>
- /// <seealso cref="ColumnMetadataKeys.ActAsKey"/>
- /// <seealso cref="TableInfo.KeyColumn"/>
- public bool IsSpecifiedAsKey => Metadata.GetValueOrDefault(ColumnMetadataKeys.ActAsKey) is true;
- public ColumnIndexType Index => Metadata.GetValueOrDefault<ColumnIndexType?>(ColumnMetadataKeys.Index) ?? ColumnIndexType.None;
-
- /// <summary>
- /// Whether the column value can be generated, which means the column has a default value or a default value generator or is AUTOINCREMENT.
- /// </summary>
- public bool CanBeGenerated => DefaultValue is not null || DefaultValueGeneratorMethod is not null || IsAutoIncrement;
-
- /// <summary>
- /// The real column name. Maybe set in metadata or just the property name.
- /// </summary>
- /// <value></value>
- public string ColumnName
- {
- get
- {
- object? value = Metadata.GetValueOrDefault(ColumnMetadataKeys.ColumnName);
- Debug.Assert(value is null || value is string);
- return ((string?)value ?? PropertyInfo?.Name) ?? throw new Exception("Failed to get column name.");
- }
- }
-
- public MethodInfo? DefaultValueGeneratorMethod
- {
- get
- {
- object? value = Metadata.GetValueOrDefault(ColumnMetadataKeys.DefaultValueGenerator);
- Debug.Assert(value is null || value is string);
- MethodInfo? result;
- if (value is null)
- {
- string methodName = ColumnName + "DefaultValueGenerator";
- result = Table.EntityType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
- }
- else
- {
- string methodName = (string)value;
- result = Table.EntityType.GetMethod(methodName, BindingFlags.Static) ?? throw new Exception("The default value generator does not exist.");
- }
-
- return result;
- }
- }
-
- public MethodInfo? ValidatorMethod
- {
- get
- {
- object? value = Metadata.GetValueOrDefault(ColumnMetadataKeys.DefaultValueGenerator);
- Debug.Assert(value is null || value is string);
- MethodInfo? result;
- if (value is null)
- {
- string methodName = ColumnName + "Validator";
- result = Table.EntityType.GetMethod(methodName, BindingFlags.Static);
- }
- else
- {
- string methodName = (string)value;
- result = Table.EntityType.GetMethod(methodName, BindingFlags.Static) ?? throw new Exception("The validator does not exist.");
- }
-
- return result;
- }
- }
-
- public void InvokeValidator(object? value)
- {
- var method = this.ValidatorMethod;
- if (method is null)
- {
- _logger.LogInformation("Try to invoke validator for column {} but it does not exist.", ColumnName);
- return;
- }
- var parameters = method.GetParameters();
- if (parameters.Length == 0)
- {
- throw new Exception("The validator method must have at least one parameter.");
- }
- else if (parameters.Length == 1)
- {
- method.Invoke(null, new object?[] { value });
- }
- else if (parameters.Length == 2)
- {
- if (parameters[0].ParameterType == typeof(ColumnInfo))
- method.Invoke(null, new object?[] { this, value });
- else if (parameters[1].ParameterType == typeof(ColumnInfo))
- method.Invoke(null, new object?[] { value, this });
- else
- throw new Exception("The validator method must have a parameter of type ColumnInfo if it has 2 parameters.");
- }
- else
- {
- throw new Exception("The validator method can only have 1 or 2 parameters.");
- }
- }
-
- public object? InvokeDefaultValueGenerator()
- {
- var method = this.DefaultValueGeneratorMethod;
- if (method is null)
- {
- _logger.LogInformation("Try to invoke default value generator for column {} but it does not exist.", ColumnName);
- return null;
- }
- var parameters = method.GetParameters();
- if (parameters.Length == 0)
- {
- return method.Invoke(null, new object?[0]);
- }
- else if (parameters.Length == 1)
- {
- if (parameters[0].ParameterType != typeof(ColumnInfo))
- throw new Exception("The default value generator method can only have a parameter of type ColumnInfo.");
- return method.Invoke(null, new object?[] { this });
- }
- else
- {
- throw new Exception("The default value generator method can only have 0 or 1 parameter.");
- }
- }
-
- public object? GenerateDefaultValue()
- {
- if (DefaultValueGeneratorMethod is not null)
- {
- return InvokeDefaultValueGenerator();
- }
-
- if (Metadata.TryGetValue(ColumnMetadataKeys.DefaultValue, out object? value))
- {
- return value;
- }
- else
- {
- return null;
- }
- }
-
- public string GenerateCreateTableColumnString(string? dbProviderId = null)
- {
- StringBuilder result = new StringBuilder();
- result.Append(ColumnName);
- result.Append(' ');
- result.Append(ColumnType.GetSqlTypeString(dbProviderId));
- if (IsPrimaryKey)
- {
- result.Append(' ');
- result.Append("PRIMARY KEY");
- }
- else if (IsNotNull)
- {
- result.Append(' ');
- result.Append("NOT NULL");
- }
-
- if (IsAutoIncrement)
- {
- result.Append(' ');
- result.Append("AUTOINCREMENT");
- }
-
- return result.ToString();
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
deleted file mode 100644
index 7247ff1..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-namespace CrupestApi.Commons.Crud;
-
-public static class ColumnMetadataKeys
-{
- public const string ColumnName = nameof(ColumnAttribute.ColumnName);
- public const string NotNull = nameof(ColumnAttribute.NotNull);
- public const string IsPrimaryKey = nameof(ColumnAttribute.IsPrimaryKey);
- public const string Index = nameof(ColumnAttribute.Index);
-
- /// <summary>
- /// This will add hooks for string type column to coerce null to ""(empty string) when get or set. No effect on non-string type.
- /// </summary>
- public const string DefaultEmptyForString = nameof(ColumnAttribute.DefaultEmptyForString);
-
- /// <summary>
- /// This indicates that you take care of generate this column value when create entity. User calling the api can not specify the value.
- /// </summary>
- public const string OnlyGenerated = nameof(ColumnAttribute.OnlyGenerated);
-
- /// <summary>
- /// The default value generator method name in entity type. Default to null, aka, search for ColumnNameDefaultValueGenerator.
- /// Generator has signature <code>static void DefaultValueGenerator(ColumnInfo column)</code>
- /// </summary>
- public const string DefaultValueGenerator = nameof(ColumnAttribute.DefaultValueGenerator);
-
- /// <summary>
- /// The validator method name in entity type. Default to null, aka, the default validator.
- /// Validator has signature <code>static void Validator(ColumnInfo column, object value)</code>
- /// Value param is never null. If you want to mean NULL, it should be a <see cref="DbNullValue"/>.
- /// </summary>
- public const string Validator = nameof(ColumnAttribute.Validator);
-
- /// <summary>
- /// The column can only be set when inserted, can't be changed in update.
- /// </summary>
- /// <returns></returns>
- public const string NoUpdate = nameof(ColumnAttribute.NoUpdate);
-
- /// <summary>
- /// This column acts as key when get one entity for http get method in path.
- /// </summary>
- public const string ActAsKey = nameof(ColumnAttribute.ActAsKey);
-
- /// <summary>
- /// The default value used for the column.
- /// </summary>
- public const string DefaultValue = nameof(ColumnAttribute.DefaultValue);
-}
-
-public interface IColumnMetadata
-{
- bool TryGetValue(string key, out object? value);
-
- object? GetValueOrDefault(string key)
- {
- if (TryGetValue(key, out var value))
- {
- return value;
- }
- else
- {
- return null;
- }
- }
-
- T? GetValueOrDefault<T>(string key)
- {
- return (T?)GetValueOrDefault(key);
- }
-
- object? this[string key]
- {
- get
- {
- if (TryGetValue(key, out var value))
- {
- return value;
- }
- else
- {
- throw new KeyNotFoundException("Key not found.");
- }
- }
- }
-}
-
-public enum ColumnIndexType
-{
- None,
- Unique,
- NonUnique
-}
-
-[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
-public class ColumnAttribute : Attribute, IColumnMetadata
-{
- // if null, use the property name.
- public string? ColumnName { get; init; }
-
- // default false.
- public bool NotNull { get; init; }
-
- // default false
- public bool IsPrimaryKey { get; init; }
-
- // default None
- public ColumnIndexType Index { get; init; } = ColumnIndexType.None;
-
- /// <seealso cref="ColumnMetadataKeys.DefaultEmptyForString"/>
- public bool DefaultEmptyForString { get; init; }
-
- /// <seealso cref="ColumnMetadataKeys.OnlyGenerated"/>
- public bool OnlyGenerated { get; init; }
-
- /// <seealso cref="ColumnMetadataKeys.DefaultValueGenerator"/>
- public string? DefaultValueGenerator { get; init; }
-
- /// <seealso cref="ColumnMetadataKeys.Validator"/>
- public string? Validator { get; init; }
-
- /// <seealso cref="ColumnMetadataKeys.NoUpdate"/>
- public bool NoUpdate { get; init; }
-
- /// <seealso cref="ColumnMetadataKeys.ActAsKey"/>
- public bool ActAsKey { get; init; }
-
- public object? DefaultValue { get; init; }
-
- public bool TryGetValue(string key, out object? value)
- {
- var property = GetType().GetProperty(key);
- if (property is null)
- {
- value = null;
- return false;
- }
- value = property.GetValue(this);
- return true;
- }
-}
-
-public class AggregateColumnMetadata : IColumnMetadata
-{
- private IDictionary<string, object?> _own = new Dictionary<string, object?>();
- private IList<IColumnMetadata> _children = new List<IColumnMetadata>();
-
- public void Add(string key, object? value)
- {
- _own[key] = value;
- }
-
- public void Remove(string key)
- {
- _own.Remove(key);
- }
-
- public void Add(IColumnMetadata child)
- {
- _children.Add(child);
- }
-
- public void Remove(IColumnMetadata child)
- {
- _children.Remove(child);
- }
-
- public bool TryGetValue(string key, out object? value)
- {
- if (_own.ContainsKey(key))
- {
- value = _own[key];
- return true;
- }
-
- bool found = false;
- value = null;
- foreach (var child in _children)
- {
- if (child.TryGetValue(key, out var tempValue))
- {
- value = tempValue;
- found = true;
- }
- }
-
- return found;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs
deleted file mode 100644
index 19eff52..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-using System.Data;
-using System.Diagnostics;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace CrupestApi.Commons.Crud;
-
-public interface IColumnTypeInfo
-{
- public static IColumnTypeInfo BoolColumnTypeInfo { get; } = new SimpleColumnTypeInfo<bool>();
- public static IColumnTypeInfo IntColumnTypeInfo { get; } = new SimpleColumnTypeInfo<int>();
- public static IColumnTypeInfo ShortColumnTypeInfo { get; } = new SimpleColumnTypeInfo<short>();
- public static IColumnTypeInfo SByteColumnTypeInfo { get; } = new SimpleColumnTypeInfo<sbyte>();
- public static IColumnTypeInfo LongColumnTypeInfo { get; } = new SimpleColumnTypeInfo<long>();
- public static IColumnTypeInfo FloatColumnTypeInfo { get; } = new SimpleColumnTypeInfo<float>();
- public static IColumnTypeInfo DoubleColumnTypeInfo { get; } = new SimpleColumnTypeInfo<double>();
- public static IColumnTypeInfo StringColumnTypeInfo { get; } = new SimpleColumnTypeInfo<string>();
- public static IColumnTypeInfo BytesColumnTypeInfo { get; } = new SimpleColumnTypeInfo<byte[]>();
- public static IColumnTypeInfo DateTimeColumnTypeInfo { get; } = new DateTimeColumnTypeInfo();
-
- Type ClrType { get; }
- Type DatabaseClrType { get; }
- bool IsSimple { get { return ClrType == DatabaseClrType; } }
- DbType DbType
- {
- get
- {
- if (DatabaseClrType == typeof(bool))
- {
- return DbType.Boolean;
- }
- else if (DatabaseClrType == typeof(int))
- {
- return DbType.Int32;
- }
- else if (DatabaseClrType == typeof(long))
- {
- return DbType.Int64;
- }
- else if (DatabaseClrType == typeof(short))
- {
- return DbType.Int16;
- }
- else if (DatabaseClrType == typeof(sbyte))
- {
- return DbType.SByte;
- }
- else if (DatabaseClrType == typeof(double))
- {
- return DbType.Double;
- }
- else if (DatabaseClrType == typeof(float))
- {
- return DbType.Single;
- }
- else if (DatabaseClrType == typeof(string))
- {
- return DbType.String;
- }
- else if (DatabaseClrType == typeof(byte[]))
- {
- return DbType.Binary;
- }
- else
- {
- throw new Exception("Can't deduce DbType.");
- }
- }
- }
-
- string GetSqlTypeString(string? dbProviderId = null)
- {
- // Default implementation for SQLite
- return DbType switch
- {
- DbType.String => "TEXT",
- DbType.Boolean or DbType.Int16 or DbType.Int32 or DbType.Int64 => "INTEGER",
- DbType.Single or DbType.Double => "REAL",
- DbType.Binary => "BLOB",
- _ => throw new Exception($"Unsupported DbType: {DbType}"),
- };
- }
-
- JsonConverter? JsonConverter { get { return null; } }
-
- // You must override this method if ClrType != DatabaseClrType
- object? ConvertFromDatabase(object? databaseValue)
- {
- Debug.Assert(IsSimple);
- return databaseValue;
- }
-
- // You must override this method if ClrType != DatabaseClrType
- object? ConvertToDatabase(object? value)
- {
- Debug.Assert(IsSimple);
- return value;
- }
-}
-
-public interface IColumnTypeProvider
-{
- IReadOnlyList<IColumnTypeInfo> GetAll();
- IColumnTypeInfo Get(Type clrType);
-
- IList<IColumnTypeInfo> GetAllCustom()
- {
- return GetAll().Where(t => !t.IsSimple).ToList();
- }
-}
-
-public class SimpleColumnTypeInfo<T> : IColumnTypeInfo
-{
- public Type ClrType => typeof(T);
- public Type DatabaseClrType => typeof(T);
-}
-
-public class DateTimeColumnTypeInfo : IColumnTypeInfo
-{
- private JsonConverter<DateTime> _jsonConverter;
-
- public DateTimeColumnTypeInfo()
- {
- _jsonConverter = new DateTimeJsonConverter(this);
- }
-
- public Type ClrType => typeof(DateTime);
- public Type DatabaseClrType => typeof(string);
-
- public JsonConverter JsonConverter => _jsonConverter;
-
- public object? ConvertToDatabase(object? value)
- {
- if (value is null) return null;
- Debug.Assert(value is DateTime);
- return ((DateTime)value).ToUniversalTime().ToString("s") + "Z";
- }
-
- public object? ConvertFromDatabase(object? databaseValue)
- {
- if (databaseValue is null) return null;
- Debug.Assert(databaseValue is string);
- var databaseString = (string)databaseValue;
- var dateTimeStyles = DateTimeStyles.None;
- if (databaseString.Length > 0 && databaseString[^1] == 'Z')
- {
- databaseString = databaseString.Substring(0, databaseString.Length - 1);
- dateTimeStyles = DateTimeStyles.AssumeUniversal & DateTimeStyles.AdjustToUniversal;
- }
- return DateTime.ParseExact(databaseString, "s", null, dateTimeStyles);
- }
-}
-
-public class DateTimeJsonConverter : JsonConverter<DateTime>
-{
- private readonly DateTimeColumnTypeInfo _typeInfo;
-
- public DateTimeJsonConverter(DateTimeColumnTypeInfo typeInfo)
- {
- _typeInfo = typeInfo;
- }
-
- public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- var databaseValue = reader.GetString();
- return (DateTime)_typeInfo.ConvertFromDatabase(databaseValue)!;
- }
-
- public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
- {
- var databaseValue = _typeInfo.ConvertToDatabase(value);
- writer.WriteStringValue((string)databaseValue!);
- }
-}
-
-public class ColumnTypeProvider : IColumnTypeProvider
-{
- private Dictionary<Type, IColumnTypeInfo> _typeMap = new Dictionary<Type, IColumnTypeInfo>();
-
- public ColumnTypeProvider()
- {
- _typeMap.Add(IColumnTypeInfo.BoolColumnTypeInfo.ClrType, IColumnTypeInfo.BoolColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.IntColumnTypeInfo.ClrType, IColumnTypeInfo.IntColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.ShortColumnTypeInfo.ClrType, IColumnTypeInfo.ShortColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.SByteColumnTypeInfo.ClrType, IColumnTypeInfo.SByteColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.LongColumnTypeInfo.ClrType, IColumnTypeInfo.LongColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.FloatColumnTypeInfo.ClrType, IColumnTypeInfo.FloatColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.DoubleColumnTypeInfo.ClrType, IColumnTypeInfo.DoubleColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.StringColumnTypeInfo.ClrType, IColumnTypeInfo.StringColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.BytesColumnTypeInfo.ClrType, IColumnTypeInfo.BytesColumnTypeInfo);
- _typeMap.Add(IColumnTypeInfo.DateTimeColumnTypeInfo.ClrType, IColumnTypeInfo.DateTimeColumnTypeInfo);
- }
-
- public IReadOnlyList<IColumnTypeInfo> GetAll()
- {
- return _typeMap.Values.ToList();
- }
-
- // This is thread-safe.
- public IColumnTypeInfo Get(Type clrType)
- {
- if (_typeMap.TryGetValue(clrType, out var typeInfo))
- {
- return typeInfo;
- }
- else
- {
- if (clrType.IsGenericType && clrType.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- clrType = clrType.GetGenericArguments()[0];
- return Get(clrType);
- }
-
- throw new Exception($"Unsupported type: {clrType}");
- }
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs
deleted file mode 100644
index 1e881d3..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System.Data;
-using CrupestApi.Commons.Crud.Migrations;
-
-namespace CrupestApi.Commons.Crud;
-
-[Flags]
-public enum UpdateBehavior
-{
- None = 0,
- SaveNull = 1
-}
-
-public class CrudService<TEntity> : IDisposable where TEntity : class
-{
- protected readonly TableInfo _table;
- protected readonly string? _connectionName;
- protected readonly IDbConnection _dbConnection;
- private readonly bool _shouldDisposeConnection;
- private IDatabaseMigrator _migrator;
- private readonly ILogger<CrudService<TEntity>> _logger;
-
- public CrudService(ITableInfoFactory tableInfoFactory, IDbConnectionFactory dbConnectionFactory, IDatabaseMigrator migrator, ILoggerFactory loggerFactory)
- {
- _connectionName = GetConnectionName();
- _table = tableInfoFactory.Get(typeof(TEntity));
- _dbConnection = dbConnectionFactory.Get(_connectionName);
- _shouldDisposeConnection = dbConnectionFactory.ShouldDisposeConnection;
- _migrator = migrator;
- _logger = loggerFactory.CreateLogger<CrudService<TEntity>>();
- }
-
- protected virtual void EnsureDatabase()
- {
- if (_migrator.NeedMigrate(_dbConnection, _table))
- {
- _logger.LogInformation($"Entity {_table.TableName} needs migration.");
- _migrator.AutoMigrate(_dbConnection, _table);
- }
- }
-
- protected virtual string GetConnectionName()
- {
- return typeof(TEntity).Name;
- }
-
- protected virtual void AfterMigrate(IDbConnection dbConnection, TableInfo tableInfo)
- {
-
- }
-
- public void Dispose()
- {
- if (_shouldDisposeConnection)
- _dbConnection.Dispose();
- }
-
- public List<TEntity> GetAll()
- {
- EnsureDatabase();
- var result = _table.Select<TEntity>(_dbConnection, null);
- return result;
- }
-
- public int GetCount()
- {
- EnsureDatabase();
- var result = _table.SelectCount(_dbConnection);
- return result;
- }
-
- public TEntity GetByKey(object key)
- {
- EnsureDatabase();
- var result = _table.Select<TEntity>(_dbConnection, null, WhereClause.Create().Eq(_table.KeyColumn.ColumnName, key)).SingleOrDefault();
- if (result is null)
- {
- throw new EntityNotExistException($"Required entity for key {key} not found.");
- }
- return result;
- }
-
- public IInsertClause ConvertEntityToInsertClauses(TEntity entity)
- {
- var result = new InsertClause();
- foreach (var column in _table.PropertyColumns)
- {
- var value = column.PropertyInfo!.GetValue(entity);
- result.Add(column.ColumnName, value);
- }
- return result;
- }
-
- public object Create(TEntity entity)
- {
- EnsureDatabase();
- var insertClause = ConvertEntityToInsertClauses(entity);
- _table.Insert(_dbConnection, insertClause, out var key);
- return key;
- }
-
- public IUpdateClause ConvertEntityToUpdateClauses(TEntity entity, UpdateBehavior behavior)
- {
- var result = UpdateClause.Create();
- var saveNull = behavior.HasFlag(UpdateBehavior.SaveNull);
- foreach (var column in _table.PropertyColumns)
- {
- var value = column.PropertyInfo!.GetValue(entity);
- if (!saveNull && value is null) continue;
- result.Add(column.ColumnName, value);
- }
- return result;
- }
-
- // Return new key.
- public object UpdateByKey(object key, TEntity entity, UpdateBehavior behavior = UpdateBehavior.None)
- {
- EnsureDatabase();
- var affectedCount = _table.Update(_dbConnection, WhereClause.Create().Eq(_table.KeyColumn.ColumnName, key),
- ConvertEntityToUpdateClauses(entity, behavior), out var newKey);
- if (affectedCount == 0)
- {
- throw new EntityNotExistException($"Required entity for key {key} not found.");
- }
- return newKey ?? key;
- }
-
- public bool DeleteByKey(object key)
- {
- EnsureDatabase();
- return _table.Delete(_dbConnection, WhereClause.Create().Eq(_table.KeyColumn.ColumnName, key)) == 1;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs
deleted file mode 100644
index a7e5193..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using CrupestApi.Commons.Crud.Migrations;
-using CrupestApi.Commons.Secrets;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-
-namespace CrupestApi.Commons.Crud;
-
-public static class CrudServiceCollectionExtensions
-{
- public static IServiceCollection AddCrudCore(this IServiceCollection services)
- {
- services.TryAddSingleton<IDbConnectionFactory, SqliteConnectionFactory>();
- services.TryAddSingleton<IColumnTypeProvider, ColumnTypeProvider>();
- services.TryAddSingleton<ITableInfoFactory, TableInfoFactory>();
- services.TryAddSingleton<IDatabaseMigrator, SqliteDatabaseMigrator>();
- services.AddSecrets();
- return services;
- }
-
- public static IServiceCollection AddCrud<TEntity, TCrudService>(this IServiceCollection services) where TEntity : class where TCrudService : CrudService<TEntity>
- {
- AddCrudCore(services);
-
- services.TryAddScoped<CrudService<TEntity>, TCrudService>();
- services.TryAddScoped<EntityJsonHelper<TEntity>>();
-
- return services;
- }
-
- public static IServiceCollection AddCrud<TEntity>(this IServiceCollection services) where TEntity : class
- {
- return services.AddCrud<TEntity, CrudService<TEntity>>();
- }
-
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs
deleted file mode 100644
index 8942979..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-namespace CrupestApi.Commons.Crud;
-
-public static class CrudWebApplicationExtensions
-{
- public static WebApplication UseCrudCore(this WebApplication app)
- {
- app.Use(async (context, next) =>
- {
- try
- {
- await next();
- }
- catch (EntityNotExistException)
- {
- await context.ResponseMessageAsync("Requested entity does not exist.", StatusCodes.Status404NotFound);
- }
- catch (UserException e)
- {
- await context.ResponseMessageAsync(e.Message);
- }
- });
-
- return app;
- }
-
- public static WebApplication MapCrud<TEntity>(this WebApplication app, string path, string? permission) where TEntity : class
- {
- app.MapGet(path, async (context) =>
- {
- if (!context.RequirePermission(permission)) return;
- var crudService = context.RequestServices.GetRequiredService<CrudService<TEntity>>();
- var entityJsonHelper = context.RequestServices.GetRequiredService<EntityJsonHelper<TEntity>>();
- var allEntities = crudService.GetAll();
- await context.ResponseJsonAsync(allEntities.Select(e => entityJsonHelper.ConvertEntityToDictionary(e)));
- });
-
- app.MapGet(path + "/{key}", async (context) =>
- {
- if (!context.RequirePermission(permission)) return;
- var crudService = context.RequestServices.GetRequiredService<CrudService<TEntity>>();
- var entityJsonHelper = context.RequestServices.GetRequiredService<EntityJsonHelper<TEntity>>();
- var key = context.Request.RouteValues["key"]?.ToString();
- if (key == null)
- {
- await context.ResponseMessageAsync("Please specify a key in path.");
- return;
- }
-
- var entity = crudService.GetByKey(key);
- await context.ResponseJsonAsync(entityJsonHelper.ConvertEntityToDictionary(entity));
- });
-
- app.MapPost(path, async (context) =>
- {
- if (!context.RequirePermission(permission)) return;
- var crudService = context.RequestServices.GetRequiredService<CrudService<TEntity>>();
- var entityJsonHelper = context.RequestServices.GetRequiredService<EntityJsonHelper<TEntity>>();
- var jsonDocument = await context.Request.ReadJsonAsync();
- var key = crudService.Create(entityJsonHelper.ConvertJsonToEntityForInsert(jsonDocument.RootElement));
- await context.ResponseJsonAsync(entityJsonHelper.ConvertEntityToDictionary(crudService.GetByKey(key)));
- });
-
- app.MapPatch(path + "/{key}", async (context) =>
- {
- if (!context.RequirePermission(permission)) return;
- var key = context.Request.RouteValues["key"]?.ToString();
- var crudService = context.RequestServices.GetRequiredService<CrudService<TEntity>>();
- var entityJsonHelper = context.RequestServices.GetRequiredService<EntityJsonHelper<TEntity>>();
- if (key == null)
- {
- await context.ResponseMessageAsync("Please specify a key in path.");
- return;
- }
-
- var jsonDocument = await context.Request.ReadJsonAsync();
- var entity = entityJsonHelper.ConvertJsonToEntityForUpdate(jsonDocument.RootElement, out var updateBehavior);
- var newKey = crudService.UpdateByKey(key, entity, updateBehavior);
- await context.ResponseJsonAsync(entityJsonHelper.ConvertEntityToDictionary(crudService.GetByKey(newKey)));
- });
-
- app.MapDelete(path + "/{key}", async (context) =>
- {
- if (!context.RequirePermission(permission)) return;
- var crudService = context.RequestServices.GetRequiredService<CrudService<TEntity>>();
- var key = context.Request.RouteValues["key"]?.ToString();
- if (key == null)
- {
- await context.ResponseMessageAsync("Please specify a key in path.");
- return;
- }
-
- var deleted = crudService.DeleteByKey(key);
- if (deleted)
- await context.ResponseMessageAsync("Deleted.", StatusCodes.Status200OK);
- else
- await context.ResponseMessageAsync("Not exist.", StatusCodes.Status200OK);
- });
-
- return app;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs
deleted file mode 100644
index 701622c..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System.Data;
-using Microsoft.Data.Sqlite;
-using Microsoft.Extensions.Options;
-
-namespace CrupestApi.Commons.Crud;
-
-public interface IDbConnectionFactory
-{
- IDbConnection Get(string? name = null);
- bool ShouldDisposeConnection { get; }
-}
-
-public class SqliteConnectionFactory : IDbConnectionFactory
-{
- private readonly IOptionsMonitor<CrupestApiConfig> _apiConfigMonitor;
-
- public SqliteConnectionFactory(IOptionsMonitor<CrupestApiConfig> apiConfigMonitor)
- {
- _apiConfigMonitor = apiConfigMonitor;
- }
-
- public IDbConnection Get(string? name = null)
- {
- var connectionString = new SqliteConnectionStringBuilder()
- {
- DataSource = Path.Combine(_apiConfigMonitor.CurrentValue.DataDir, $"{name ?? "crupest-api"}.db"),
- Mode = SqliteOpenMode.ReadWriteCreate
- }.ToString();
-
- var connection = new SqliteConnection(connectionString);
- connection.Open();
- return connection;
- }
-
- public bool ShouldDisposeConnection => true;
-}
-
-public class SqliteMemoryConnectionFactory : IDbConnectionFactory, IDisposable
-{
- private readonly Dictionary<string, IDbConnection> _connections = new();
-
- public IDbConnection Get(string? name = null)
- {
- name = name ?? "crupest-api";
-
- if (_connections.TryGetValue(name, out var connection))
- {
- return connection;
- }
- else
- {
- var connectionString = new SqliteConnectionStringBuilder()
- {
- DataSource = ":memory:",
- Mode = SqliteOpenMode.ReadWriteCreate
- }.ToString();
-
- connection = new SqliteConnection(connectionString);
- _connections.Add(name, connection);
- connection.Open();
- return connection;
- }
- }
-
- public bool ShouldDisposeConnection => false;
-
-
- public void Dispose()
- {
- foreach (var connection in _connections.Values)
- {
- connection.Dispose();
- }
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbNullValue.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbNullValue.cs
deleted file mode 100644
index 5dc5a61..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbNullValue.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace CrupestApi.Commons.Crud;
-
-/// <summary>
-/// This will always represent null value in database.
-/// </summary>
-public class DbNullValue
-{
- public static DbNullValue Instance { get; } = new DbNullValue();
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs
deleted file mode 100644
index cf3f178..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs
+++ /dev/null
@@ -1,206 +0,0 @@
-using System.Globalization;
-using System.Text.Json;
-using Microsoft.Extensions.Options;
-
-namespace CrupestApi.Commons.Crud;
-
-/// <summary>
-/// Contains all you need to do with json.
-/// </summary>
-public class EntityJsonHelper<TEntity> where TEntity : class
-{
- private readonly TableInfo _table;
- private readonly IOptionsMonitor<JsonSerializerOptions> _jsonSerializerOptions;
-
- public EntityJsonHelper(ITableInfoFactory tableInfoFactory, IOptionsMonitor<JsonSerializerOptions> jsonSerializerOptions)
- {
- _table = tableInfoFactory.Get(typeof(TEntity));
- _jsonSerializerOptions = jsonSerializerOptions;
- }
-
- public Dictionary<string, object?> ConvertEntityToDictionary(TEntity entity, bool includeNonColumnProperties = false)
- {
- var result = new Dictionary<string, object?>();
-
- foreach (var column in _table.PropertyColumns)
- {
- var value = column.PropertyInfo!.GetValue(entity);
- var realValue = column.ColumnType.ConvertToDatabase(value);
- result[column.ColumnName] = realValue;
- }
-
- if (includeNonColumnProperties)
- {
- foreach (var propertyInfo in _table.NonColumnProperties)
- {
- var value = propertyInfo.GetValue(entity);
- result[propertyInfo.Name] = value;
- }
- }
-
- return result;
- }
-
- public string ConvertEntityToJson(TEntity entity, bool includeNonColumnProperties = false)
- {
- var dictionary = ConvertEntityToDictionary(entity, includeNonColumnProperties);
- return JsonSerializer.Serialize(dictionary, _jsonSerializerOptions.CurrentValue);
- }
-
- private object? ConvertJsonValue(JsonElement? optionalJsonElement, Type type, string propertyName)
- {
- if (optionalJsonElement is null)
- {
- return null;
- }
-
- var jsonElement = optionalJsonElement.Value;
-
- if (jsonElement.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
- {
- return null;
- }
-
- if (jsonElement.ValueKind is JsonValueKind.String)
- {
- if (type != typeof(string))
- {
- throw new UserException($"Property {propertyName} must be a string.");
- }
- return jsonElement.GetString()!;
- }
-
- if (jsonElement.ValueKind is JsonValueKind.True or JsonValueKind.False)
- {
- if (type != typeof(bool))
- {
- throw new UserException($"Property {propertyName} must be a boolean.");
- }
- return jsonElement.GetBoolean();
- }
-
- if (jsonElement.ValueKind is JsonValueKind.Number)
- {
- try
- {
- return Convert.ChangeType(jsonElement.GetRawText(), type, CultureInfo.InvariantCulture);
- }
- catch (Exception)
- {
- throw new UserException($"Property {propertyName} must be a valid number.");
- }
- }
-
- throw new UserException($"Property {propertyName} is of wrong type.");
- }
-
- public Dictionary<string, JsonElement> ConvertJsonObjectToDictionary(JsonElement jsonElement)
- {
- var result = new Dictionary<string, JsonElement>();
-
- foreach (var property in jsonElement.EnumerateObject())
- {
- result[property.Name.ToLower()] = property.Value;
- }
-
- return result;
- }
-
- public TEntity ConvertJsonToEntityForInsert(JsonElement jsonElement)
- {
- if (jsonElement.ValueKind is not JsonValueKind.Object)
- throw new ArgumentException("The jsonElement must be an object.");
-
- var result = Activator.CreateInstance<TEntity>();
-
- Dictionary<string, JsonElement> jsonProperties = ConvertJsonObjectToDictionary(jsonElement);
-
- foreach (var column in _table.PropertyColumns)
- {
- var jsonPropertyValue = jsonProperties.GetValueOrDefault(column.ColumnName.ToLower());
- var value = ConvertJsonValue(jsonPropertyValue, column.ColumnType.DatabaseClrType, column.ColumnName);
- if (column.IsOnlyGenerated && value is not null)
- {
- throw new UserException($"Property {column.ColumnName} is auto generated, you cannot set it.");
- }
- if (!column.CanBeGenerated && value is null && column.IsNotNull)
- {
- throw new UserException($"Property {column.ColumnName} can NOT be generated, you must set it.");
- }
- var realValue = column.ColumnType.ConvertFromDatabase(value);
- column.PropertyInfo!.SetValue(result, realValue);
- }
-
- return result;
- }
-
- public TEntity ConvertJsonToEntityForInsert(string json)
- {
- var jsonElement = JsonSerializer.Deserialize<JsonElement>(json, _jsonSerializerOptions.CurrentValue);
- return ConvertJsonToEntityForInsert(jsonElement!);
- }
-
- public TEntity ConvertJsonToEntityForUpdate(JsonElement jsonElement, out UpdateBehavior updateBehavior)
- {
- if (jsonElement.ValueKind is not JsonValueKind.Object)
- throw new UserException("The jsonElement must be an object.");
-
- updateBehavior = UpdateBehavior.None;
-
- Dictionary<string, JsonElement> jsonProperties = ConvertJsonObjectToDictionary(jsonElement);
-
- bool saveNull = false;
- if (jsonProperties.TryGetValue("$saveNull".ToLower(), out var saveNullValue))
- {
- if (saveNullValue.ValueKind is JsonValueKind.True)
- {
- updateBehavior |= UpdateBehavior.SaveNull;
- saveNull = true;
- }
- else if (saveNullValue.ValueKind is JsonValueKind.False)
- {
-
- }
- else
- {
- throw new UserException("The $saveNull must be a boolean.");
- }
- }
-
- var result = Activator.CreateInstance<TEntity>();
- foreach (var column in _table.PropertyColumns)
- {
- if (jsonProperties.TryGetValue(column.ColumnName.ToLower(), out var jsonPropertyValue))
- {
- if (jsonPropertyValue.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
- {
- if ((column.IsOnlyGenerated || column.IsNoUpdate) && saveNull)
- {
- throw new UserException($"Property {column.ColumnName} is auto generated or not updatable, you cannot set it.");
- }
-
- column.PropertyInfo!.SetValue(result, null);
- }
- else
- {
- if (column.IsOnlyGenerated || column.IsNoUpdate)
- {
- throw new UserException($"Property {column.ColumnName} is auto generated or not updatable, you cannot set it.");
- }
-
- var value = ConvertJsonValue(jsonPropertyValue, column.ColumnType.DatabaseClrType, column.ColumnName);
- var realValue = column.ColumnType.ConvertFromDatabase(value);
- column.PropertyInfo!.SetValue(result, realValue);
- }
- }
- }
-
- return result;
- }
-
- public TEntity ConvertJsonToEntityForUpdate(string json, out UpdateBehavior updateBehavior)
- {
- var jsonElement = JsonSerializer.Deserialize<JsonElement>(json, _jsonSerializerOptions.CurrentValue);
- return ConvertJsonToEntityForUpdate(jsonElement!, out updateBehavior);
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/IClause.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/IClause.cs
deleted file mode 100644
index 964a669..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/IClause.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Dapper;
-
-namespace CrupestApi.Commons.Crud;
-
-public interface IClause
-{
- IEnumerable<IClause> GetSubclauses()
- {
- return Enumerable.Empty<IClause>();
- }
-
- IEnumerable<string> GetRelatedColumns()
- {
- var subclauses = GetSubclauses();
- var result = new List<string>();
- foreach (var subclause in subclauses)
- {
- var columns = subclause.GetRelatedColumns();
- if (columns is not null)
- result.AddRange(columns);
- }
- return result;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/InsertClause.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/InsertClause.cs
deleted file mode 100644
index a880e66..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/InsertClause.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System.Text;
-
-namespace CrupestApi.Commons.Crud;
-
-public class InsertItem
-{
- /// <summary>
- /// Null means use default value. Use <see cref="DbNullValue"/>.
- /// </summary>
- public InsertItem(string columnName, object? value)
- {
- ColumnName = columnName;
- Value = value;
- }
-
- public string ColumnName { get; set; }
- public object? Value { get; set; }
-}
-
-public interface IInsertClause : IClause
-{
- List<InsertItem> Items { get; }
- string GenerateColumnListSql(string? dbProviderId = null);
- (string sql, ParamList parameters) GenerateValueListSql(string? dbProviderId = null);
-}
-
-public class InsertClause : IInsertClause
-{
- public List<InsertItem> Items { get; } = new List<InsertItem>();
-
- public InsertClause(params InsertItem[] items)
- {
- Items.AddRange(items);
- }
-
- public InsertClause Add(params InsertItem[] items)
- {
- Items.AddRange(items);
- return this;
- }
-
- public InsertClause Add(string column, object? value)
- {
- return Add(new InsertItem(column, value));
- }
-
- public static InsertClause Create(params InsertItem[] items)
- {
- return new InsertClause(items);
- }
-
- public List<string> GetRelatedColumns()
- {
- return Items.Select(i => i.ColumnName).ToList();
- }
-
- public string GenerateColumnListSql(string? dbProviderId = null)
- {
- return string.Join(", ", Items.Select(i => i.ColumnName));
- }
-
- public (string sql, ParamList parameters) GenerateValueListSql(string? dbProviderId = null)
- {
- var parameters = new ParamList();
- var sb = new StringBuilder();
- for (var i = 0; i < Items.Count; i++)
- {
- var item = Items[i];
- var parameterName = parameters.AddRandomNameParameter(item.Value, item.ColumnName);
- sb.Append($"@{parameterName}");
- if (i != Items.Count - 1)
- sb.Append(", ");
- }
-
- return (sb.ToString(), parameters);
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/DatabaseMigrator.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/DatabaseMigrator.cs
deleted file mode 100644
index f1ae616..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/DatabaseMigrator.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System.Data;
-
-namespace CrupestApi.Commons.Crud.Migrations;
-
-public class TableColumn
-{
- public TableColumn(string name, string type, bool notNull, int primaryKey)
- {
- Name = name;
- Type = type;
- NotNull = notNull;
- PrimaryKey = primaryKey;
- }
-
- public string Name { get; set; }
- public string Type { get; set; }
- public bool NotNull { get; set; }
-
- /// <summary>
- /// 0 if not primary key. 1-based index if in primary key.
- /// </summary>
- public int PrimaryKey { get; set; }
-}
-
-public class Table
-{
- public Table(string name)
- {
- Name = name;
- }
-
- public string Name { get; set; }
- public List<TableColumn> Columns { get; set; } = new List<TableColumn>();
-}
-
-public interface IDatabaseMigrator
-{
- Table? GetTable(IDbConnection dbConnection, string tableName);
- Table ConvertTableInfoToTable(TableInfo tableInfo);
- string GenerateCreateTableColumnSqlSegment(TableColumn column);
- string GenerateCreateTableSql(string tableName, IEnumerable<TableColumn> columns);
- bool NeedMigrate(IDbConnection dbConnection, TableInfo tableInfo);
- void AutoMigrate(IDbConnection dbConnection, TableInfo tableInfo);
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/SqliteDatabaseMigrator.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/SqliteDatabaseMigrator.cs
deleted file mode 100644
index 33310d6..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/Migrations/SqliteDatabaseMigrator.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using System.Data;
-using System.Text;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-using Dapper;
-
-namespace CrupestApi.Commons.Crud.Migrations;
-
-public class SqliteDatabaseMigrator : IDatabaseMigrator
-{
- private void CheckTableName(string name)
- {
- if (Regex.Match(name, @"^[_0-9a-zA-Z]+$").Success is false)
- {
- throw new ArgumentException("Fxxk, what have you passed as table name.");
- }
- }
-
- public Table? GetTable(IDbConnection dbConnection, string tableName)
- {
- var count = dbConnection.QuerySingle<int>(
- "SELECT count(*) FROM sqlite_schema WHERE type = 'table' AND name = @TableName;",
- new { TableName = tableName });
- if (count == 0)
- {
- return null;
- }
- else if (count > 1)
- {
- throw new Exception($"More than 1 table has name {tableName}. What happened?");
- }
- else
- {
- var table = new Table(tableName);
- var queryColumns = dbConnection.Query<dynamic>($"PRAGMA table_info({tableName})");
-
- foreach (var column in queryColumns)
- {
- var columnName = (string)column.name;
- var columnType = (string)column.type;
- var isNullable = Convert.ToBoolean(column.notnull);
- var primaryKey = Convert.ToInt32(column.pk);
-
- table.Columns.Add(new TableColumn(columnName, columnType, isNullable, primaryKey));
- }
-
- return table;
- }
- }
-
- public Table ConvertTableInfoToTable(TableInfo tableInfo)
- {
- var table = new Table(tableInfo.TableName);
-
- foreach (var columnInfo in tableInfo.Columns)
- {
- table.Columns.Add(new TableColumn(columnInfo.ColumnName, columnInfo.ColumnType.GetSqlTypeString(),
- columnInfo.IsNotNull, columnInfo.IsPrimaryKey ? 1 : 0));
- }
-
- return table;
- }
-
- public string GenerateCreateTableColumnSqlSegment(TableColumn column)
- {
- StringBuilder result = new StringBuilder();
- result.Append(column.Name);
- result.Append(' ');
- result.Append(column.Type);
- if (column.PrimaryKey is not 0)
- {
- result.Append(" PRIMARY KEY AUTOINCREMENT");
- }
- else if (column.NotNull)
- {
- result.Append(" NOT NULL");
- }
-
- return result.ToString();
- }
-
- public string GenerateCreateTableSql(string tableName, IEnumerable<TableColumn> columns)
- {
- CheckTableName(tableName);
-
- var sql = $@"
-CREATE TABLE {tableName} (
- {string.Join(",\n ", columns.Select(GenerateCreateTableColumnSqlSegment))}
-);
- ".Trim();
-
- return sql;
-
- }
-
- public void AutoMigrate(IDbConnection dbConnection, TableInfo tableInfo)
- {
- var tableName = tableInfo.TableName;
- var databaseTable = GetTable(dbConnection, tableName);
- var wantedTable = ConvertTableInfoToTable(tableInfo);
- var databaseTableColumnNames = databaseTable is null ? new List<string>() : databaseTable.Columns.Select(column => column.Name).ToList();
- var wantedTableColumnNames = wantedTable.Columns.Select(column => column.Name).ToList();
-
- var notChangeColumns = wantedTableColumnNames.Where(column => databaseTableColumnNames.Contains(column)).ToList();
- var addColumns = wantedTableColumnNames.Where(column => !databaseTableColumnNames.Contains(column)).ToList();
-
- if (databaseTable is not null && dbConnection.QuerySingle<int>($"SELECT count(*) FROM {tableName}") > 0)
- {
- foreach (var columnName in addColumns)
- {
- var columnInfo = tableInfo.GetColumn(columnName);
- if (!columnInfo.CanBeGenerated)
- {
- throw new Exception($"Column {columnName} cannot be generated. So we can't auto-migrate.");
- }
- }
- }
-
- // We are sqlite, so it's a little bit difficult.
- using var transaction = dbConnection.BeginTransaction();
-
- if (databaseTable is not null)
- {
- var tempTableName = tableInfo.TableName + "_temp";
- dbConnection.Execute($"ALTER TABLE {tableName} RENAME TO {tempTableName}", new { TableName = tableName, tempTableName });
-
- var createTableSql = GenerateCreateTableSql(tableName, wantedTable.Columns);
- dbConnection.Execute(createTableSql);
-
- // Copy old data to new table.
- var originalRows = dbConnection.Query<dynamic>($"SELECT * FROM {tempTableName}").Cast<IDictionary<string, object?>>().ToList();
- foreach (var originalRow in originalRows)
- {
- var parameters = new DynamicParameters();
-
- foreach (var columnName in notChangeColumns)
- {
- parameters.Add(columnName, originalRow[columnName]);
- }
-
- foreach (var columnName in addColumns)
- {
- parameters.Add(columnName, tableInfo.GetColumn(columnName).GenerateDefaultValue());
- }
-
- string columnSql = string.Join(", ", wantedTableColumnNames);
- string valuesSql = string.Join(", ", wantedTableColumnNames.Select(c => "@" + c));
-
- string sql = $"INSERT INTO {tableName} ({columnSql}) VALUES {valuesSql})";
- dbConnection.Execute(sql, parameters);
- }
-
- // Finally drop old table
- dbConnection.Execute($"DROP TABLE {tempTableName}");
- }
- else
- {
- var createTableSql = GenerateCreateTableSql(tableName, wantedTable.Columns);
- dbConnection.Execute(createTableSql);
- }
-
- // Commit transaction.
- transaction.Commit();
- }
-
- public bool NeedMigrate(IDbConnection dbConnection, TableInfo tableInfo)
- {
- var tableName = tableInfo.TableName;
- var databaseTable = GetTable(dbConnection, tableName);
- var wantedTable = ConvertTableInfoToTable(tableInfo);
- var databaseTableColumns = databaseTable is null ? new HashSet<string>() : new HashSet<string>(databaseTable.Columns.Select(c => c.Name));
- var wantedTableColumns = new HashSet<string>(wantedTable.Columns.Select(c => c.Name));
- return !databaseTableColumns.SetEquals(wantedTableColumns);
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/OrderByClause.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/OrderByClause.cs
deleted file mode 100644
index 734d044..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/OrderByClause.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-namespace CrupestApi.Commons.Crud;
-
-public class OrderByItem
-{
- public OrderByItem(string columnName, bool isAscending)
- {
- ColumnName = columnName;
- IsAscending = isAscending;
- }
-
- public string ColumnName { get; }
- public bool IsAscending { get; }
-
- public string GenerateSql()
- {
- return $"{ColumnName} {(IsAscending ? "ASC" : "DESC")}";
- }
-}
-
-public interface IOrderByClause : IClause
-{
- List<OrderByItem> Items { get; }
- // Contains "ORDER BY" keyword!
- string GenerateSql(string? dbProviderId = null);
-}
-
-public class OrderByClause : IOrderByClause
-{
- public List<OrderByItem> Items { get; } = new List<OrderByItem>();
-
- public OrderByClause(params OrderByItem[] items)
- {
- Items.AddRange(items);
- }
-
- public static OrderByClause Create(params OrderByItem[] items)
- {
- return new OrderByClause(items);
- }
-
- public List<string> GetRelatedColumns()
- {
- return Items.Select(x => x.ColumnName).ToList();
- }
-
- public string GenerateSql(string? dbProviderId = null)
- {
- return "ORDER BY " + string.Join(", ", Items.Select(i => i.GenerateSql()));
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ParamMap.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ParamMap.cs
deleted file mode 100644
index 37d77ca..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ParamMap.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System.Data;
-using System.Diagnostics;
-
-namespace CrupestApi.Commons.Crud;
-
-/// <summary>
-/// <see cref="ColumnName"/> is an optional column name related to the param. You may use it to do some column related things. Like use a more accurate conversion.
-/// </summary>
-/// <remarks>
-/// If value is DbNullValue, it will be treated as null.
-/// </remarks>
-public record ParamInfo(string Name, object? Value, string? ColumnName = null);
-
-public class ParamList : List<ParamInfo>
-{
- private static Random random = new Random();
- private const string chars = "abcdefghijklmnopqrstuvwxyz";
- public static string GenerateRandomKey(int length)
- {
- lock (random)
- {
- var result = new string(Enumerable.Repeat(chars, length)
- .Select(s => s[random.Next(s.Length)]).ToArray());
- return result;
- }
- }
-
- public string GenerateRandomParameterName()
- {
- var parameterName = GenerateRandomKey(10);
- int retryTimes = 1;
- while (ContainsKey(parameterName))
- {
- retryTimes++;
- Debug.Assert(retryTimes <= 100);
- parameterName = GenerateRandomKey(10);
- }
- return parameterName;
- }
-
-
- public bool ContainsKey(string name)
- {
- return this.SingleOrDefault(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) is not null;
- }
-
- public T? Get<T>(string key)
- {
- return (T?)this.SingleOrDefault(p => p.Name.Equals(key, StringComparison.OrdinalIgnoreCase))?.Value;
- }
-
- public object? this[string key]
- {
- get
- {
- return this.SingleOrDefault(p => p.Name.Equals(key, StringComparison.OrdinalIgnoreCase)) ?? throw new KeyNotFoundException("Key not found.");
- }
- }
-
- public void Add(string name, object? value, string? columnName = null)
- {
- Add(new ParamInfo(name, value, columnName));
- }
-
- // Return the random name.
- public string AddRandomNameParameter(object? value, string? columnName = null)
- {
- var parameterName = GenerateRandomParameterName();
- var param = new ParamInfo(parameterName, value, columnName);
- Add(param);
- return parameterName;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md
deleted file mode 100644
index b008ea7..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# CRUD Technic Notes
-
-## Overview
-
-The ultimate CRUD scaffold finally comes.
-
-## Database Pipeline
-
-### Select
-
-1. Create select `what`, where clause, order clause, `Offset` and `Limit`.
-2. Check clauses' related columns are valid.
-3. Generate sql string and param list.
-4. Convert param list to `Dapper` dynamic params with proper type conversion in `IColumnTypeInfo`.
-5. Execute sql and get `dynamic`s.
-6. (Optional) Convert `dynamic`s to `TEntity`s.
-
-### Insert
-
-1. Create insert clause.
-2. Check clauses' related columns are valid.
-3. Create a real empty insert clause.
-4. For each column:
- 1. If insert item exists and value is not null but the column `IsGenerated` is true, throw exception.
- 2. If insert item does not exist or value is `null`, use default value generator to generate value. However, `DbNullValue` always means use `NULL` for that column.
- 3. If value is `null` and the column `IsAutoIncrement` is true, skip to next column.
- 4. Coerce null to `DbNullValue`.
- 5. Run validator to validate the value.
- 6. If value is `DbNullValue`, `IsNotNull` is true, throw exception.
- 7. Add column and value to real insert clause.
-5. Generate sql string and param list.
-6. Convert param list to `Dapper` dynamic params with proper type conversion in `IColumnTypeInfo`.
-7. Execute sql and return `KeyColumn` value.
-
-### Update
-
-1. Create update clause, where clause.
-2. Check clauses' related columns are valid. Then generate sql string and param list.
-3. Create a real empty update clause.
-4. For each column:
- 1. If update item exists and value is not null but the column `IsNoUpdate` is true, throw exception.
- 2. Invoke validator to validate the value.
- 3. If `IsNotNull` is true and value is `DbNullValue`, throw exception.
- 4. Add column and value to real update clause.
-5. Generate sql string and param list.
-6. Convert param list to `Dapper` dynamic params with proper type conversion in `IColumnTypeInfo`.
-7. Execute sql and return count of affected rows.
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs
deleted file mode 100644
index 4a7ea95..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs
+++ /dev/null
@@ -1,628 +0,0 @@
-using System.Data;
-using System.Diagnostics;
-using System.Reflection;
-using System.Text;
-using Dapper;
-
-namespace CrupestApi.Commons.Crud;
-
-/// <summary>
-/// Contains all you need to manipulate a table.
-/// </summary>
-public class TableInfo
-{
- private readonly IColumnTypeProvider _columnTypeProvider;
- private readonly Lazy<List<string>> _lazyColumnNameList;
- private readonly ILoggerFactory _loggerFactory;
- private readonly ILogger<TableInfo> _logger;
-
- public TableInfo(Type entityType, IColumnTypeProvider columnTypeProvider, ILoggerFactory loggerFactory)
- : this(entityType.Name, entityType, columnTypeProvider, loggerFactory)
- {
- }
-
- public TableInfo(string tableName, Type entityType, IColumnTypeProvider columnTypeProvider, ILoggerFactory loggerFactory)
- {
- _loggerFactory = loggerFactory;
- _logger = loggerFactory.CreateLogger<TableInfo>();
-
- _logger.LogInformation("Create TableInfo for entity type '{}'.", entityType.Name);
-
- _columnTypeProvider = columnTypeProvider;
-
- TableName = tableName;
- EntityType = entityType;
-
-
- var properties = entityType.GetProperties();
- _logger.LogInformation("Find following properties: {}", string.Join(", ", properties.Select(p => p.Name)));
-
- var columnInfos = new List<ColumnInfo>();
-
- bool hasId = false;
- ColumnInfo? primaryKeyColumn = null;
- ColumnInfo? keyColumn = null;
-
- List<PropertyInfo> nonColumnProperties = new();
-
- foreach (var property in properties)
- {
- _logger.LogInformation("Check property '{}'.", property.Name);
- if (CheckPropertyIsColumn(property))
- {
- _logger.LogInformation("{} is a column, create ColumnInfo for it.", property.Name);
- var columnInfo = new ColumnInfo(this, property, _columnTypeProvider, _loggerFactory);
- columnInfos.Add(columnInfo);
- if (columnInfo.IsPrimaryKey)
- {
- _logger.LogInformation("Column {} is a primary key.", property.Name);
- primaryKeyColumn = columnInfo;
- }
- if (columnInfo.ColumnName.Equals("id", StringComparison.OrdinalIgnoreCase))
- {
- _logger.LogInformation("Column {} has name id.", property.Name);
- hasId = true;
- }
- if (columnInfo.IsSpecifiedAsKey)
- {
- if (keyColumn is not null)
- {
- throw new Exception("Already exists a key column.");
- }
- _logger.LogInformation("Column {} is specified as key.", property.Name);
- keyColumn = columnInfo;
- }
- }
- else
- {
- _logger.LogInformation("{} is not a column.", property.Name);
- nonColumnProperties.Add(property);
- }
- }
-
- if (primaryKeyColumn is null)
- {
- if (hasId) throw new Exception("A column named id already exists but is not primary key.");
- _logger.LogInformation("No primary key column found, create one automatically.");
- primaryKeyColumn = CreateAutoIdColumn();
- columnInfos.Add(primaryKeyColumn);
- }
-
- if (keyColumn is null)
- {
- _logger.LogInformation("No key column is specified, will use primary key.");
- keyColumn = primaryKeyColumn;
- }
-
- Columns = columnInfos;
- PrimaryKeyColumn = primaryKeyColumn;
- KeyColumn = keyColumn;
- NonColumnProperties = nonColumnProperties;
-
- _logger.LogInformation("Check table validity.");
- CheckValidity();
-
- _logger.LogInformation("TableInfo succeeded to create.");
-
- _lazyColumnNameList = new Lazy<List<string>>(() => Columns.Select(c => c.ColumnName).ToList());
- }
-
- private ColumnInfo CreateAutoIdColumn()
- {
- return new ColumnInfo(this,
- new ColumnAttribute
- {
- ColumnName = "Id",
- NotNull = true,
- IsPrimaryKey = true,
- },
- typeof(long), _columnTypeProvider, _loggerFactory);
- }
-
- public Type EntityType { get; }
- public string TableName { get; }
- public IReadOnlyList<ColumnInfo> Columns { get; }
- public IReadOnlyList<ColumnInfo> PropertyColumns => Columns.Where(c => c.PropertyInfo is not null).ToList();
- public ColumnInfo PrimaryKeyColumn { get; }
- /// <summary>
- /// Maybe not the primary key. But acts as primary key.
- /// </summary>
- /// <seealso cref="ColumnMetadataKeys.ActAsKey"/>
- public ColumnInfo KeyColumn { get; }
- public IReadOnlyList<PropertyInfo> ColumnProperties => PropertyColumns.Select(c => c.PropertyInfo!).ToList();
- public IReadOnlyList<PropertyInfo> NonColumnProperties { get; }
- public IReadOnlyList<string> ColumnNameList => _lazyColumnNameList.Value;
-
- protected bool CheckPropertyIsColumn(PropertyInfo property)
- {
- var columnAttribute = property.GetCustomAttribute<ColumnAttribute>();
- if (columnAttribute is null) return false;
- return true;
- }
-
- public ColumnInfo GetColumn(string columnName)
- {
- foreach (var column in Columns)
- {
- if (column.ColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase))
- {
- return column;
- }
- }
- throw new KeyNotFoundException("No such column with given name.");
- }
-
- public void CheckGeneratedColumnHasGenerator()
- {
- foreach (var column in Columns)
- {
- if (column.IsOnlyGenerated && column.DefaultValueGeneratorMethod is null)
- {
- throw new Exception($"Column '{column.ColumnName}' is generated but has no generator.");
- }
- }
- }
-
- public void CheckValidity()
- {
- // Check if there is only one primary key.
- bool hasPrimaryKey = false;
- bool hasKey = false;
- foreach (var column in Columns)
- {
- if (column.IsPrimaryKey)
- {
- if (hasPrimaryKey) throw new Exception("More than one columns are primary key.");
- hasPrimaryKey = true;
- }
-
- if (column.IsSpecifiedAsKey)
- {
- if (hasKey) throw new Exception("More than one columns are specified as key column.");
- }
- }
-
- if (!hasPrimaryKey) throw new Exception("No column is primary key.");
-
- // Check two columns have the same sql name.
- HashSet<string> sqlNameSet = new HashSet<string>();
-
- foreach (var column in Columns)
- {
- if (sqlNameSet.Contains(column.ColumnName))
- throw new Exception($"Two columns have the same sql name '{column.ColumnName}'.");
- sqlNameSet.Add(column.ColumnName);
- }
-
- CheckGeneratedColumnHasGenerator();
- }
-
- public string GenerateCreateIndexSql(string? dbProviderId = null)
- {
- var sb = new StringBuilder();
-
- foreach (var column in Columns)
- {
- if (column.Index == ColumnIndexType.None) continue;
-
- sb.Append($"CREATE {(column.Index == ColumnIndexType.Unique ? "UNIQUE" : "")} INDEX {TableName}_{column.ColumnName}_index ON {TableName} ({column.ColumnName});\n");
- }
-
- return sb.ToString();
- }
-
- public string GenerateCreateTableSql(bool createIndex = true, string? dbProviderId = null)
- {
- var tableName = TableName;
- var columnSql = string.Join(",\n", Columns.Select(c => c.GenerateCreateTableColumnString(dbProviderId)));
-
- var sql = $@"
-CREATE TABLE {tableName}(
- {columnSql}
-);
- ";
-
- if (createIndex)
- {
- sql += GenerateCreateIndexSql(dbProviderId);
- }
-
- return sql;
- }
-
- public void CheckColumnName(string columnName)
- {
- if (!ColumnNameList.Contains(columnName))
- {
- throw new ArgumentException($"Column {columnName} is not in the table.");
- }
- }
-
- public void CheckRelatedColumns(IClause? clause)
- {
- if (clause is not null)
- {
- var relatedColumns = clause.GetRelatedColumns();
- foreach (var column in relatedColumns)
- {
- CheckColumnName(column);
- }
- }
- }
-
- /// <summary>
- /// If you call this manually, it's your duty to call hooks.
- /// </summary>
- /// <seealso cref="SelectDynamic"/>
- public (string sql, ParamList parameters) GenerateSelectSql(string? selectWhat, IWhereClause? whereClause, IOrderByClause? orderByClause = null, int? skip = null, int? limit = null, string? dbProviderId = null)
- {
- CheckRelatedColumns(whereClause);
- CheckRelatedColumns(orderByClause);
-
- var parameters = new ParamList();
-
- StringBuilder result = new StringBuilder()
- .Append($"SELECT {selectWhat ?? "*"} FROM ")
- .Append(TableName);
-
- if (whereClause is not null)
- {
- result.Append(" WHERE ");
- var (whereSql, whereParameters) = whereClause.GenerateSql(dbProviderId);
- parameters.AddRange(whereParameters);
- result.Append(whereSql);
- }
-
- if (orderByClause is not null)
- {
- result.Append(' ');
- var orderBySql = orderByClause.GenerateSql(dbProviderId);
- result.Append(orderBySql);
- }
-
- if (limit is not null)
- {
- result.Append(" LIMIT @Limit");
- parameters.Add("Limit", limit.Value);
- }
-
- if (skip is not null)
- {
- result.Append(" OFFSET @Skip");
- parameters.Add("Skip", skip.Value);
- }
-
- result.Append(';');
-
- return (result.ToString(), parameters);
- }
-
- /// <summary>
- /// If you call this manually, it's your duty to call hooks.
- /// </summary>
- /// <seealso cref="Insert"/>
- public (string sql, ParamList parameters) GenerateInsertSql(IInsertClause insertClause, string? dbProviderId = null)
- {
- CheckRelatedColumns(insertClause);
-
- var parameters = new ParamList();
-
- var result = new StringBuilder()
- .Append("INSERT INTO ")
- .Append(TableName)
- .Append(" (")
- .Append(insertClause.GenerateColumnListSql(dbProviderId))
- .Append(") VALUES (");
-
- var (valueSql, valueParameters) = insertClause.GenerateValueListSql(dbProviderId);
- result.Append(valueSql).Append(");");
-
- parameters.AddRange(valueParameters);
-
- return (result.ToString(), parameters);
- }
-
- /// <summary>
- /// If you call this manually, it's your duty to call hooks.
- /// </summary>
- /// <seealso cref="Update"/>
- public (string sql, ParamList parameters) GenerateUpdateSql(IWhereClause? whereClause, IUpdateClause updateClause)
- {
- CheckRelatedColumns(whereClause);
- CheckRelatedColumns(updateClause);
-
- var parameters = new ParamList();
-
- StringBuilder sb = new StringBuilder("UPDATE ");
- sb.Append(TableName);
- sb.Append(" SET ");
- var (updateSql, updateParameters) = updateClause.GenerateSql();
- sb.Append(updateSql);
- parameters.AddRange(updateParameters);
- if (whereClause is not null)
- {
- sb.Append(" WHERE ");
- var (whereSql, whereParameters) = whereClause.GenerateSql();
- sb.Append(whereSql);
- parameters.AddRange(whereParameters);
- }
- sb.Append(';');
-
- return (sb.ToString(), parameters);
- }
-
- /// <summary>
- /// If you call this manually, it's your duty to call hooks.
- /// </summary>
- /// <seealso cref="Delete"/>
- public (string sql, ParamList parameters) GenerateDeleteSql(IWhereClause? whereClause)
- {
- CheckRelatedColumns(whereClause);
-
- var parameters = new ParamList();
-
- StringBuilder sb = new StringBuilder("DELETE FROM ");
- sb.Append(TableName);
- if (whereClause is not null)
- {
- sb.Append(" WHERE ");
- var (whereSql, whereParameters) = whereClause.GenerateSql();
- parameters.AddRange(whereParameters);
- sb.Append(whereSql);
- }
- sb.Append(';');
-
- return (sb.ToString(), parameters);
- }
-
- private DynamicParameters ConvertParameters(ParamList parameters)
- {
- var result = new DynamicParameters();
- foreach (var param in parameters)
- {
- if (param.Value is null || param.Value is DbNullValue)
- {
- result.Add(param.Name, null);
- continue;
- }
-
- var columnName = param.ColumnName;
- IColumnTypeInfo typeInfo;
- if (columnName is not null)
- {
- typeInfo = GetColumn(columnName).ColumnType;
- }
- else
- {
- typeInfo = _columnTypeProvider.Get(param.Value.GetType());
- }
-
- result.Add(param.Name, typeInfo.ConvertToDatabase(param.Value), typeInfo.DbType);
- }
- return result;
- }
-
- /// <summary>
- /// ConvertParameters. Select. Call hooks.
- /// </summary>
- public virtual List<dynamic> SelectDynamic(IDbConnection dbConnection, string? what = null, IWhereClause? where = null, IOrderByClause? orderBy = null, int? skip = null, int? limit = null)
- {
- var (sql, parameters) = GenerateSelectSql(what, where, orderBy, skip, limit);
- var queryResult = dbConnection.Query<dynamic>(sql, ConvertParameters(parameters));
- return queryResult.ToList();
- }
-
- public virtual int SelectCount(IDbConnection dbConnection, IWhereClause? where = null, IOrderByClause? orderBy = null, int? skip = null, int? limit = null)
- {
- var (sql, parameters) = GenerateSelectSql("COUNT(*)", where, orderBy, skip, limit);
- var result = dbConnection.QuerySingle<int>(sql, ConvertParameters(parameters));
- return result;
- }
-
- public virtual TResult MapDynamicTo<TResult>(dynamic d)
- {
- var dict = (IDictionary<string, object?>)d;
-
- var result = Activator.CreateInstance<TResult>();
- Type resultType = typeof(TResult);
-
- foreach (var column in Columns)
- {
- var resultProperty = resultType.GetProperty(column.ColumnName);
- if (dict.ContainsKey(column.ColumnName) && resultProperty is not null)
- {
- if (dict[column.ColumnName] is null)
- {
- resultProperty.SetValue(result, null);
- continue;
- }
- object? value = Convert.ChangeType(dict[column.ColumnName], column.ColumnType.DatabaseClrType);
- value = column.ColumnType.ConvertFromDatabase(value);
- resultProperty.SetValue(result, value);
- }
- }
-
- return result;
- }
-
- /// <summary>
- /// Select and call hooks.
- /// </summary>
- public virtual List<TResult> Select<TResult>(IDbConnection dbConnection, string? what = null, IWhereClause? where = null, IOrderByClause? orderBy = null, int? skip = null, int? limit = null)
- {
- List<dynamic> queryResult = SelectDynamic(dbConnection, what, where, orderBy, skip, limit).ToList();
-
- return queryResult.Select(MapDynamicTo<TResult>).ToList();
- }
-
- public IInsertClause ConvertEntityToInsertClause(object entity)
- {
- Debug.Assert(EntityType.IsInstanceOfType(entity));
- var result = new InsertClause();
- foreach (var column in PropertyColumns)
- {
- var value = column.PropertyInfo!.GetValue(entity);
- result.Add(column.ColumnName, value);
- }
- return result;
- }
-
- /// <summary>
- /// Insert a entity and call hooks.
- /// </summary>
- /// <returns>The key of insert entity.</returns>
- public int Insert(IDbConnection dbConnection, IInsertClause insert, out object key)
- {
- object? finalKey = null;
-
- var realInsert = InsertClause.Create();
-
- foreach (var column in Columns)
- {
- InsertItem? item = insert.Items.SingleOrDefault(i => i.ColumnName == column.ColumnName);
-
- var value = item?.Value;
-
- if (column.IsOnlyGenerated && value is not null)
- {
- throw new Exception($"The column '{column.ColumnName}' is auto generated. You can't specify it explicitly.");
- }
-
- if (value is null)
- {
- value = column.GenerateDefaultValue();
- }
-
- if (value is null && column.IsAutoIncrement)
- {
- continue;
- }
-
- if (value is null)
- {
- value = DbNullValue.Instance;
- }
-
- column.InvokeValidator(value);
-
- InsertItem realInsertItem;
-
- if (value is DbNullValue)
- {
- if (column.IsNotNull)
- {
- throw new Exception($"Column '{column.ColumnName}' is not nullable. Please specify a non-null value.");
- }
-
- realInsertItem = new InsertItem(column.ColumnName, null);
- }
- else
- {
- realInsertItem = new InsertItem(column.ColumnName, value);
- }
-
- realInsert.Add(realInsertItem);
-
- if (realInsertItem.ColumnName == KeyColumn.ColumnName)
- {
- finalKey = realInsertItem.Value;
- }
- }
-
- if (finalKey is null) throw new Exception("No key???");
- key = finalKey;
-
- var (sql, parameters) = GenerateInsertSql(realInsert);
-
- var affectedRowCount = dbConnection.Execute(sql, ConvertParameters(parameters));
-
- if (affectedRowCount != 1)
- throw new Exception("Failed to insert.");
-
- return affectedRowCount;
- }
-
- /// <summary>
- /// Upgrade a entity and call hooks.
- /// </summary>
- /// <returns>The key of insert entity.</returns>
- public virtual int Update(IDbConnection dbConnection, IWhereClause? where, IUpdateClause update, out object? newKey)
- {
- newKey = null;
-
- var realUpdate = UpdateClause.Create();
-
- foreach (var column in Columns)
- {
- UpdateItem? item = update.Items.FirstOrDefault(i => i.ColumnName == column.ColumnName);
- object? value = item?.Value;
-
- if (value is not null)
- {
- if (column.IsNoUpdate)
- {
- throw new Exception($"The column '{column.ColumnName}' can't be update.");
- }
-
- column.InvokeValidator(value);
-
- realUpdate.Add(column.ColumnName, value);
-
- if (column.ColumnName == KeyColumn.ColumnName)
- {
- newKey = value;
- }
- }
- }
-
- var (sql, parameters) = GenerateUpdateSql(where, realUpdate);
- return dbConnection.Execute(sql, ConvertParameters(parameters));
- }
-
- public virtual int Delete(IDbConnection dbConnection, IWhereClause? where)
- {
- var (sql, parameters) = GenerateDeleteSql(where);
- return dbConnection.Execute(sql, ConvertParameters(parameters));
- }
-}
-
-public interface ITableInfoFactory
-{
- TableInfo Get(Type type);
-}
-
-public class TableInfoFactory : ITableInfoFactory
-{
- private readonly Dictionary<Type, TableInfo> _cache = new Dictionary<Type, TableInfo>();
- private readonly IColumnTypeProvider _columnTypeProvider;
- private readonly ILoggerFactory _loggerFactory;
- private readonly ILogger<TableInfoFactory> _logger;
-
- public TableInfoFactory(IColumnTypeProvider columnTypeProvider, ILoggerFactory loggerFactory)
- {
- _columnTypeProvider = columnTypeProvider;
- _loggerFactory = loggerFactory;
- _logger = loggerFactory.CreateLogger<TableInfoFactory>();
- }
-
- // This is thread-safe.
- public TableInfo Get(Type type)
- {
- lock (_cache)
- {
- if (_cache.TryGetValue(type, out var tableInfo))
- {
- _logger.LogDebug("Table info of type '{}' is cached, return it.", type.Name);
- return tableInfo;
- }
- else
- {
- _logger.LogDebug("Table info for type '{}' is not in cache, create it.", type.Name);
- tableInfo = new TableInfo(type, _columnTypeProvider, _loggerFactory);
- _logger.LogDebug("Table info for type '{}' is created, add it to cache.", type.Name);
- _cache.Add(type, tableInfo);
- return tableInfo;
- }
- }
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UpdateClause.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UpdateClause.cs
deleted file mode 100644
index de5c6c3..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UpdateClause.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System.Text;
-
-namespace CrupestApi.Commons.Crud;
-
-public class UpdateItem
-{
- public UpdateItem(string columnName, object? value)
- {
- ColumnName = columnName;
- Value = value;
- }
-
- public string ColumnName { get; set; }
- public object? Value { get; set; }
-}
-
-public interface IUpdateClause : IClause
-{
- List<UpdateItem> Items { get; }
- (string sql, ParamList parameters) GenerateSql();
-}
-
-public class UpdateClause : IUpdateClause
-{
- public List<UpdateItem> Items { get; } = new List<UpdateItem>();
-
- public UpdateClause(IEnumerable<UpdateItem> items)
- {
- Items.AddRange(items);
- }
-
- public UpdateClause(params UpdateItem[] items)
- {
- Items.AddRange(items);
- }
-
- public UpdateClause Add(params UpdateItem[] items)
- {
- Items.AddRange(items);
- return this;
- }
-
- public UpdateClause Add(string column, object? value)
- {
- return Add(new UpdateItem(column, value));
- }
-
- public static UpdateClause Create(params UpdateItem[] items)
- {
- return new UpdateClause(items);
- }
-
- public List<string> GetRelatedColumns()
- {
- return Items.Select(i => i.ColumnName).ToList();
- }
-
- public (string sql, ParamList parameters) GenerateSql()
- {
- var parameters = new ParamList();
-
- StringBuilder result = new StringBuilder();
-
- foreach (var item in Items)
- {
- if (result.Length > 0)
- {
- result.Append(", ");
- }
-
- var parameterName = parameters.AddRandomNameParameter(item.Value, item.ColumnName);
- result.Append($"{item.ColumnName} = @{parameterName}");
- }
-
- return (result.ToString(), parameters);
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UserException.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UserException.cs
deleted file mode 100644
index 1a10b97..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/UserException.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace CrupestApi.Commons.Crud;
-
-/// <summary>
-/// This exception means the exception is caused by user and can be safely shown to user.
-/// </summary>
-[System.Serializable]
-public class UserException : Exception
-{
- public UserException() { }
- public UserException(string message) : base(message) { }
- public UserException(string message, System.Exception inner) : base(message, inner) { }
- protected UserException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs
deleted file mode 100644
index de69f2f..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-using System.Text;
-
-namespace CrupestApi.Commons.Crud;
-
-public interface IWhereClause : IClause
-{
- // Does not contain "WHERE" keyword!
- (string sql, ParamList parameters) GenerateSql(string? dbProviderId = null);
-}
-
-public class CompositeWhereClause : IWhereClause
-{
- public CompositeWhereClause(string concatOp, bool parenthesesSubclause, params IWhereClause[] subclauses)
- {
- ConcatOp = concatOp;
- ParenthesesSubclause = parenthesesSubclause;
- Subclauses = subclauses.ToList();
- }
-
- public string ConcatOp { get; }
- public bool ParenthesesSubclause { get; }
- public List<IWhereClause> Subclauses { get; }
-
- public CompositeWhereClause Eq(string column, object? value)
- {
- Subclauses.Add(SimpleCompareWhereClause.Eq(column, value));
- return this;
- }
-
- public (string sql, ParamList parameters) GenerateSql(string? dbProviderId = null)
- {
- var parameters = new ParamList();
- var sql = new StringBuilder();
- var subclauses = GetSubclauses();
- if (subclauses is null) return ("", new());
- var first = true;
- foreach (var subclause in Subclauses)
- {
- var (subSql, subParameters) = subclause.GenerateSql(dbProviderId);
- if (subSql is null) continue;
- if (first)
- {
- first = false;
- }
- else
- {
- sql.Append($" {ConcatOp} ");
- }
- if (ParenthesesSubclause)
- {
- sql.Append("(");
- }
- sql.Append(subSql);
- if (ParenthesesSubclause)
- {
- sql.Append(")");
- }
- parameters.AddRange(subParameters);
- }
- return (sql.ToString(), parameters);
- }
-
- public object GetSubclauses()
- {
- return Subclauses;
- }
-}
-
-public class AndWhereClause : CompositeWhereClause
-{
- public AndWhereClause(params IWhereClause[] clauses)
- : this(true, clauses)
- {
-
- }
-
- public AndWhereClause(bool parenthesesSubclause, params IWhereClause[] clauses)
- : base("AND", parenthesesSubclause, clauses)
- {
-
- }
-
- public static AndWhereClause Create(params IWhereClause[] clauses)
- {
- return new AndWhereClause(clauses);
- }
-}
-
-public class OrWhereClause : CompositeWhereClause
-{
- public OrWhereClause(params IWhereClause[] clauses)
- : this(true, clauses)
- {
-
- }
-
- public OrWhereClause(bool parenthesesSubclause, params IWhereClause[] clauses)
- : base("OR", parenthesesSubclause, clauses)
- {
-
- }
-
- public static OrWhereClause Create(params IWhereClause[] clauses)
- {
- return new OrWhereClause(clauses);
- }
-}
-
-// It's simple because it only compare column and value but not expressions.
-public class SimpleCompareWhereClause : IWhereClause
-{
- public string Column { get; }
- public string Operator { get; }
- public object? Value { get; }
-
- public List<string> GetRelatedColumns()
- {
- return new List<string> { Column };
- }
-
- // It's user's responsibility to keep column safe, with proper escape.
- public SimpleCompareWhereClause(string column, string op, object? value)
- {
- Column = column;
- Operator = op;
- Value = value;
- }
-
- public static SimpleCompareWhereClause Create(string column, string op, object? value)
- {
- return new SimpleCompareWhereClause(column, op, value);
- }
-
- public static SimpleCompareWhereClause Eq(string column, object? value)
- {
- return new SimpleCompareWhereClause(column, "=", value);
- }
-
- public static SimpleCompareWhereClause Neq(string column, object? value)
- {
- return new SimpleCompareWhereClause(column, "<>", value);
- }
-
- public static SimpleCompareWhereClause Gt(string column, object? value)
- {
- return new SimpleCompareWhereClause(column, ">", value);
- }
-
- public static SimpleCompareWhereClause Gte(string column, object? value)
- {
- return new SimpleCompareWhereClause(column, ">=", value);
- }
-
- public static SimpleCompareWhereClause Lt(string column, object? value)
- {
- return new SimpleCompareWhereClause(column, "<", value);
- }
-
- public static SimpleCompareWhereClause Lte(string column, object? value)
- {
- return new SimpleCompareWhereClause(column, "<=", value);
- }
-
- public (string sql, ParamList parameters) GenerateSql(string? dbProviderId = null)
- {
- var parameters = new ParamList();
- var parameterName = parameters.AddRandomNameParameter(Value, Column);
- return ($"{Column} {Operator} @{parameterName}", parameters);
- }
-}
-
-public class WhereClause : AndWhereClause
-{
- public WhereClause()
- {
- }
-
- public void Add(IWhereClause subclause)
- {
- Subclauses.Add(subclause);
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/CrupestApi.Commons.csproj b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/CrupestApi.Commons.csproj
deleted file mode 100644
index 8e291fa..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/CrupestApi.Commons.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
- <PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
- <TargetType>library</TargetType>
- <Nullable>enable</Nullable>
- <ImplicitUsings>enable</ImplicitUsings>
- <SelfContained>false</SelfContained>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Dapper" Version="2.0.123" />
- <PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.0" />
- </ItemGroup>
-
-</Project> \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/EntityNotExistException.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/EntityNotExistException.cs
deleted file mode 100644
index 0e1f4f4..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/EntityNotExistException.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace CrupestApi.Commons;
-
-public class EntityNotExistException : Exception
-{
- public EntityNotExistException() { }
- public EntityNotExistException(string message) : base(message) { }
- public EntityNotExistException(string message, Exception inner) : base(message, inner) { }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/HttpContextExtensions.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/HttpContextExtensions.cs
deleted file mode 100644
index a0b2d89..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/HttpContextExtensions.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System.Text.Json;
-using CrupestApi.Commons.Secrets;
-using Microsoft.Extensions.Options;
-
-namespace CrupestApi.Commons;
-
-public delegate void HttpResponseAction(HttpResponse response);
-
-public class MessageBody
-{
- public MessageBody(string message)
- {
- Message = message;
- }
-
- public string Message { get; set; }
-}
-
-public static class CrupestApiJsonExtensions
-{
- public static IServiceCollection AddJsonOptions(this IServiceCollection services)
- {
- services.AddOptions<JsonSerializerOptions>();
- services.Configure<JsonSerializerOptions>(config =>
- {
- config.AllowTrailingCommas = true;
- config.PropertyNameCaseInsensitive = true;
- config.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
- });
-
- return services;
- }
-
- public static async Task<JsonDocument> ReadJsonAsync(this HttpRequest request)
- {
- var jsonOptions = request.HttpContext.RequestServices.GetRequiredService<IOptionsSnapshot<JsonSerializerOptions>>();
- using var stream = request.Body;
- var body = await JsonSerializer.DeserializeAsync<JsonDocument>(stream, jsonOptions.Value);
- return body!;
- }
-
- public static async Task WriteJsonAsync<T>(this HttpResponse response, T bodyObject, int statusCode = 200, HttpResponseAction? beforeWriteBody = null, CancellationToken cancellationToken = default)
- {
- var jsonOptions = response.HttpContext.RequestServices.GetRequiredService<IOptionsSnapshot<JsonSerializerOptions>>();
- byte[] json = JsonSerializer.SerializeToUtf8Bytes<T>(bodyObject, jsonOptions.Value);
-
- var byteCount = json.Length;
-
- response.StatusCode = statusCode;
- response.Headers.ContentType = "application/json; charset=utf-8";
- response.Headers.ContentLength = byteCount;
-
- if (beforeWriteBody is not null)
- {
- beforeWriteBody(response);
- }
-
- await response.Body.WriteAsync(json, cancellationToken);
- }
-
- public static async Task WriteMessageAsync(this HttpResponse response, string message, int statusCode = 400, HttpResponseAction? beforeWriteBody = null, CancellationToken cancellationToken = default)
- {
- await response.WriteJsonAsync(new MessageBody(message), statusCode: statusCode, beforeWriteBody, cancellationToken);
- }
-
- public static Task ResponseJsonAsync<T>(this HttpContext context, T bodyObject, int statusCode = 200, HttpResponseAction? beforeWriteBody = null, CancellationToken cancellationToken = default)
- {
- return context.Response.WriteJsonAsync<T>(bodyObject, statusCode, beforeWriteBody, cancellationToken);
- }
-
- public static Task ResponseMessageAsync(this HttpContext context, string message, int statusCode = 400, HttpResponseAction? beforeWriteBody = null, CancellationToken cancellationToken = default)
- {
- return context.Response.WriteMessageAsync(message, statusCode, beforeWriteBody, cancellationToken);
- }
-
- public static string? GetToken(this HttpRequest request)
- {
- var token = request.Headers["Authorization"].ToString();
- if (token.StartsWith("Bearer "))
- {
- token = token.Substring("Bearer ".Length);
- return token;
- }
-
- if (request.Query.TryGetValue("token", out var tokenValues))
- {
- return tokenValues.Last();
- }
-
- return null;
- }
-
- public static bool RequirePermission(this HttpContext context, string? permission)
- {
- if (permission is null) return true;
-
- var token = context.Request.GetToken();
- if (token is null)
- {
- context.ResponseMessageAsync("Unauthorized", 401);
- return false;
- }
-
- var secretService = context.RequestServices.GetRequiredService<ISecretService>();
- var permissions = secretService.GetPermissions(token);
- if (!permissions.Contains(permission))
- {
- context.ResponseMessageAsync("Forbidden", 403);
- return false;
- }
- return true;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/ISecretService.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/ISecretService.cs
deleted file mode 100644
index 83025f8..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/ISecretService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace CrupestApi.Commons.Secrets;
-
-public interface ISecretService
-{
- void CreateTestSecret(string key, string secret);
-
- List<string> GetPermissions(string secret);
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretInfo.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretInfo.cs
deleted file mode 100644
index c3a4de0..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretInfo.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Security.Cryptography;
-using System.Text;
-using CrupestApi.Commons.Crud;
-
-namespace CrupestApi.Commons.Secrets;
-
-public class SecretInfo
-{
- [Column(NotNull = true)]
- public string Key { get; set; } = default!;
- [Column(NotNull = true, NoUpdate = true, ActAsKey = true)]
- public string Secret { get; set; } = default!;
- [Column(DefaultEmptyForString = true)]
- public string Description { get; set; } = default!;
- [Column(NotNull = false)]
- public DateTime? ExpireTime { get; set; }
- [Column(NotNull = true, DefaultValue = false)]
- public bool Revoked { get; set; }
- [Column(NotNull = true)]
- public DateTime CreateTime { get; set; }
-
- private static RandomNumberGenerator RandomNumberGenerator = RandomNumberGenerator.Create();
-
- private static string GenerateRandomKey(int length)
- {
- const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- var result = new StringBuilder(length);
- lock (RandomNumberGenerator)
- {
- for (int i = 0; i < length; i++)
- {
- result.Append(chars[RandomNumberGenerator.GetInt32(chars.Length)]);
- }
- }
- return result.ToString();
- }
-
-
- public static string SecretDefaultValueGenerator()
- {
- return GenerateRandomKey(16);
- }
-
- public static DateTime CreateTimeDefaultValueGenerator()
- {
- return DateTime.UtcNow;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretService.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretService.cs
deleted file mode 100644
index c693d8d..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretService.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Data;
-using CrupestApi.Commons.Crud;
-using CrupestApi.Commons.Crud.Migrations;
-
-namespace CrupestApi.Commons.Secrets;
-
-public class SecretService : CrudService<SecretInfo>, ISecretService
-{
- private readonly ILogger<SecretService> _logger;
-
- public SecretService(ITableInfoFactory tableInfoFactory, IDbConnectionFactory dbConnectionFactory, IDatabaseMigrator migrator, ILoggerFactory loggerFactory)
- : base(tableInfoFactory, dbConnectionFactory, migrator, loggerFactory)
- {
- _logger = loggerFactory.CreateLogger<SecretService>();
- }
-
- protected override void AfterMigrate(IDbConnection connection, TableInfo table)
- {
- if (table.SelectCount(connection) == 0)
- {
- _logger.LogInformation("No secrets found, insert default secrets.");
- using var transaction = connection.BeginTransaction();
- var insertClause = InsertClause.Create()
- .Add(nameof(SecretInfo.Key), SecretsConstants.SecretManagementKey)
- .Add(nameof(SecretInfo.Secret), "crupest")
- .Add(nameof(SecretInfo.Description), "This is the init key. Please revoke it immediately after creating a new one.");
- _table.Insert(connection, insertClause, out var _);
- transaction.Commit();
- }
- }
-
- public void CreateTestSecret(string key, string secret)
- {
- var connection = _dbConnection;
- var insertClause = InsertClause.Create()
- .Add(nameof(SecretInfo.Key), key)
- .Add(nameof(SecretInfo.Secret), secret)
- .Add(nameof(SecretInfo.Description), "Test secret.");
- _table.Insert(connection, insertClause, out var _);
- }
-
- public List<string> GetPermissions(string secret)
- {
- var list = _table.Select<SecretInfo>(_dbConnection,
- where: WhereClause.Create().Eq(nameof(SecretInfo.Secret), secret));
- return list.Select(x => x.Key).ToList();
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretServiceCollectionExtensions.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretServiceCollectionExtensions.cs
deleted file mode 100644
index a9c0e5f..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.Extensions.DependencyInjection.Extensions;
-
-namespace CrupestApi.Commons.Secrets;
-
-public static class SecretServiceCollectionExtensions
-{
- public static IServiceCollection AddSecrets(this IServiceCollection services)
- {
- services.TryAddScoped<ISecretService, SecretService>();
- return services;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretsConstants.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretsConstants.cs
deleted file mode 100644
index 207cc45..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Commons/Secrets/SecretsConstants.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace CrupestApi.Commons.Secrets;
-
-public static class SecretsConstants
-{
- public const string SecretManagementKey = "crupest.secrets.management";
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/CrupestApi.Files.csproj b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/CrupestApi.Files.csproj
deleted file mode 100644
index 2221809..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/CrupestApi.Files.csproj
+++ /dev/null
@@ -1,20 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
- <ItemGroup>
- <ProjectReference Include="..\CrupestApi.Commons\CrupestApi.Commons.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="Dapper" Version="2.0.123" />
- <PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.0" />
- </ItemGroup>
-
- <PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
- <TargetType>library</TargetType>
- <Nullable>enable</Nullable>
- <ImplicitUsings>enable</ImplicitUsings>
- <SelfContained>false</SelfContained>
- </PropertyGroup>
-
-</Project>
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/FilesService.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/FilesService.cs
deleted file mode 100644
index c851a92..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Files/FilesService.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace CrupestApi.Files;
-
-public class FilesService
-{
-
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/CrupestApi.Secrets.csproj b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/CrupestApi.Secrets.csproj
deleted file mode 100644
index 70c83f3..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/CrupestApi.Secrets.csproj
+++ /dev/null
@@ -1,20 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
- <ItemGroup>
- <ProjectReference Include="..\CrupestApi.Commons\CrupestApi.Commons.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="Dapper" Version="2.0.123" />
- <PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.0" />
- </ItemGroup>
-
- <PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
- <TargetType>library</TargetType>
- <Nullable>enable</Nullable>
- <ImplicitUsings>enable</ImplicitUsings>
- <SelfContained>false</SelfContained>
- </PropertyGroup>
-
-</Project>
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsExtensions.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsExtensions.cs
deleted file mode 100644
index e09887b..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Secrets/SecretsExtensions.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using CrupestApi.Commons.Secrets;
-using CrupestApi.Commons.Crud;
-
-namespace CrupestApi.Secrets;
-
-public static class SecretsExtensions
-{
- public static IServiceCollection AddSecrets(this IServiceCollection services)
- {
- services.AddCrud<SecretInfo, SecretService>();
- return services;
- }
-
- public static WebApplication MapSecrets(this WebApplication webApplication, string path = "/api/secrets")
- {
- webApplication.MapCrud<SecretInfo>(path, SecretsConstants.SecretManagementKey);
- return webApplication;
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/CrupestApi.Todos.csproj b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/CrupestApi.Todos.csproj
deleted file mode 100644
index 86460e3..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/CrupestApi.Todos.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
- <ItemGroup>
- <ProjectReference Include="..\CrupestApi.Commons\CrupestApi.Commons.csproj" />
- </ItemGroup>
-
- <PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
- <TargetType>library</TargetType>
- <Nullable>enable</Nullable>
- <ImplicitUsings>enable</ImplicitUsings>
- <SelfContained>false</SelfContained>
- </PropertyGroup>
-
-</Project>
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosConfiguration.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosConfiguration.cs
deleted file mode 100644
index e8160d2..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosConfiguration.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace CrupestApi.Todos;
-
-public class TodosConfiguration
-{
- [Required]
- public string Username { get; set; } = default!;
- [Required]
- public int ProjectNumber { get; set; } = default!;
- [Required]
- public string Token { get; set; } = default!;
- public int Count { get; set; }
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosService.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosService.cs
deleted file mode 100644
index 5839086..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosService.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using System.Net.Http.Headers;
-using System.Net.Mime;
-using System.Text;
-using System.Text.Json;
-using Microsoft.Extensions.Options;
-
-namespace CrupestApi.Todos;
-
-public class TodosItem
-{
- public string Status { get; set; } = default!;
- public string Title { get; set; } = default!;
- public bool Closed { get; set; }
- public string Color { get; set; } = default!;
-}
-
-public class TodosService
-{
- private readonly IOptionsSnapshot<TodosConfiguration> _options;
- private readonly ILogger<TodosService> _logger;
-
- public TodosService(IOptionsSnapshot<TodosConfiguration> options, ILogger<TodosService> logger)
- {
- _options = options;
- _logger = logger;
- }
-
- private static string CreateGraphQLQuery(TodosConfiguration todoConfiguration)
- {
- return $$"""
-{
- user(login: "{{todoConfiguration.Username}}") {
- projectV2(number: {{todoConfiguration.ProjectNumber}}) {
- items(last: {{todoConfiguration.Count}}) {
- nodes {
- fieldValueByName(name: "Status") {
- ... on ProjectV2ItemFieldSingleSelectValue {
- name
- }
- }
- content {
- __typename
- ... on Issue {
- title
- closed
- }
- ... on PullRequest {
- title
- closed
- }
- ... on DraftIssue {
- title
- }
- }
- }
- }
- }
- }
-}
-""";
- }
-
-
- public async Task<List<TodosItem>> GetTodosAsync()
- {
- var todoOptions = _options.Value;
- if (todoOptions is null)
- {
- throw new Exception("Fail to get todos configuration.");
- }
-
- _logger.LogInformation("Username: {}; ProjectNumber: {}; Count: {}", todoOptions.Username, todoOptions.ProjectNumber, todoOptions.Count);
- _logger.LogInformation("Getting todos from GitHub GraphQL API...");
-
- using var httpClient = new HttpClient();
-
- using var requestContent = new StringContent(JsonSerializer.Serialize(new
- {
- query = CreateGraphQLQuery(todoOptions)
- }));
- 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", todoOptions.Token);
- request.Headers.TryAddWithoutValidation("User-Agent", todoOptions.Username);
-
- using var response = await httpClient.SendAsync(request);
- var responseBody = await response.Content.ReadAsStringAsync();
-
- _logger.LogInformation("GitHub server returned status code: {}", response.StatusCode);
- _logger.LogInformation("GitHub server returned body: {}", responseBody);
-
- if (response.IsSuccessStatusCode)
- {
- 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 result = new List<TodosItem>();
-
- 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.");
- }
-
- bool done = false;
-
- var statusField = node.GetProperty("fieldValueByName");
- if (statusField.ValueKind != JsonValueKind.Null) // if there is a "Status" field
- {
- var statusName = statusField.GetProperty("name").GetString();
- if (statusName is null)
- {
- throw new Exception("Fail to get status.");
- }
-
- // if name is "Done", then it is closed, otherwise we check if the issue is closed
- if (statusName.Equals("Done", StringComparison.OrdinalIgnoreCase))
- {
- done = true;
- }
- }
-
- JsonElement closedElement;
- // if item has a "closed" field, then it is a pull request or an issue, and we check if it is closed
- if (content.TryGetProperty("closed", out closedElement) && closedElement.GetBoolean())
- {
- done = true;
- }
-
- // If item "Status" field is "Done' or item is a pull request or issue and it is closed, then it is done.
- // Otherwise it is not closed. Like:
- // 1. it is a draft issue with no "Status" field or "Status" field is not "Done"
- // 2. it is a pull request or issue with no "Status" field or "Status" field is not "Done" and it is not closed
-
- result.Add(new TodosItem
- {
- Title = title,
- Status = done ? "Done" : "Todo",
- Closed = done,
- Color = done ? "green" : "blue"
- });
- }
-
- return result;
- }
- else
- {
- const string message = "Fail to get todos from GitHub.";
- _logger.LogError(message);
- throw new Exception(message);
- }
- }
-}
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosServiceCollectionExtensions.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosServiceCollectionExtensions.cs
deleted file mode 100644
index a49d55d..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Microsoft.Extensions.DependencyInjection.Extensions;
-
-namespace CrupestApi.Todos;
-
-public static class TodosServiceCollectionExtensions
-{
- public static IServiceCollection AddTodos(this IServiceCollection services)
- {
- services.AddOptions<TodosConfiguration>().BindConfiguration("CrupestApi:Todos");
- services.PostConfigure<TodosConfiguration>(config =>
- {
- if (config.Count == 0)
- {
- config.Count = 20;
- }
- });
- services.TryAddScoped<TodosService>();
- return services;
- }
-}
-
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosWebApplicationExtensions.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosWebApplicationExtensions.cs
deleted file mode 100644
index 0ff05a0..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.Todos/TodosWebApplicationExtensions.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using CrupestApi.Commons;
-
-namespace CrupestApi.Todos;
-
-public static class TodosWebApplicationExtensions
-{
- public static WebApplication MapTodos(this WebApplication app, string path)
- {
- if (app is null)
- {
- throw new ArgumentNullException(nameof(app));
- }
-
- app.MapGet(path, async (context) =>
- {
- var todosService = context.RequestServices.GetRequiredService<TodosService>();
-
- try
- {
- var todos = await todosService.GetTodosAsync();
- await context.Response.WriteJsonAsync(todos);
-
- }
- catch (Exception e)
- {
- await context.Response.WriteMessageAsync(e.Message, statusCode: StatusCodes.Status503ServiceUnavailable);
- }
- });
-
- return app;
- }
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi.sln b/dropped/docker/crupest-api/CrupestApi/CrupestApi.sln
deleted file mode 100644
index ebfd960..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi.sln
+++ /dev/null
@@ -1,46 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31903.59
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi", "CrupestApi\CrupestApi.csproj", "{E30916BB-08F9-45F0-BC1A-69B66AE79913}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi.Todos", "CrupestApi.Todos\CrupestApi.Todos.csproj", "{BF9F5F71-AE65-4896-8E6F-FE0D4AD0E7D1}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi.Secrets", "CrupestApi.Secrets\CrupestApi.Secrets.csproj", "{9A7CC9F9-70CB-408A-ADFC-5119C0BDB236}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi.Commons", "CrupestApi.Commons\CrupestApi.Commons.csproj", "{38083CCA-E56C-4D24-BAB6-EEC30E0F478F}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi.Commons.Tests", "CrupestApi.Commons.Tests\CrupestApi.Commons.Tests.csproj", "{0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {E30916BB-08F9-45F0-BC1A-69B66AE79913}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E30916BB-08F9-45F0-BC1A-69B66AE79913}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E30916BB-08F9-45F0-BC1A-69B66AE79913}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E30916BB-08F9-45F0-BC1A-69B66AE79913}.Release|Any CPU.Build.0 = Release|Any CPU
- {BF9F5F71-AE65-4896-8E6F-FE0D4AD0E7D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BF9F5F71-AE65-4896-8E6F-FE0D4AD0E7D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BF9F5F71-AE65-4896-8E6F-FE0D4AD0E7D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BF9F5F71-AE65-4896-8E6F-FE0D4AD0E7D1}.Release|Any CPU.Build.0 = Release|Any CPU
- {9A7CC9F9-70CB-408A-ADFC-5119C0BDB236}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9A7CC9F9-70CB-408A-ADFC-5119C0BDB236}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9A7CC9F9-70CB-408A-ADFC-5119C0BDB236}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9A7CC9F9-70CB-408A-ADFC-5119C0BDB236}.Release|Any CPU.Build.0 = Release|Any CPU
- {38083CCA-E56C-4D24-BAB6-EEC30E0F478F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {38083CCA-E56C-4D24-BAB6-EEC30E0F478F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {38083CCA-E56C-4D24-BAB6-EEC30E0F478F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {38083CCA-E56C-4D24-BAB6-EEC30E0F478F}.Release|Any CPU.Build.0 = Release|Any CPU
- {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi/CrupestApi.csproj b/dropped/docker/crupest-api/CrupestApi/CrupestApi/CrupestApi.csproj
deleted file mode 100644
index 5954f00..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi/CrupestApi.csproj
+++ /dev/null
@@ -1,17 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
- <ItemGroup>
- <ProjectReference Include="..\CrupestApi.Todos\CrupestApi.Todos.csproj" />
- <ProjectReference Include="..\CrupestApi.Files\CrupestApi.Files.csproj" />
- <ProjectReference Include="..\CrupestApi.Commons\CrupestApi.Commons.csproj" />
- <ProjectReference Include="..\CrupestApi.Secrets\CrupestApi.Secrets.csproj" />
- </ItemGroup>
-
- <PropertyGroup>
- <TargetFramework>net7.0</TargetFramework>
- <Nullable>enable</Nullable>
- <ImplicitUsings>enable</ImplicitUsings>
- <SelfContained>false</SelfContained>
- </PropertyGroup>
-
-</Project> \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi/Program.cs b/dropped/docker/crupest-api/CrupestApi/CrupestApi/Program.cs
deleted file mode 100644
index 46648d9..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi/Program.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using CrupestApi.Commons;
-using CrupestApi.Commons.Crud;
-using CrupestApi.Secrets;
-using CrupestApi.Todos;
-
-var builder = WebApplication.CreateBuilder(args);
-
-string configFilePath = Environment.GetEnvironmentVariable("CRUPEST_API_CONFIG_FILE") ?? "/crupest-api-config.json";
-builder.Configuration.AddJsonFile(configFilePath, optional: false, reloadOnChange: true);
-
-builder.Services.AddJsonOptions();
-builder.Services.AddCrupestApiConfig();
-
-builder.Services.AddTodos();
-builder.Services.AddSecrets();
-
-var app = builder.Build();
-
-app.UseCrudCore();
-app.MapTodos("/api/todos");
-// TODO: It's not safe now!
-// app.MapSecrets("/api/secrets");
-
-app.Run();
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi/Properties/launchSettings.json b/dropped/docker/crupest-api/CrupestApi/CrupestApi/Properties/launchSettings.json
deleted file mode 100644
index a4a5cbf..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi/Properties/launchSettings.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/launchsettings.json",
- "profiles": {
- "dev": {
- "commandName": "Project",
- "dotnetRunMessages": true,
- "applicationUrl": "http://localhost:5188",
- "workingDirectory": ".",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "CRUPEST_API_CONFIG_FILE": "dev-config.json"
- }
- }
- }
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/CrupestApi/CrupestApi/appsettings.json b/dropped/docker/crupest-api/CrupestApi/CrupestApi/appsettings.json
deleted file mode 100644
index 53753bd..0000000
--- a/dropped/docker/crupest-api/CrupestApi/CrupestApi/appsettings.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information"
- }
- },
- "AllowedHosts": "*"
-} \ No newline at end of file
diff --git a/dropped/docker/crupest-api/Dockerfile b/dropped/docker/crupest-api/Dockerfile
deleted file mode 100644
index feb7522..0000000
--- a/dropped/docker/crupest-api/Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS build
-COPY CrupestApi /CrupestApi
-WORKDIR /CrupestApi
-RUN dotnet publish CrupestApi/CrupestApi.csproj --configuration Release --output ./publish -r linux-x64
-
-FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine
-ENV ASPNETCORE_URLS=http://0.0.0.0:5000
-ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
-COPY --from=build /CrupestApi/publish /CrupestApi
-WORKDIR /CrupestApi
-VOLUME [ "/crupest-api-config.json" ]
-EXPOSE 5000
-ENTRYPOINT ["dotnet", "CrupestApi.dll"]
diff --git a/dropped/template/crupest-api-config.json.template b/dropped/template/crupest-api-config.json.template
deleted file mode 100644
index 65a7944..0000000
--- a/dropped/template/crupest-api-config.json.template
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "CrupestApi": {
- "Todos": {
- "Username": "$CRUPEST_GITHUB_USERNAME",
- "ProjectNumber": "$CRUPEST_GITHUB_PROJECT_NUMBER",
- "Token": "$CRUPEST_GITHUB_TOKEN",
- "Count": "$CRUPEST_GITHUB_TODO_COUNT"
- }
- }
-}
diff --git a/dropped/template/v2ray-client-config.json.template b/dropped/template/v2ray-client-config.json.template
deleted file mode 100644
index 0c99c6d..0000000
--- a/dropped/template/v2ray-client-config.json.template
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "inbounds": [
- {
- "port": 1080,
- "listen": "127.0.0.1",
- "protocol": "socks",
- "sniffing": {
- "enabled": true,
- "destOverride": [
- "http",
- "tls"
- ]
- },
- "settings": {
- "auth": "noauth",
- "udp": false
- }
- }
- ],
- "outbounds": [
- {
- "protocol": "vmess",
- "settings": {
- "vnext": [
- {
- "address": "$CRUPEST_DOMAIN",
- "port": 443,
- "users": [
- {
- "id": "$CRUPEST_V2RAY_TOKEN",
- "alterId": 0
- }
- ]
- }
- ]
- },
- "streamSettings": {
- "network": "ws",
- "security": "tls",
- "wsSettings": {
- "path": "/_$CRUPEST_V2RAY_PATH"
- }
- }
- }
- ]
-} \ No newline at end of file
diff --git a/dropped/template/docker-compose.yaml.template b/templates/disabled/docker-compose.yaml
index 1b28c5b..565ca49 100644
--- a/dropped/template/docker-compose.yaml.template
+++ b/templates/disabled/docker-compose.yaml
@@ -28,17 +28,5 @@ services:
volumes:
- ./data/timeline:/root/timeline
- crupest-api:
- pull_policy: build
- build:
- context: ./docker/crupest-api
- dockerfile: Dockerfile
- pull: true
- tags:
- - "crupest/crupest-api:latest"
- container_name: crupest-api
- volumes:
- - "./crupest-api-config.json:/crupest-api-config.json:ro"
-
volumes:
debian-dev-home:
diff --git a/dropped/template/nginx/code.conf.template b/templates/disabled/nginx/code.conf.template
index 205c7ba..205c7ba 100644
--- a/dropped/template/nginx/code.conf.template
+++ b/templates/disabled/nginx/code.conf.template
diff --git a/dropped/template/nginx/timeline.conf.template b/templates/disabled/nginx/timeline.conf.template
index 551e0ae..551e0ae 100644
--- a/dropped/template/nginx/timeline.conf.template
+++ b/templates/disabled/nginx/timeline.conf.template
diff --git a/templates/docker-compose.yaml.template b/templates/docker-compose.yaml.template
index d55c7c2..4a8bb36 100644
--- a/templates/docker-compose.yaml.template
+++ b/templates/docker-compose.yaml.template
@@ -120,20 +120,6 @@ services:
timeout: 3s
retries: 0
- forgejo:
- image: code.forgejo.org/forgejo/forgejo:10
- pull_policy: always
- container_name: forgejo
- mem_limit: 800mb
- environment:
- - USER_UID=1000
- - USER_GID=1000
- volumes:
- - ./data/forgejo:/data
- - /etc/timezone:/etc/timezone:ro
- - /etc/localtime:/etc/localtime:ro
- restart: on-failure:3
-
git-server:
pull_policy: build
build:
diff --git a/templates/forgejo.app.ini.init.template b/templates/forgejo.app.ini.init.template
deleted file mode 100644
index 7dc3800..0000000
--- a/templates/forgejo.app.ini.init.template
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copy this file to ./data/forgejo/gitea/conf/app.ini
-# TODO: Copy this to data directory automatically.
-
-APP_NAME = Forgejo, loved by crupest.
-RUN_MODE = prod
-WORK_PATH = /data/gitea
-
-[server]
-HTTP_ADDR = 0.0.0.0
-HTTP_PORT = 3000
-ROOT_URL = https://git.${CRUPEST_DOMAIN}
-DISABLE_SSH = true
-LFS_START_SERVER = true
-
-[database]
-DB_TYPE = sqlite3
-
-[security]
-INSTALL_LOCK = false
-REVERSE_PROXY_LIMIT = 1
-REVERSE_PROXY_TRUSTED_PROXIES = *
-
-[service]
-DISABLE_REGISTRATION = false
-ALLOW_ONLY_INTERNAL_REGISTRATION = true
-
-[mailer]
-ENABLED = true
-PROTOCOL = smtp
-SMTP_ADDR = mail.${CRUPEST_DOMAIN}
-SMTP_PORT = 465
-USER = ${CRUPEST_FORGEJO_MAILER_USER}
-PASSWD = ${CRUPEST_FORGEJO_MAILER_PASSWD}
-
-[log]
-MODE = console,file
-
-[cron]
-ENABLED = true
-
-[actions]
-ENABLED = false
diff --git a/templates/nginx/conf.d/git.conf.template b/templates/nginx/conf.d/git.conf.template
deleted file mode 100644
index 3a2948c..0000000
--- a/templates/nginx/conf.d/git.conf.template
+++ /dev/null
@@ -1,20 +0,0 @@
-server {
- server_name git.${CRUPEST_DOMAIN};
- include common/https-listen;
-
- location / {
- include common/proxy-common;
- proxy_pass http://forgejo:3000/;
- }
-
- client_max_body_size 5G;
-}
-
-
-server {
- server_name git.${CRUPEST_DOMAIN};
- include common/http-listen;
-
- include common/https-redirect;
- include common/acme-challenge;
-}
diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py
index 1394ee4..6aa6294 100644
--- a/tools/cru-py/cru/service/_config.py
+++ b/tools/cru-py/cru/service/_config.py
@@ -197,14 +197,8 @@ class ConfigManager(AppCommandFeatureProvider):
"AUTO_BACKUP_COS_BUCKET",
"bucket name for Tencent COS, used for auto backup",
)
- _add_text("GITHUB_USERNAME", "github username for fetching todos")
- _add_int("GITHUB_PROJECT_NUMBER", "github project number for fetching todos")
- _add_text("GITHUB_TOKEN", "github token for fetching todos")
- _add_text("GITHUB_TODO_COUNT", "github todo count")
_add_uuid("V2RAY_TOKEN", "v2ray user id")
_add_uuid("V2RAY_PATH", "v2ray path, which will be prefixed by _")
- _add_text("FORGEJO_MAILER_USER", "Forgejo SMTP user")
- _add_text("FORGEJO_MAILER_PASSWD", "Forgejo SMTP password")
_add_random_string("2FAUTH_APP_KEY", "2FAuth App Key")
_add_text("2FAUTH_MAIL_USERNAME", "2FAuth SMTP user")
_add_text("2FAUTH_MAIL_PASSWORD", "2FAuth SMTP password")