diff --git a/GZCTF.Test/CTFServer.Test.csproj b/GZCTF.Test/CTFServer.Test.csproj
index 4a9290109..67687bfae 100644
--- a/GZCTF.Test/CTFServer.Test.csproj
+++ b/GZCTF.Test/CTFServer.Test.csproj
@@ -21,11 +21,11 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/GZCTF/Program.cs b/GZCTF/Program.cs
index a960450a1..86f6fccc5 100644
--- a/GZCTF/Program.cs
+++ b/GZCTF/Program.cs
@@ -25,6 +25,8 @@
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+Banner();
+
#region Directory
var uploadPath = Path.Combine(builder.Configuration.GetSection("UploadFolder").Value ?? "uploads");
@@ -39,6 +41,10 @@
builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(LogLevel.Trace);
builder.Host.UseSerilog(dispose: true);
+builder.Configuration.AddEnvironmentVariables("GZCTF_");
+Log.Logger = LogHelper.GetInitLogger();
+
+Log.Logger.Debug("GZCTF 正在启动中……");
#endregion Logging
@@ -51,6 +57,13 @@
}
else
{
+ if (!builder.Configuration.GetSection("ConnectionStrings").GetSection("Database").Exists())
+ {
+ Log.Logger.Fatal("未找到数据库连接字符串字段 ConnectionStrings,请检查 appsettings.json 是否正常挂载及配置");
+ Thread.Sleep(30000);
+ Environment.Exit(1);
+ }
+
builder.Services.AddDbContext(
options =>
{
@@ -72,19 +85,27 @@
builder.Host.ConfigureAppConfiguration((host, config) =>
{
config.AddJsonFile("ratelimit.json", optional: true, reloadOnChange: true);
- config.AddEntityConfiguration(options =>
+ try
{
- if (builder.Configuration.GetSection("ConnectionStrings").Exists())
- options.UseNpgsql(builder.Configuration.GetConnectionString("Database"));
- else
- options.UseInMemoryDatabase("TestDb");
- });
+ config.AddEntityConfiguration(options =>
+ {
+ if (builder.Configuration.GetSection("ConnectionStrings").Exists())
+ options.UseNpgsql(builder.Configuration.GetConnectionString("Database"));
+ else
+ options.UseInMemoryDatabase("TestDb");
+ });
+ }
+ catch
+ {
+ Log.Logger.Fatal("数据库连接失败,请检查 Database 连接字符串配置");
+ Thread.Sleep(30000);
+ Environment.Exit(1);
+ }
});
}
#endregion Configuration
#region OpenApiDocument
-
builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddOpenApiDocument(settings =>
{
@@ -99,7 +120,6 @@
});
settings.DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull;
});
-
#endregion OpenApiDocument
#region SignalR
@@ -265,13 +285,16 @@ await context.Posts.AddAsync(new()
await context.SaveChangesAsync();
}
- // only for testing
- if (app.Environment.IsDevelopment())
+ if (app.Environment.IsDevelopment() || app.Configuration.GetSection("ADMIN_PASSWORD").Exists())
{
var usermanager = serviceScope.ServiceProvider.GetRequiredService>();
var admin = await usermanager.FindByNameAsync("Admin");
+
if (admin is null)
{
+ var password = app.Environment.IsDevelopment() ? "Admin@2022" :
+ app.Configuration.GetValue("ADMIN_PASSWORD");
+
admin = new UserInfo
{
UserName = "Admin",
@@ -280,7 +303,7 @@ await context.Posts.AddAsync(new()
EmailConfirmed = true,
RegisterTimeUTC = DateTimeOffset.UtcNow
};
- await usermanager.CreateAsync(admin, "Admin@2022");
+ await usermanager.CreateAsync(admin, password);
}
}
}
@@ -340,7 +363,7 @@ await context.Posts.AddAsync(new()
}
catch (Exception exception)
{
- logger.LogError(exception, "因异常,应用程序意外停止");
+ logger.LogError(exception, "因异常,应用程序意外终止");
throw;
}
finally
@@ -352,4 +375,28 @@ await context.Posts.AddAsync(new()
public partial class Program
{
public static bool IsTesting { get; set; } = false;
+
+ public static void Banner()
+ {
+ const string banner =
+ @" ___ ___ ___ ___ " + "\n" +
+ @" / /\ / /\ / /\ ___ / /\ " + "\n" +
+ @" / /:/_ / /::| / /:/ / /\ / /:/_ " + "\n" +
+ @" / /:/ /\ / /:/:| / /:/ / /:/ / /:/ /\" + "\n" +
+ @" / /:/_/::\ / /:/|:|__ / /:/ ___ / /:/ / /:/ /:/" + "\n" +
+ @" /__/:/__\/\:\ /__/:/ |:| /\ /__/:/ / /\ / /::\ /__/:/ /:/ " + "\n" +
+ @" \ \:\ /~~/:/ \__\/ |:|/:/ \ \:\ / /:/ /__/:/\:\ \ \:\/:/ " + "\n" +
+ @" \ \:\ /:/ | |:/:/ \ \:\ /:/ \__\/ \:\ \ \::/ " + "\n" +
+ @" \ \:\/:/ | |::/ \ \:\/:/ \ \:\ \ \:\ " + "\n" +
+ @" \ \::/ | |:/ \ \::/ \__\/ \ \:\ " + "\n" +
+ @" \__\/ |__|/ \__\/ \__\/ " + "\n";
+ Console.WriteLine(banner);
+
+ var versionStr = "";
+ var version = typeof(Codec).Assembly.GetName().Version;
+ if (version is not null)
+ versionStr = $"Version: {version.Major}.{version.Minor}.{version.Build}";
+
+ Console.WriteLine($"GZCTF © 2022-present GZTimeWalker {versionStr,33}\n");
+ }
}
diff --git a/GZCTF/Utils/LogHelper.cs b/GZCTF/Utils/LogHelper.cs
index 837ae6302..f3b419c02 100644
--- a/GZCTF/Utils/LogHelper.cs
+++ b/GZCTF/Utils/LogHelper.cs
@@ -96,6 +96,20 @@ public static void Log(this ILogger _logger, string msg, string uname, str
private const string LogTemplate = "[{@t:yy-MM-dd HH:mm:ss.fff} {@l:u3}] {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1)}: {@m} {#if Length(Status) > 0}#{Status} <{UserName}>{#if Length(IP) > 0}@{IP}{#end}{#end}\n{@x}";
+ public static Logger GetInitLogger()
+ => new LoggerConfiguration()
+ .Enrich.FromLogContext()
+ .MinimumLevel.Debug()
+ .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
+ .MinimumLevel.Override("AspNetCoreRateLimit", LogEventLevel.Warning)
+ .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
+ .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Warning)
+ .WriteTo.Async(t => t.Console(
+ formatter: new ExpressionTemplate(LogTemplate, theme: TemplateTheme.Literate),
+ restrictedToMinimumLevel: LogEventLevel.Debug
+ ))
+ .CreateLogger();
+
public static Logger GetLogger(IConfiguration configuration, IServiceProvider serviceProvider)
=> new LoggerConfiguration()
.Enrich.FromLogContext()
@@ -141,4 +155,4 @@ public TimeColumnWriter() : base(NpgsqlDbType.TimestampTz)
public override object GetValue(LogEvent logEvent, IFormatProvider? formatProvider = null)
=> logEvent.Timestamp.ToUniversalTime();
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index 57b70471a..0aa58506c 100644
--- a/README.md
+++ b/README.md
@@ -122,10 +122,12 @@ docker pull ghcr.io/gztimewalker/gzctf/gzctf:latest
### 初始管理员
-生产环境中默认不存在管理员权限用户,需要手动更改数据库条目。当管理员注册完成并成功登录后,进入所选数据库表后执行:
+生产环境中默认不存在管理员权限用户,需要在首次启动时设置 `GZCTF_ADMIN_PASSWORD` 环境变量来设置初始管理员密码,并通过 `Admin` 账号登录。
+
+你也可以通过手动更改数据库条目来将当前已注册的用户设置为管理员。当管理员注册完成并成功登录后,进入所选数据库表后执行:
```sql
-update "AspNetUsers" set "Role"=3;
+UPDATE "AspNetUsers" SET "Role"=3 WHERE "UserName"='some_user_name';
```
### 端口暴露范围设置
diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml
index 5570630f1..a6af29466 100644
--- a/scripts/docker-compose.yml
+++ b/scripts/docker-compose.yml
@@ -3,6 +3,8 @@ services:
gzctf:
image: gztime/gzctf:latest
restart: always
+ # environment:
+ # - "GZCTF_ADMIN_PASSWORD=Your_Password_Here"
ports:
- "80:80"
networks: