feat: allow production provider bootstrap
This commit is contained in:
@@ -83,6 +83,9 @@ func New(options Options) (*App, error) {
|
||||
if err := validateProductionProviderConfiguration(getenv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if allowUnconfiguredProviders(getenv) {
|
||||
log.Printf("WARNING: %s is enabled; the Go API will start with unconfigured providers, and generation/quote requests will remain unavailable until credentials are added and the process is restarted", "ZHINIAN_ALLOW_UNCONFIGURED_PROVIDERS")
|
||||
}
|
||||
}
|
||||
database, err := postgres.Open(ctx, config)
|
||||
if err != nil {
|
||||
@@ -265,11 +268,15 @@ func New(options Options) (*App, error) {
|
||||
if providerRegistry == nil {
|
||||
providerRegistry = buildProviderRegistry(getenv)
|
||||
}
|
||||
var unavailableProviders map[string]string
|
||||
if config.Backend == postgres.BackendPostgres {
|
||||
unavailableProviders = providerUnavailableMessages(getenv)
|
||||
}
|
||||
jobService := jobs.NewService(jobStore, nil)
|
||||
jobBuilder := jobs.ProviderJobBuilder{
|
||||
ImageProvider: imageProvider(getenv), VideoProvider: videoProvider(getenv),
|
||||
ImageModel: imageModel(getenv), VideoModel: videoModel(getenv), ImageEngine: imageEngine(getenv), VideoEngine: videoEngine(getenv),
|
||||
ImageEngines: providerImageTargets(getenv), VideoEngines: providerVideoTargets(getenv), NewID: applicationJobID,
|
||||
ImageEngines: providerImageTargets(getenv), VideoEngines: providerVideoTargets(getenv), UnavailableProviders: unavailableProviders, NewID: applicationJobID,
|
||||
}
|
||||
usageService := usage.Service{
|
||||
Repository: usageRepository,
|
||||
|
||||
@@ -77,6 +77,19 @@ func TestApplicationRejectsInvalidProductionDatabaseConfiguration(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductionProviderBootstrapFlagAllowsApplicationComposition(t *testing.T) {
|
||||
app, err := application.New(application.Options{Getenv: applicationEnv(map[string]string{
|
||||
"NODE_ENV": "production",
|
||||
"ZHINIAN_DATA_BACKEND": "postgres",
|
||||
"DATABASE_URL": "postgres://user:password@127.0.0.1:5432/zhinian",
|
||||
"ZHINIAN_ALLOW_UNCONFIGURED_PROVIDERS": "true",
|
||||
})})
|
||||
if err != nil {
|
||||
t.Fatalf("New() with provider bootstrap flag = %v", err)
|
||||
}
|
||||
app.Close()
|
||||
}
|
||||
|
||||
func TestProductionLocalBackendNeverGrantsAnonymousAdministrator(t *testing.T) {
|
||||
app, err := application.New(application.Options{Getenv: applicationEnv(map[string]string{
|
||||
"NODE_ENV": "production",
|
||||
|
||||
@@ -59,6 +59,10 @@ func parseBool(value string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func allowUnconfiguredProviders(getenv postgres.Getenv) bool {
|
||||
return parseBool(getenv("ZHINIAN_ALLOW_UNCONFIGURED_PROVIDERS"))
|
||||
}
|
||||
|
||||
func applicationJobID() string {
|
||||
return applicationID("job")
|
||||
}
|
||||
@@ -186,7 +190,27 @@ func providerVideoTargets(getenv postgres.Getenv) map[string]jobs.ProviderTarget
|
||||
}
|
||||
}
|
||||
|
||||
func providerUnavailableMessages(getenv postgres.Getenv) map[string]string {
|
||||
missing := map[string]string{}
|
||||
if strings.TrimSpace(getenv("VOLCENGINE_ACCESS_KEY_ID")) == "" || strings.TrimSpace(getenv("VOLCENGINE_SECRET_ACCESS_KEY")) == "" {
|
||||
missing["volcengine-visual"] = "即梦服务商未配置,请先配置 VOLCENGINE_ACCESS_KEY_ID 和 VOLCENGINE_SECRET_ACCESS_KEY。"
|
||||
}
|
||||
if strings.TrimSpace(getenv("EVOLINK_API_KEY")) == "" {
|
||||
missing["evolink"] = "EvoLink 服务商未配置,请先配置 EVOLINK_API_KEY。"
|
||||
}
|
||||
if bailianAPIKey(getenv) == "" {
|
||||
missing["bailian"] = "百炼服务商未配置,请先配置 BAILIAN_API_KEY 或 DASHSCOPE_API_KEY。"
|
||||
}
|
||||
if strings.TrimSpace(getenv("SEEDANCE_API_KEY")) == "" {
|
||||
missing["seedance"] = "Seedance 服务商未配置,请先配置 SEEDANCE_API_KEY。"
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
func validateProductionProviderConfiguration(getenv postgres.Getenv) error {
|
||||
if allowUnconfiguredProviders(getenv) {
|
||||
return nil
|
||||
}
|
||||
missing := make([]string, 0, 4)
|
||||
if strings.TrimSpace(getenv("VOLCENGINE_ACCESS_KEY_ID")) == "" || strings.TrimSpace(getenv("VOLCENGINE_SECRET_ACCESS_KEY")) == "" {
|
||||
missing = append(missing, "即梦 VOLCENGINE_ACCESS_KEY_ID/VOLCENGINE_SECRET_ACCESS_KEY")
|
||||
|
||||
@@ -128,6 +128,26 @@ func TestValidateProductionProviderConfigurationRequiresAllRealCredentials(t *te
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateProductionProviderConfigurationCanBeSkippedForBootstrap(t *testing.T) {
|
||||
values := map[string]string{"ZHINIAN_ALLOW_UNCONFIGURED_PROVIDERS": "true"}
|
||||
if err := validateProductionProviderConfiguration(func(name string) string { return values[name] }); err != nil {
|
||||
t.Fatalf("validateProductionProviderConfiguration() with bootstrap flag = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderUnavailableMessagesIdentifyOnlyMissingCredentials(t *testing.T) {
|
||||
values := map[string]string{
|
||||
"VOLCENGINE_ACCESS_KEY_ID": "access",
|
||||
"VOLCENGINE_SECRET_ACCESS_KEY": "secret",
|
||||
"EVOLINK_API_KEY": "evolink",
|
||||
"BAILIAN_API_KEY": "bailian",
|
||||
}
|
||||
missing := providerUnavailableMessages(func(name string) string { return values[name] })
|
||||
if len(missing) != 1 || !strings.Contains(missing["seedance"], "SEEDANCE_API_KEY") {
|
||||
t.Fatalf("missing = %#v", missing)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderTargetsNeverSelectRemovedProvider(t *testing.T) {
|
||||
getenv := func(string) string { return "" }
|
||||
for engine, target := range providerImageTargets(getenv) {
|
||||
|
||||
Reference in New Issue
Block a user