mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:37:34 +00:00
chore: move the go code into a subfolder
This commit is contained in:
32
apps/api/cmd/setup/01.go
Normal file
32
apps/api/cmd/setup/01.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 01_sql/adminapi.sql
|
||||
createAdminViews string
|
||||
//go:embed 01_sql/auth.sql
|
||||
createAuthViews string
|
||||
//go:embed 01_sql/projections.sql
|
||||
createProjections string
|
||||
)
|
||||
|
||||
type ProjectionTable struct {
|
||||
dbClient *sql.DB
|
||||
}
|
||||
|
||||
func (mig *ProjectionTable) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
stmt := createAdminViews + createAuthViews + createProjections
|
||||
_, err := mig.dbClient.ExecContext(ctx, stmt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *ProjectionTable) String() string {
|
||||
return "01_tables"
|
||||
}
|
57
apps/api/cmd/setup/01_sql/adminapi.sql
Normal file
57
apps/api/cmd/setup/01_sql/adminapi.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
CREATE SCHEMA adminapi;
|
||||
|
||||
CREATE TABLE adminapi.locks (
|
||||
locker_id TEXT,
|
||||
locked_until TIMESTAMPTZ(3),
|
||||
view_name TEXT,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (view_name, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE adminapi.current_sequences (
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
event_date TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (view_name, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE adminapi.failed_events (
|
||||
view_name TEXT,
|
||||
failed_sequence BIGINT,
|
||||
failure_count SMALLINT,
|
||||
err_msg TEXT,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (view_name, failed_sequence, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE adminapi.styling (
|
||||
aggregate_id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
label_policy_state INT2 NOT NULL DEFAULT 0::INT2,
|
||||
sequence INT8 NULL,
|
||||
primary_color TEXT NULL,
|
||||
background_color TEXT NULL,
|
||||
warn_color TEXT NULL,
|
||||
font_color TEXT NULL,
|
||||
primary_color_dark TEXT NULL,
|
||||
background_color_dark TEXT NULL,
|
||||
warn_color_dark TEXT NULL,
|
||||
font_color_dark TEXT NULL,
|
||||
logo_url TEXT NULL,
|
||||
icon_url TEXT NULL,
|
||||
logo_dark_url TEXT NULL,
|
||||
icon_dark_url TEXT NULL,
|
||||
font_url TEXT NULL,
|
||||
err_msg_popup BOOL NULL,
|
||||
disable_watermark BOOL NULL,
|
||||
hide_login_name_suffix BOOL NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (aggregate_id, label_policy_state, instance_id)
|
||||
);
|
229
apps/api/cmd/setup/01_sql/auth.sql
Normal file
229
apps/api/cmd/setup/01_sql/auth.sql
Normal file
@@ -0,0 +1,229 @@
|
||||
CREATE SCHEMA auth;
|
||||
|
||||
CREATE TABLE auth.locks (
|
||||
locker_id TEXT,
|
||||
locked_until TIMESTAMPTZ(3),
|
||||
view_name TEXT,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (view_name, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.current_sequences (
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
event_date TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (view_name, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.failed_events (
|
||||
view_name TEXT,
|
||||
failed_sequence BIGINT,
|
||||
failure_count SMALLINT,
|
||||
err_msg TEXT,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (view_name, failed_sequence, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.users (
|
||||
id TEXT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
resource_owner TEXT NULL,
|
||||
user_state INT2 NULL,
|
||||
password_set BOOL NULL,
|
||||
password_change_required BOOL NULL,
|
||||
password_change TIMESTAMPTZ NULL,
|
||||
last_login TIMESTAMPTZ NULL,
|
||||
user_name TEXT NULL,
|
||||
login_names TEXT[] NULL,
|
||||
preferred_login_name TEXT NULL,
|
||||
first_name TEXT NULL,
|
||||
last_name TEXT NULL,
|
||||
nick_name TEXT NULL,
|
||||
display_name TEXT NULL,
|
||||
preferred_language TEXT NULL,
|
||||
gender INT2 NULL,
|
||||
email TEXT NULL,
|
||||
is_email_verified BOOL NULL,
|
||||
phone TEXT NULL,
|
||||
is_phone_verified BOOL NULL,
|
||||
country TEXT NULL,
|
||||
locality TEXT NULL,
|
||||
postal_code TEXT NULL,
|
||||
region TEXT NULL,
|
||||
street_address TEXT NULL,
|
||||
otp_state INT2 NULL,
|
||||
mfa_max_set_up INT2 NULL,
|
||||
mfa_init_skipped TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
init_required BOOL NULL,
|
||||
username_change_required BOOL NULL,
|
||||
machine_name TEXT NULL,
|
||||
machine_description TEXT NULL,
|
||||
user_type TEXT NULL,
|
||||
u2f_tokens BYTEA NULL,
|
||||
passwordless_tokens BYTEA NULL,
|
||||
avatar_key TEXT NULL,
|
||||
passwordless_init_required BOOL NULL,
|
||||
password_init_required BOOL NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (id, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.user_sessions (
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
resource_owner TEXT NULL,
|
||||
state INT2 NULL,
|
||||
user_agent_id TEXT NULL,
|
||||
user_id TEXT NULL,
|
||||
user_name TEXT NULL,
|
||||
password_verification TIMESTAMPTZ NULL,
|
||||
second_factor_verification TIMESTAMPTZ NULL,
|
||||
multi_factor_verification TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
second_factor_verification_type INT2 NULL,
|
||||
multi_factor_verification_type INT2 NULL,
|
||||
user_display_name TEXT NULL,
|
||||
login_name TEXT NULL,
|
||||
external_login_verification TIMESTAMPTZ NULL,
|
||||
selected_idp_config_id TEXT NULL,
|
||||
passwordless_verification TIMESTAMPTZ NULL,
|
||||
avatar_key TEXT NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (user_agent_id, user_id, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.user_external_idps (
|
||||
external_user_id TEXT NOT NULL,
|
||||
idp_config_id TEXT NOT NULL,
|
||||
user_id TEXT NULL,
|
||||
idp_name TEXT NULL,
|
||||
user_display_name TEXT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
resource_owner TEXT NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (external_user_id, idp_config_id, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.tokens (
|
||||
id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
resource_owner TEXT NULL,
|
||||
application_id TEXT NULL,
|
||||
user_agent_id TEXT NULL,
|
||||
user_id TEXT NULL,
|
||||
expiration TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
scopes TEXT[] NULL,
|
||||
audience TEXT[] NULL,
|
||||
preferred_language TEXT NULL,
|
||||
refresh_token_id TEXT NULL,
|
||||
is_pat BOOL NOT NULL DEFAULT false,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (id, instance_id)
|
||||
);
|
||||
|
||||
CREATE INDEX user_user_agent_idx ON auth.tokens (user_id, user_agent_id);
|
||||
|
||||
CREATE TABLE auth.refresh_tokens (
|
||||
id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
resource_owner TEXT NULL,
|
||||
token TEXT NULL,
|
||||
client_id TEXT NOT NULL,
|
||||
user_agent_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
auth_time TIMESTAMPTZ NULL,
|
||||
idle_expiration TIMESTAMPTZ NULL,
|
||||
expiration TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
scopes TEXT[] NULL,
|
||||
audience TEXT[] NULL,
|
||||
amr TEXT[] NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (id, instance_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX unique_client_user_index ON auth.refresh_tokens (client_id, user_agent_id, user_id);
|
||||
|
||||
CREATE TABLE auth.org_project_mapping (
|
||||
org_id TEXT NOT NULL,
|
||||
project_id TEXT NOT NULL,
|
||||
project_grant_id TEXT NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (org_id, project_id, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.idp_providers (
|
||||
aggregate_id TEXT NOT NULL,
|
||||
idp_config_id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
name TEXT NULL,
|
||||
idp_config_type INT2 NULL,
|
||||
idp_provider_type INT2 NULL,
|
||||
idp_state INT2 NULL,
|
||||
styling_type INT2 NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (aggregate_id, idp_config_id, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.idp_configs (
|
||||
idp_config_id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
aggregate_id TEXT NULL,
|
||||
name TEXT NULL,
|
||||
idp_state INT2 NULL,
|
||||
idp_provider_type INT2 NULL,
|
||||
is_oidc BOOL NULL,
|
||||
oidc_client_id TEXT NULL,
|
||||
oidc_client_secret JSONB NULL,
|
||||
oidc_issuer TEXT NULL,
|
||||
oidc_scopes TEXT[] NULL,
|
||||
oidc_idp_display_name_mapping INT2 NULL,
|
||||
oidc_idp_username_mapping INT2 NULL,
|
||||
styling_type INT2 NULL,
|
||||
oauth_authorization_endpoint TEXT NULL,
|
||||
oauth_token_endpoint TEXT NULL,
|
||||
auto_register BOOL NULL,
|
||||
jwt_endpoint TEXT NULL,
|
||||
jwt_keys_endpoint TEXT NULL,
|
||||
jwt_header_name TEXT NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (idp_config_id, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.auth_requests (
|
||||
id TEXT NOT NULL,
|
||||
request JSONB NULL,
|
||||
code TEXT NULL,
|
||||
request_type INT2 NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (id, instance_id)
|
||||
);
|
||||
|
||||
CREATE INDEX auth_code_idx ON auth.auth_requests (code);
|
28
apps/api/cmd/setup/01_sql/projections.sql
Normal file
28
apps/api/cmd/setup/01_sql/projections.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
CREATE TABLE projections.locks (
|
||||
locker_id TEXT,
|
||||
locked_until TIMESTAMPTZ(3),
|
||||
projection_name TEXT,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (projection_name, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE projections.current_sequences (
|
||||
projection_name TEXT,
|
||||
aggregate_type TEXT,
|
||||
current_sequence BIGINT,
|
||||
instance_id TEXT NOT NULL,
|
||||
timestamp TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (projection_name, aggregate_type, instance_id)
|
||||
);
|
||||
|
||||
CREATE TABLE projections.failed_events (
|
||||
projection_name TEXT,
|
||||
failed_sequence BIGINT,
|
||||
failure_count SMALLINT,
|
||||
error TEXT,
|
||||
instance_id TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (projection_name, failed_sequence, instance_id)
|
||||
);
|
38
apps/api/cmd/setup/02.go
Normal file
38
apps/api/cmd/setup/02.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
const (
|
||||
createAssets = `
|
||||
CREATE TABLE system.assets (
|
||||
instance_id TEXT,
|
||||
asset_type TEXT,
|
||||
resource_owner TEXT,
|
||||
name TEXT,
|
||||
content_type TEXT,
|
||||
hash TEXT GENERATED ALWAYS AS (md5(data)) STORED,
|
||||
data BYTEA,
|
||||
updated_at TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (instance_id, resource_owner, name)
|
||||
);
|
||||
`
|
||||
)
|
||||
|
||||
type AssetTable struct {
|
||||
dbClient *sql.DB
|
||||
}
|
||||
|
||||
func (mig *AssetTable) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, createAssets)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AssetTable) String() string {
|
||||
return "02_assets"
|
||||
}
|
206
apps/api/cmd/setup/03.go
Normal file
206
apps/api/cmd/setup/03.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/cache/connector"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
crypto_db "github.com/zitadel/zitadel/internal/crypto/database"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type FirstInstance struct {
|
||||
InstanceName string
|
||||
DefaultLanguage language.Tag
|
||||
Org command.InstanceOrgSetup
|
||||
MachineKeyPath string
|
||||
PatPath string
|
||||
LoginClientPatPath string
|
||||
Features *command.InstanceFeatures
|
||||
|
||||
Skip bool
|
||||
|
||||
instanceSetup command.InstanceSetup
|
||||
userEncryptionKey *crypto.KeyConfig
|
||||
smtpEncryptionKey *crypto.KeyConfig
|
||||
oidcEncryptionKey *crypto.KeyConfig
|
||||
masterKey string
|
||||
db *database.DB
|
||||
es *eventstore.Eventstore
|
||||
defaults systemdefaults.SystemDefaults
|
||||
zitadelRoles []authz.RoleMapping
|
||||
externalDomain string
|
||||
externalSecure bool
|
||||
externalPort uint16
|
||||
domain string
|
||||
}
|
||||
|
||||
func (mig *FirstInstance) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
if mig.Skip {
|
||||
return nil
|
||||
}
|
||||
keyStorage, err := mig.verifyEncryptionKeys(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAlg, err := crypto.NewAESCrypto(mig.userEncryptionKey, keyStorage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
smtpEncryption, err := crypto.NewAESCrypto(mig.smtpEncryptionKey, keyStorage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oidcEncryption, err := crypto.NewAESCrypto(mig.oidcEncryptionKey, keyStorage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd, err := command.StartCommands(ctx,
|
||||
mig.es,
|
||||
connector.Connectors{},
|
||||
mig.defaults,
|
||||
mig.zitadelRoles,
|
||||
nil,
|
||||
nil,
|
||||
mig.externalDomain,
|
||||
mig.externalSecure,
|
||||
mig.externalPort,
|
||||
nil,
|
||||
nil,
|
||||
smtpEncryption,
|
||||
nil,
|
||||
userAlg,
|
||||
nil,
|
||||
oidcEncryption,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mig.instanceSetup.InstanceName = mig.InstanceName
|
||||
mig.instanceSetup.CustomDomain = mig.externalDomain
|
||||
mig.instanceSetup.DefaultLanguage = mig.DefaultLanguage
|
||||
mig.instanceSetup.Org = mig.Org
|
||||
// check if username is email style or else append @<orgname>.<custom-domain>
|
||||
//this way we have the same value as before changing `UserLoginMustBeDomain` to false
|
||||
if !mig.instanceSetup.DomainPolicy.UserLoginMustBeDomain && !strings.Contains(mig.instanceSetup.Org.Human.Username, "@") {
|
||||
orgDomain, err := domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mig.instanceSetup.Org.Human.Username = mig.instanceSetup.Org.Human.Username + "@" + orgDomain
|
||||
}
|
||||
mig.instanceSetup.Org.Human.Email.Address = mig.instanceSetup.Org.Human.Email.Address.Normalize()
|
||||
if mig.instanceSetup.Org.Human.Email.Address == "" {
|
||||
mig.instanceSetup.Org.Human.Email.Address = domain.EmailAddress(mig.instanceSetup.Org.Human.Username)
|
||||
if !strings.Contains(string(mig.instanceSetup.Org.Human.Email.Address), "@") {
|
||||
orgDomain, err := domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mig.instanceSetup.Org.Human.Email.Address = domain.EmailAddress(mig.instanceSetup.Org.Human.Username + "@" + orgDomain)
|
||||
}
|
||||
}
|
||||
|
||||
_, token, key, loginClientToken, _, err := cmd.SetUpInstance(ctx, &mig.instanceSetup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (mig.instanceSetup.Org.Machine != nil &&
|
||||
((mig.instanceSetup.Org.Machine.Pat != nil && token == "") ||
|
||||
(mig.instanceSetup.Org.Machine.MachineKey != nil && key == nil))) ||
|
||||
(mig.instanceSetup.Org.LoginClient != nil &&
|
||||
(mig.instanceSetup.Org.LoginClient.Pat != nil && loginClientToken == "")) {
|
||||
return err
|
||||
}
|
||||
return mig.outputMachineAuthentication(key, token, loginClientToken)
|
||||
}
|
||||
|
||||
func (mig *FirstInstance) verifyEncryptionKeys(ctx context.Context) (*crypto_db.Database, error) {
|
||||
keyStorage, err := crypto_db.NewKeyStorage(mig.db, mig.masterKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot start key storage: %w", err)
|
||||
}
|
||||
if err = verifyKey(ctx, mig.userEncryptionKey, keyStorage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = verifyKey(ctx, mig.smtpEncryptionKey, keyStorage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = verifyKey(ctx, mig.oidcEncryptionKey, keyStorage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keyStorage, nil
|
||||
}
|
||||
|
||||
func (mig *FirstInstance) outputMachineAuthentication(key *command.MachineKey, token, loginClientToken string) error {
|
||||
if key != nil {
|
||||
keyDetails, err := key.Detail()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := outputStdoutOrPath(mig.MachineKeyPath, string(keyDetails)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if token != "" {
|
||||
if err := outputStdoutOrPath(mig.PatPath, token); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if loginClientToken != "" {
|
||||
if err := outputStdoutOrPath(mig.LoginClientPatPath, loginClientToken); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func outputStdoutOrPath(path string, content string) (err error) {
|
||||
f := os.Stdout
|
||||
if path != "" {
|
||||
f, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
_, err = fmt.Fprintln(f, content)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *FirstInstance) String() string {
|
||||
return "03_default_instance"
|
||||
}
|
||||
|
||||
func verifyKey(ctx context.Context, key *crypto.KeyConfig, storage crypto.KeyStorage) (err error) {
|
||||
_, err = crypto.LoadKey(key.EncryptionKeyID, storage)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
k, err := crypto.NewKey(key.EncryptionKeyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storage.CreateKeys(ctx, k)
|
||||
}
|
27
apps/api/cmd/setup/05.go
Normal file
27
apps/api/cmd/setup/05.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 05.sql
|
||||
lastFailedStmts string
|
||||
)
|
||||
|
||||
type LastFailed struct {
|
||||
dbClient *sql.DB
|
||||
}
|
||||
|
||||
func (mig *LastFailed) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, lastFailedStmts)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *LastFailed) String() string {
|
||||
return "05_last_failed"
|
||||
}
|
11
apps/api/cmd/setup/05.sql
Normal file
11
apps/api/cmd/setup/05.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE INDEX IF NOT EXISTS current_sequences_instance_id_idx ON adminapi.current_sequences (instance_id);
|
||||
CREATE INDEX IF NOT EXISTS current_sequences_instance_id_idx ON auth.current_sequences (instance_id);
|
||||
CREATE INDEX IF NOT EXISTS current_sequences_instance_id_idx ON projections.current_sequences (instance_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS failed_events_instance_id_idx ON adminapi.failed_events (instance_id);
|
||||
CREATE INDEX IF NOT EXISTS failed_events_instance_id_idx ON auth.failed_events (instance_id);
|
||||
CREATE INDEX IF NOT EXISTS failed_events_instance_id_idx ON projections.failed_events (instance_id);
|
||||
|
||||
ALTER TABLE adminapi.failed_events ADD COLUMN IF NOT EXISTS last_failed TIMESTAMPTZ;
|
||||
ALTER TABLE auth.failed_events ADD COLUMN IF NOT EXISTS last_failed TIMESTAMPTZ;
|
||||
ALTER TABLE projections.failed_events ADD COLUMN IF NOT EXISTS last_failed TIMESTAMPTZ;
|
30
apps/api/cmd/setup/06.go
Normal file
30
apps/api/cmd/setup/06.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 06/adminapi.sql
|
||||
createAdminViews06 string
|
||||
//go:embed 06/auth.sql
|
||||
createAuthViews06 string
|
||||
)
|
||||
|
||||
type OwnerRemoveColumns struct {
|
||||
dbClient *sql.DB
|
||||
}
|
||||
|
||||
func (mig *OwnerRemoveColumns) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
stmt := createAdminViews06 + createAuthViews06
|
||||
_, err := mig.dbClient.ExecContext(ctx, stmt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *OwnerRemoveColumns) String() string {
|
||||
return "06_resource_owner_columns"
|
||||
}
|
30
apps/api/cmd/setup/06/adminapi.sql
Normal file
30
apps/api/cmd/setup/06/adminapi.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
CREATE TABLE adminapi.styling2 (
|
||||
aggregate_id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
label_policy_state INT2 NOT NULL DEFAULT 0::INT2,
|
||||
sequence INT8 NULL,
|
||||
primary_color TEXT NULL,
|
||||
background_color TEXT NULL,
|
||||
warn_color TEXT NULL,
|
||||
font_color TEXT NULL,
|
||||
primary_color_dark TEXT NULL,
|
||||
background_color_dark TEXT NULL,
|
||||
warn_color_dark TEXT NULL,
|
||||
font_color_dark TEXT NULL,
|
||||
logo_url TEXT NULL,
|
||||
icon_url TEXT NULL,
|
||||
logo_dark_url TEXT NULL,
|
||||
icon_dark_url TEXT NULL,
|
||||
font_url TEXT NULL,
|
||||
err_msg_popup BOOL NULL,
|
||||
disable_watermark BOOL NULL,
|
||||
hide_login_name_suffix BOOL NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
owner_removed BOOL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (instance_id, aggregate_id, label_policy_state)
|
||||
);
|
||||
|
||||
CREATE INDEX st2_owner_removed_idx ON adminapi.styling2 (owner_removed);
|
124
apps/api/cmd/setup/06/auth.sql
Normal file
124
apps/api/cmd/setup/06/auth.sql
Normal file
@@ -0,0 +1,124 @@
|
||||
CREATE TABLE auth.users2 (
|
||||
id TEXT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
resource_owner TEXT NULL,
|
||||
user_state INT2 NULL,
|
||||
password_set BOOL NULL,
|
||||
password_change_required BOOL NULL,
|
||||
password_change TIMESTAMPTZ NULL,
|
||||
last_login TIMESTAMPTZ NULL,
|
||||
user_name TEXT NULL,
|
||||
login_names TEXT[] NULL,
|
||||
preferred_login_name TEXT NULL,
|
||||
first_name TEXT NULL,
|
||||
last_name TEXT NULL,
|
||||
nick_name TEXT NULL,
|
||||
display_name TEXT NULL,
|
||||
preferred_language TEXT NULL,
|
||||
gender INT2 NULL,
|
||||
email TEXT NULL,
|
||||
is_email_verified BOOL NULL,
|
||||
phone TEXT NULL,
|
||||
is_phone_verified BOOL NULL,
|
||||
country TEXT NULL,
|
||||
locality TEXT NULL,
|
||||
postal_code TEXT NULL,
|
||||
region TEXT NULL,
|
||||
street_address TEXT NULL,
|
||||
otp_state INT2 NULL,
|
||||
mfa_max_set_up INT2 NULL,
|
||||
mfa_init_skipped TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
init_required BOOL NULL,
|
||||
username_change_required BOOL NULL,
|
||||
machine_name TEXT NULL,
|
||||
machine_description TEXT NULL,
|
||||
user_type TEXT NULL,
|
||||
u2f_tokens BYTEA NULL,
|
||||
passwordless_tokens BYTEA NULL,
|
||||
avatar_key TEXT NULL,
|
||||
passwordless_init_required BOOL NULL,
|
||||
password_init_required BOOL NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
owner_removed BOOL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (instance_id, id)
|
||||
);
|
||||
CREATE INDEX u2_owner_removed_idx ON auth.users2 (owner_removed);
|
||||
|
||||
CREATE TABLE auth.user_external_idps2 (
|
||||
external_user_id TEXT NOT NULL,
|
||||
idp_config_id TEXT NOT NULL,
|
||||
user_id TEXT NULL,
|
||||
idp_name TEXT NULL,
|
||||
user_display_name TEXT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
resource_owner TEXT NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
owner_removed BOOL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (instance_id, external_user_id, idp_config_id)
|
||||
);
|
||||
CREATE INDEX ext_idps2_owner_removed_idx ON auth.user_external_idps2 (owner_removed);
|
||||
|
||||
CREATE TABLE auth.org_project_mapping2 (
|
||||
org_id TEXT NOT NULL,
|
||||
project_id TEXT NOT NULL,
|
||||
project_grant_id TEXT NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
owner_removed BOOL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (instance_id, org_id, project_id)
|
||||
);
|
||||
CREATE INDEX org_proj_m2_owner_removed_idx ON auth.org_project_mapping2 (owner_removed);
|
||||
|
||||
CREATE TABLE auth.idp_providers2 (
|
||||
aggregate_id TEXT NOT NULL,
|
||||
idp_config_id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
name TEXT NULL,
|
||||
idp_config_type INT2 NULL,
|
||||
idp_provider_type INT2 NULL,
|
||||
idp_state INT2 NULL,
|
||||
styling_type INT2 NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
owner_removed BOOL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (instance_id, aggregate_id, idp_config_id)
|
||||
);
|
||||
CREATE INDEX idp_prov2_owner_removed_idx ON auth.idp_providers2 (owner_removed);
|
||||
|
||||
CREATE TABLE auth.idp_configs2 (
|
||||
idp_config_id TEXT NOT NULL,
|
||||
creation_date TIMESTAMPTZ NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
sequence INT8 NULL,
|
||||
aggregate_id TEXT NULL,
|
||||
name TEXT NULL,
|
||||
idp_state INT2 NULL,
|
||||
idp_provider_type INT2 NULL,
|
||||
is_oidc BOOL NULL,
|
||||
oidc_client_id TEXT NULL,
|
||||
oidc_client_secret JSONB NULL,
|
||||
oidc_issuer TEXT NULL,
|
||||
oidc_scopes TEXT[] NULL,
|
||||
oidc_idp_display_name_mapping INT2 NULL,
|
||||
oidc_idp_username_mapping INT2 NULL,
|
||||
styling_type INT2 NULL,
|
||||
oauth_authorization_endpoint TEXT NULL,
|
||||
oauth_token_endpoint TEXT NULL,
|
||||
auto_register BOOL NULL,
|
||||
jwt_endpoint TEXT NULL,
|
||||
jwt_keys_endpoint TEXT NULL,
|
||||
jwt_header_name TEXT NULL,
|
||||
instance_id TEXT NOT NULL,
|
||||
owner_removed BOOL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (instance_id, idp_config_id)
|
||||
);
|
||||
CREATE INDEX idp_conf2_owner_removed_idx ON auth.idp_configs2 (owner_removed);
|
34
apps/api/cmd/setup/07.go
Normal file
34
apps/api/cmd/setup/07.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 07/logstore.sql
|
||||
createLogstoreSchema07 string
|
||||
//go:embed 07/access.sql
|
||||
createAccessLogsTable07 string
|
||||
//go:embed 07/execution.sql
|
||||
createExecutionLogsTable07 string
|
||||
)
|
||||
|
||||
type LogstoreTables struct {
|
||||
dbClient *sql.DB
|
||||
username string
|
||||
}
|
||||
|
||||
func (mig *LogstoreTables) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
stmt := strings.ReplaceAll(createLogstoreSchema07, "%[1]s", mig.username) + createAccessLogsTable07 + createExecutionLogsTable07
|
||||
_, err := mig.dbClient.ExecContext(ctx, stmt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *LogstoreTables) String() string {
|
||||
return "07_logstore"
|
||||
}
|
14
apps/api/cmd/setup/07/access.sql
Normal file
14
apps/api/cmd/setup/07/access.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE IF NOT EXISTS logstore.access (
|
||||
log_date TIMESTAMPTZ NOT NULL
|
||||
, protocol INT NOT NULL
|
||||
, request_url TEXT NOT NULL
|
||||
, response_status INT NOT NULL
|
||||
, request_headers JSONB
|
||||
, response_headers JSONB
|
||||
, instance_id TEXT NOT NULL
|
||||
, project_id TEXT NOT NULL
|
||||
, requested_domain TEXT
|
||||
, requested_host TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX protocol_date_desc ON logstore.access (instance_id, protocol, log_date DESC) INCLUDE (request_url, response_status, request_headers);
|
11
apps/api/cmd/setup/07/execution.sql
Normal file
11
apps/api/cmd/setup/07/execution.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS logstore.execution (
|
||||
log_date TIMESTAMPTZ NOT NULL
|
||||
, took INTERVAL
|
||||
, message TEXT NOT NULL
|
||||
, loglevel INT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
, action_id TEXT NOT NULL
|
||||
, metadata JSONB
|
||||
);
|
||||
|
||||
CREATE INDEX log_date_desc ON logstore.execution (instance_id, log_date DESC) INCLUDE (took);
|
3
apps/api/cmd/setup/07/logstore.sql
Normal file
3
apps/api/cmd/setup/07/logstore.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
CREATE SCHEMA IF NOT EXISTS logstore;
|
||||
|
||||
GRANT ALL ON ALL TABLES IN SCHEMA logstore TO "%[1]s";
|
27
apps/api/cmd/setup/08.go
Normal file
27
apps/api/cmd/setup/08.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 08/08.sql
|
||||
tokenIndexes08 string
|
||||
)
|
||||
|
||||
type AuthTokenIndexes struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AuthTokenIndexes) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, tokenIndexes08)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AuthTokenIndexes) String() string {
|
||||
return "08_auth_token_indexes"
|
||||
}
|
5
apps/api/cmd/setup/08/08.sql
Normal file
5
apps/api/cmd/setup/08/08.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE INDEX IF NOT EXISTS inst_refresh_tkn_idx ON auth.tokens(instance_id, refresh_token_id);
|
||||
CREATE INDEX IF NOT EXISTS inst_app_tkn_idx ON auth.tokens(instance_id, application_id);
|
||||
CREATE INDEX IF NOT EXISTS inst_ro_tkn_idx ON auth.tokens(instance_id, resource_owner);
|
||||
DROP INDEX IF EXISTS auth.user_user_agent_idx;
|
||||
CREATE INDEX IF NOT EXISTS inst_usr_agnt_tkn_idx ON auth.tokens(instance_id, user_id, user_agent_id);
|
79
apps/api/cmd/setup/10.go
Normal file
79
apps/api/cmd/setup/10.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/cockroach-go/v2/crdb"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 10/10_create_temp_table.sql
|
||||
correctCreationDate10CreateTable string
|
||||
//go:embed 10/10_fill_table.sql
|
||||
correctCreationDate10FillTable string
|
||||
//go:embed 10/10_update.sql
|
||||
correctCreationDate10Update string
|
||||
//go:embed 10/10_count_wrong_events.sql
|
||||
correctCreationDate10CountWrongEvents string
|
||||
//go:embed 10/10_empty_table.sql
|
||||
correctCreationDate10Truncate string
|
||||
)
|
||||
|
||||
type CorrectCreationDate struct {
|
||||
dbClient *database.DB
|
||||
FailAfter time.Duration
|
||||
}
|
||||
|
||||
func (mig *CorrectCreationDate) Execute(ctx context.Context, _ eventstore.Event) (err error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, mig.FailAfter)
|
||||
defer cancel()
|
||||
|
||||
for i := 0; ; i++ {
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("start iteration")
|
||||
var affected int64
|
||||
err = crdb.ExecuteTx(ctx, mig.dbClient.DB, nil, func(tx *sql.Tx) error {
|
||||
_, err := tx.ExecContext(ctx, correctCreationDate10CreateTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("temp table created")
|
||||
|
||||
_, err = tx.ExecContext(ctx, correctCreationDate10Truncate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, correctCreationDate10FillTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("temp table filled")
|
||||
|
||||
res := tx.QueryRowContext(ctx, correctCreationDate10CountWrongEvents)
|
||||
if err := res.Scan(&affected); err != nil || affected == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, correctCreationDate10Update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logging.WithFields("mig", mig.String(), "iteration", i, "count", affected).Debug("creation dates updated")
|
||||
return nil
|
||||
})
|
||||
logging.WithFields("mig", mig.String(), "iteration", i).Debug("end iteration")
|
||||
if affected == 0 || err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mig *CorrectCreationDate) String() string {
|
||||
return "10_correct_creation_date"
|
||||
}
|
1
apps/api/cmd/setup/10/10_count_wrong_events.sql
Normal file
1
apps/api/cmd/setup/10/10_count_wrong_events.sql
Normal file
@@ -0,0 +1 @@
|
||||
SELECT COUNT(*) FROM wrong_events
|
6
apps/api/cmd/setup/10/10_create_temp_table.sql
Normal file
6
apps/api/cmd/setup/10/10_create_temp_table.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
CREATE TEMPORARY TABLE IF NOT EXISTS wrong_events (
|
||||
instance_id TEXT
|
||||
, event_sequence BIGINT
|
||||
, current_cd TIMESTAMPTZ
|
||||
, next_cd TIMESTAMPTZ
|
||||
);
|
1
apps/api/cmd/setup/10/10_empty_table.sql
Normal file
1
apps/api/cmd/setup/10/10_empty_table.sql
Normal file
@@ -0,0 +1 @@
|
||||
TRUNCATE wrong_events
|
19
apps/api/cmd/setup/10/10_fill_table.sql
Normal file
19
apps/api/cmd/setup/10/10_fill_table.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
INSERT INTO wrong_events (
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
instance_id
|
||||
, event_sequence
|
||||
, creation_date AS current_cd
|
||||
, lead(creation_date) OVER (
|
||||
PARTITION BY instance_id
|
||||
ORDER BY event_sequence DESC
|
||||
) AS next_cd
|
||||
FROM
|
||||
eventstore.events
|
||||
WHERE
|
||||
"position" IS NULL
|
||||
) sub WHERE
|
||||
current_cd < next_cd
|
||||
ORDER BY
|
||||
event_sequence DESC
|
||||
);
|
10
apps/api/cmd/setup/10/10_update.sql
Normal file
10
apps/api/cmd/setup/10/10_update.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
UPDATE
|
||||
eventstore.events e
|
||||
SET
|
||||
creation_date = we.next_cd
|
||||
, "position" = (EXTRACT(EPOCH FROM we.next_cd))
|
||||
FROM
|
||||
wrong_events we
|
||||
WHERE
|
||||
e.event_sequence = we.event_sequence
|
||||
AND e.instance_id = we.instance_id;
|
27
apps/api/cmd/setup/12.go
Normal file
27
apps/api/cmd/setup/12.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 12/12_add_otp_columns.sql
|
||||
addOTPColumns string
|
||||
)
|
||||
|
||||
type AddOTPColumns struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddOTPColumns) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addOTPColumns)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddOTPColumns) String() string {
|
||||
return "12_auth_users_otp_columns"
|
||||
}
|
2
apps/api/cmd/setup/12/12_add_otp_columns.sql
Normal file
2
apps/api/cmd/setup/12/12_add_otp_columns.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE auth.users2 ADD COLUMN otp_sms_added BOOL DEFAULT false;
|
||||
ALTER TABLE auth.users2 ADD COLUMN otp_email_added BOOL DEFAULT false;
|
27
apps/api/cmd/setup/13.go
Normal file
27
apps/api/cmd/setup/13.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 13/13_fix_quota_constraints.sql
|
||||
fixQuotaConstraints string
|
||||
)
|
||||
|
||||
type FixQuotaConstraints struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *FixQuotaConstraints) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, fixQuotaConstraints)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *FixQuotaConstraints) String() string {
|
||||
return "13_fix_quota_constraints"
|
||||
}
|
4
apps/api/cmd/setup/13/13_fix_quota_constraints.sql
Normal file
4
apps/api/cmd/setup/13/13_fix_quota_constraints.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN from_anchor DROP NOT NULL;
|
||||
ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN amount DROP NOT NULL;
|
||||
ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN interval DROP NOT NULL;
|
||||
ALTER TABLE IF EXISTS projections.quotas ALTER COLUMN limit_usage DROP NOT NULL;
|
67
apps/api/cmd/setup/14.go
Normal file
67
apps/api/cmd/setup/14.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 14/*.sql
|
||||
newEventsTable embed.FS
|
||||
)
|
||||
|
||||
type NewEventsTable struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *NewEventsTable) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
// if events already exists events2 is created during a setup job
|
||||
var count int
|
||||
err := mig.dbClient.QueryRowContext(ctx,
|
||||
func(row *sql.Row) error {
|
||||
if err := row.Scan(&count); err != nil {
|
||||
return err
|
||||
}
|
||||
return row.Err()
|
||||
},
|
||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema = 'eventstore' AND table_name like 'events2'",
|
||||
)
|
||||
if err != nil || count == 1 {
|
||||
return err
|
||||
}
|
||||
|
||||
statements, err := readStatements(newEventsTable, "14")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range statements {
|
||||
stmt.query = strings.ReplaceAll(stmt.query, "{{.username}}", mig.dbClient.Username())
|
||||
logging.WithFields("file", stmt.file, "migration", mig.String()).Info("execute statement")
|
||||
_, err = mig.dbClient.ExecContext(ctx, stmt.query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *NewEventsTable) String() string {
|
||||
return "14_events_push"
|
||||
}
|
||||
|
||||
func (mig *NewEventsTable) ContinueOnErr(err error) bool {
|
||||
pgErr := new(pgconn.PgError)
|
||||
if errors.As(err, &pgErr) {
|
||||
return pgErr.Code == "42P01"
|
||||
}
|
||||
return false
|
||||
}
|
1
apps/api/cmd/setup/14/01_disable_inserts.sql
Normal file
1
apps/api/cmd/setup/14/01_disable_inserts.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE eventstore.events RENAME TO events_old;
|
31
apps/api/cmd/setup/14/02_create_and_fill_events2.sql
Normal file
31
apps/api/cmd/setup/14/02_create_and_fill_events2.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
CREATE TABLE eventstore.events2 (
|
||||
instance_id,
|
||||
aggregate_type,
|
||||
aggregate_id,
|
||||
|
||||
event_type,
|
||||
"sequence",
|
||||
revision,
|
||||
created_at,
|
||||
payload,
|
||||
creator,
|
||||
"owner",
|
||||
|
||||
"position",
|
||||
in_tx_order
|
||||
) AS SELECT
|
||||
instance_id,
|
||||
aggregate_type,
|
||||
aggregate_id,
|
||||
|
||||
event_type,
|
||||
event_sequence,
|
||||
substr(aggregate_version, 2)::SMALLINT,
|
||||
creation_date,
|
||||
event_data,
|
||||
editor_user,
|
||||
resource_owner,
|
||||
|
||||
EXTRACT(EPOCH FROM creation_date),
|
||||
event_sequence
|
||||
FROM eventstore.events_old;
|
4
apps/api/cmd/setup/14/03_events2_pk.sql
Normal file
4
apps/api/cmd/setup/14/03_events2_pk.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
BEGIN;
|
||||
ALTER TABLE eventstore.events2 DROP CONSTRAINT IF EXISTS events2_pkey;
|
||||
ALTER TABLE eventstore.events2 ADD PRIMARY KEY (instance_id, aggregate_type, aggregate_id, "sequence");
|
||||
COMMIT;
|
7
apps/api/cmd/setup/14/04_constraints.sql
Normal file
7
apps/api/cmd/setup/14/04_constraints.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE eventstore.events2 ALTER COLUMN event_type SET NOT NULL,
|
||||
ALTER COLUMN revision SET NOT NULL,
|
||||
ALTER COLUMN created_at SET NOT NULL,
|
||||
ALTER COLUMN creator SET NOT NULL,
|
||||
ALTER COLUMN "owner" SET NOT NULL,
|
||||
ALTER COLUMN "position" SET NOT NULL,
|
||||
ALTER COLUMN in_tx_order SET NOT NULL;
|
3
apps/api/cmd/setup/14/05_indexes.sql
Normal file
3
apps/api/cmd/setup/14/05_indexes.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
CREATE INDEX IF NOT EXISTS es_active_instances ON eventstore.events2 (created_at DESC, instance_id);
|
||||
CREATE INDEX IF NOT EXISTS es_wm ON eventstore.events2 (aggregate_id, instance_id, aggregate_type, event_type);
|
||||
CREATE INDEX IF NOT EXISTS es_projection ON eventstore.events2 (instance_id, aggregate_type, event_type, "position");
|
39
apps/api/cmd/setup/15.go
Normal file
39
apps/api/cmd/setup/15.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 15/*.sql
|
||||
currentProjectionState embed.FS
|
||||
)
|
||||
|
||||
type CurrentProjectionState struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *CurrentProjectionState) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
statements, err := readStatements(currentProjectionState, "15")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range statements {
|
||||
logging.WithFields("file", stmt.file, "migration", mig.String()).Info("execute statement")
|
||||
_, err = mig.dbClient.ExecContext(ctx, stmt.query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *CurrentProjectionState) String() string {
|
||||
return "15_current_projection_state"
|
||||
}
|
16
apps/api/cmd/setup/15/01_new_failed_events.sql
Normal file
16
apps/api/cmd/setup/15/01_new_failed_events.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS projections.failed_events2 (
|
||||
projection_name TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
, event_creation_date TIMESTAMPTZ NOT NULL
|
||||
, failed_sequence INT8 NOT NULL
|
||||
|
||||
, failure_count INT2 NULL DEFAULT 0
|
||||
, error TEXT
|
||||
, last_failed TIMESTAMPTZ
|
||||
|
||||
, PRIMARY KEY (projection_name, instance_id, aggregate_type, aggregate_id, failed_sequence)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS fe2_instance_id_idx on projections.failed_events2 (instance_id);
|
26
apps/api/cmd/setup/15/02_fe_from_projections.sql
Normal file
26
apps/api/cmd/setup/15/02_fe_from_projections.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.projection_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.error
|
||||
, fe.last_failed
|
||||
FROM
|
||||
projections.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
26
apps/api/cmd/setup/15/03_fe_from_adminapi.sql
Normal file
26
apps/api/cmd/setup/15/03_fe_from_adminapi.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.view_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.err_msg
|
||||
, fe.last_failed
|
||||
FROM
|
||||
adminapi.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
26
apps/api/cmd/setup/15/04_fe_from_auth.sql
Normal file
26
apps/api/cmd/setup/15/04_fe_from_auth.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO projections.failed_events2 (
|
||||
projection_name
|
||||
, instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
, event_creation_date
|
||||
, failed_sequence
|
||||
, failure_count
|
||||
, error
|
||||
, last_failed
|
||||
) SELECT
|
||||
fe.view_name
|
||||
, fe.instance_id
|
||||
, e.aggregate_type
|
||||
, e.aggregate_id
|
||||
, e.created_at
|
||||
, e.sequence
|
||||
, fe.failure_count
|
||||
, fe.err_msg
|
||||
, fe.last_failed
|
||||
FROM
|
||||
auth.failed_events fe
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = fe.instance_id
|
||||
AND e.sequence = fe.failed_sequence
|
||||
ON CONFLICT DO NOTHING;
|
15
apps/api/cmd/setup/15/05_current_states.sql
Normal file
15
apps/api/cmd/setup/15/05_current_states.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS projections.current_states (
|
||||
projection_name TEXT NOT NULL
|
||||
, instance_id TEXT NOT NULL
|
||||
|
||||
, last_updated TIMESTAMPTZ
|
||||
|
||||
, aggregate_id TEXT
|
||||
, aggregate_type TEXT
|
||||
, "sequence" INT8
|
||||
, event_date TIMESTAMPTZ
|
||||
, "position" DECIMAL
|
||||
|
||||
, PRIMARY KEY (projection_name, instance_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS cs_instance_id_idx ON projections.current_states (instance_id);
|
28
apps/api/cmd/setup/15/06_cs_from_projections.sql
Normal file
28
apps/api/cmd/setup/15/06_cs_from_projections.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) SELECT
|
||||
cs.projection_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.timestamp
|
||||
FROM
|
||||
projections.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.aggregate_type = cs.aggregate_type
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
projections.current_sequences cs2
|
||||
WHERE
|
||||
cs.projection_name = cs2.projection_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
27
apps/api/cmd/setup/15/07_cs_from_adminapi.sql
Normal file
27
apps/api/cmd/setup/15/07_cs_from_adminapi.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) SELECT
|
||||
cs.view_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.last_successful_spooler_run
|
||||
FROM
|
||||
adminapi.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
adminapi.current_sequences cs2
|
||||
WHERE
|
||||
cs.view_name = cs2.view_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
27
apps/api/cmd/setup/15/08_cs_from_auth.sql
Normal file
27
apps/api/cmd/setup/15/08_cs_from_auth.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
INSERT INTO projections.current_states (
|
||||
projection_name
|
||||
, instance_id
|
||||
, event_date
|
||||
, "position"
|
||||
, last_updated
|
||||
) SELECT
|
||||
cs.view_name
|
||||
, cs.instance_id
|
||||
, e.created_at
|
||||
, e.position
|
||||
, cs.last_successful_spooler_run
|
||||
FROM
|
||||
auth.current_sequences cs
|
||||
JOIN eventstore.events2 e ON
|
||||
e.instance_id = cs.instance_id
|
||||
AND e.sequence = cs.current_sequence
|
||||
AND cs.current_sequence = (
|
||||
SELECT
|
||||
MAX(cs2.current_sequence)
|
||||
FROM
|
||||
auth.current_sequences cs2
|
||||
WHERE
|
||||
cs.view_name = cs2.view_name
|
||||
AND cs.instance_id = cs2.instance_id
|
||||
)
|
||||
ON CONFLICT DO NOTHING;
|
34
apps/api/cmd/setup/16.go
Normal file
34
apps/api/cmd/setup/16.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 16.sql
|
||||
uniqueConstraintLower string
|
||||
)
|
||||
|
||||
type UniqueConstraintToLower struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *UniqueConstraintToLower) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
res, err := mig.dbClient.ExecContext(ctx, uniqueConstraintLower)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err := res.RowsAffected()
|
||||
logging.WithFields("count", count).Info("unique constraints updated")
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *UniqueConstraintToLower) String() string {
|
||||
return "16_unique_constraint_lower"
|
||||
}
|
13
apps/api/cmd/setup/16.sql
Normal file
13
apps/api/cmd/setup/16.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
WITH casesensitive as (
|
||||
SELECT instance_id, unique_type, lower(unique_field)
|
||||
FROM eventstore.unique_constraints
|
||||
GROUP BY instance_id, unique_type, lower(unique_field)
|
||||
HAVING count(unique_field) < 2
|
||||
)
|
||||
UPDATE eventstore.unique_constraints c
|
||||
SET unique_field = casesensitive.lower
|
||||
FROM casesensitive
|
||||
WHERE c.instance_id = casesensitive.instance_id
|
||||
AND c.unique_type = casesensitive.unique_type
|
||||
AND lower(c.unique_field) = casesensitive.lower
|
||||
AND c.unique_field <> casesensitive.lower;
|
27
apps/api/cmd/setup/17.go
Normal file
27
apps/api/cmd/setup/17.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 17.sql
|
||||
addOffsetField string
|
||||
)
|
||||
|
||||
type AddOffsetToCurrentStates struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddOffsetToCurrentStates) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addOffsetField)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddOffsetToCurrentStates) String() string {
|
||||
return "17_add_offset_col_to_current_states"
|
||||
}
|
1
apps/api/cmd/setup/17.sql
Normal file
1
apps/api/cmd/setup/17.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE projections.current_states ADD filter_offset INTEGER;
|
27
apps/api/cmd/setup/18.go
Normal file
27
apps/api/cmd/setup/18.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 18.sql
|
||||
addLowerFieldsToLoginNames string
|
||||
)
|
||||
|
||||
type AddLowerFieldsToLoginNames struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddLowerFieldsToLoginNames) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addLowerFieldsToLoginNames)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddLowerFieldsToLoginNames) String() string {
|
||||
return "18_add_lower_fields_to_login_names"
|
||||
}
|
6
apps/api/cmd/setup/18.sql
Normal file
6
apps/api/cmd/setup/18.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE IF EXISTS projections.login_names3_users ADD COLUMN IF NOT EXISTS user_name_lower TEXT GENERATED ALWAYS AS (lower(user_name)) STORED;
|
||||
CREATE INDEX IF NOT EXISTS login_names3_users_search ON projections.login_names3_users (instance_id, user_name_lower) INCLUDE (resource_owner);
|
||||
|
||||
ALTER TABLE IF EXISTS projections.login_names3_domains ADD COLUMN IF NOT EXISTS name_lower TEXT GENERATED ALWAYS AS (lower(name)) STORED;
|
||||
CREATE INDEX IF NOT EXISTS login_names3_domain_search ON projections.login_names3_domains (instance_id, resource_owner, name_lower);
|
||||
CREATE INDEX IF NOT EXISTS login_names3_domain_search_result ON projections.login_names3_domains (instance_id, resource_owner) INCLUDE (is_primary);
|
27
apps/api/cmd/setup/19.go
Normal file
27
apps/api/cmd/setup/19.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 19.sql
|
||||
addCurrentSequencesIndex string
|
||||
)
|
||||
|
||||
type AddCurrentSequencesIndex struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddCurrentSequencesIndex) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addCurrentSequencesIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddCurrentSequencesIndex) String() string {
|
||||
return "19_add_current_sequences_index"
|
||||
}
|
1
apps/api/cmd/setup/19.sql
Normal file
1
apps/api/cmd/setup/19.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS events2_current_sequence ON eventstore.events2 ("sequence" DESC, aggregate_id, aggregate_type, instance_id);
|
27
apps/api/cmd/setup/20.go
Normal file
27
apps/api/cmd/setup/20.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 20.sql
|
||||
addByUserIndexToSession string
|
||||
)
|
||||
|
||||
type AddByUserIndexToSession struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddByUserIndexToSession) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addByUserIndexToSession)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddByUserIndexToSession) String() string {
|
||||
return "20_add_by_user_index_on_session"
|
||||
}
|
1
apps/api/cmd/setup/20.sql
Normal file
1
apps/api/cmd/setup/20.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS user_sessions_by_user ON auth.user_sessions (instance_id, user_id);
|
27
apps/api/cmd/setup/21.go
Normal file
27
apps/api/cmd/setup/21.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 21.sql
|
||||
addBlockFieldToLimits string
|
||||
)
|
||||
|
||||
type AddBlockFieldToLimits struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddBlockFieldToLimits) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addBlockFieldToLimits)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddBlockFieldToLimits) String() string {
|
||||
return "21_add_block_field_to_limits"
|
||||
}
|
1
apps/api/cmd/setup/21.sql
Normal file
1
apps/api/cmd/setup/21.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE IF EXISTS projections.limits ADD COLUMN IF NOT EXISTS block BOOLEAN;
|
27
apps/api/cmd/setup/22.go
Normal file
27
apps/api/cmd/setup/22.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 22.sql
|
||||
activeInstanceEvents string
|
||||
)
|
||||
|
||||
type ActiveInstanceEvents struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *ActiveInstanceEvents) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, activeInstanceEvents)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *ActiveInstanceEvents) String() string {
|
||||
return "22_active_instance_events_index"
|
||||
}
|
1
apps/api/cmd/setup/22.sql
Normal file
1
apps/api/cmd/setup/22.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS active_instances_events ON eventstore.events2 (aggregate_type, event_type) WHERE aggregate_type = 'instance' AND event_type IN ('instance.added', 'instance.removed');
|
27
apps/api/cmd/setup/23.go
Normal file
27
apps/api/cmd/setup/23.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 23.sql
|
||||
correctGlobalUniqueConstraints string
|
||||
)
|
||||
|
||||
type CorrectGlobalUniqueConstraints struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *CorrectGlobalUniqueConstraints) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, correctGlobalUniqueConstraints)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *CorrectGlobalUniqueConstraints) String() string {
|
||||
return "23_correct_global_unique_constraints"
|
||||
}
|
1
apps/api/cmd/setup/23.sql
Normal file
1
apps/api/cmd/setup/23.sql
Normal file
@@ -0,0 +1 @@
|
||||
UPDATE eventstore.unique_constraints SET instance_id = '' WHERE unique_type = 'instance_domain';
|
27
apps/api/cmd/setup/24.go
Normal file
27
apps/api/cmd/setup/24.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 24.sql
|
||||
addTokenActor string
|
||||
)
|
||||
|
||||
type AddActorToAuthTokens struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddActorToAuthTokens) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addTokenActor)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddActorToAuthTokens) String() string {
|
||||
return "24_add_actor_col_to_auth_tokens"
|
||||
}
|
2
apps/api/cmd/setup/24.sql
Normal file
2
apps/api/cmd/setup/24.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE auth.tokens ADD COLUMN actor jsonb;
|
||||
ALTER TABLE auth.refresh_tokens ADD COLUMN actor jsonb;
|
27
apps/api/cmd/setup/25.go
Normal file
27
apps/api/cmd/setup/25.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 25.sql
|
||||
addLowerFieldsToVerifiedEmail string
|
||||
)
|
||||
|
||||
type User11AddLowerFieldsToVerifiedEmail struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *User11AddLowerFieldsToVerifiedEmail) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addLowerFieldsToVerifiedEmail)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *User11AddLowerFieldsToVerifiedEmail) String() string {
|
||||
return "25_user14_add_lower_fields_to_verified_email"
|
||||
}
|
2
apps/api/cmd/setup/25.sql
Normal file
2
apps/api/cmd/setup/25.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE IF EXISTS projections.users14_notifications ADD COLUMN IF NOT EXISTS verified_email_lower TEXT GENERATED ALWAYS AS (lower(verified_email)) STORED;
|
||||
CREATE INDEX IF NOT EXISTS users14_notifications_email_search ON projections.users14_notifications (instance_id, verified_email_lower);
|
27
apps/api/cmd/setup/26.go
Normal file
27
apps/api/cmd/setup/26.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 26.sql
|
||||
authUsers3 string
|
||||
)
|
||||
|
||||
type AuthUsers3 struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AuthUsers3) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, authUsers3)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AuthUsers3) String() string {
|
||||
return "26_auth_users3"
|
||||
}
|
16
apps/api/cmd/setup/26.sql
Normal file
16
apps/api/cmd/setup/26.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS auth.users3 (
|
||||
instance_id TEXT NOT NULL,
|
||||
id TEXT NOT NULL,
|
||||
resource_owner TEXT NOT NULL,
|
||||
change_date TIMESTAMPTZ NULL,
|
||||
password_set BOOL NULL,
|
||||
password_change TIMESTAMPTZ NULL,
|
||||
last_login TIMESTAMPTZ NULL,
|
||||
init_required BOOL NULL,
|
||||
mfa_init_skipped TIMESTAMPTZ NULL,
|
||||
username_change_required BOOL NULL,
|
||||
passwordless_init_required BOOL NULL,
|
||||
password_init_required BOOL NULL,
|
||||
|
||||
PRIMARY KEY (instance_id, id)
|
||||
)
|
27
apps/api/cmd/setup/27.go
Normal file
27
apps/api/cmd/setup/27.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 27.sql
|
||||
addSAMLNameIDFormat string
|
||||
)
|
||||
|
||||
type IDPTemplate6SAMLNameIDFormat struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *IDPTemplate6SAMLNameIDFormat) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addSAMLNameIDFormat)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *IDPTemplate6SAMLNameIDFormat) String() string {
|
||||
return "27_idp_templates6_add_saml_name_id_format"
|
||||
}
|
2
apps/api/cmd/setup/27.sql
Normal file
2
apps/api/cmd/setup/27.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE IF EXISTS projections.idp_templates6_saml ADD COLUMN IF NOT EXISTS name_id_format SMALLINT;
|
||||
ALTER TABLE IF EXISTS projections.idp_templates6_saml ADD COLUMN IF NOT EXISTS transient_mapping_attribute_name TEXT;
|
27
apps/api/cmd/setup/28.go
Normal file
27
apps/api/cmd/setup/28.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 28.sql
|
||||
addFieldTable string
|
||||
)
|
||||
|
||||
type AddFieldTable struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddFieldTable) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addFieldTable)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddFieldTable) String() string {
|
||||
return "28_add_search_table"
|
||||
}
|
64
apps/api/cmd/setup/28.sql
Normal file
64
apps/api/cmd/setup/28.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
CREATE TABLE eventstore.fields (
|
||||
id TEXT NOT NULL DEFAULT gen_random_uuid()
|
||||
, instance_id TEXT NOT NULL
|
||||
, resource_owner TEXT NOT NULL
|
||||
|
||||
, aggregate_type TEXT NOT NULL
|
||||
, aggregate_id TEXT NOT NULL
|
||||
|
||||
, object_type TEXT NOT NULL
|
||||
, object_id TEXT NOT NULL
|
||||
, object_revision INT2
|
||||
|
||||
, field_name TEXT NOT NULL
|
||||
|
||||
-- all the values of fields are inserted into value column as jsonb and if we need to index something we store it to the type specific column additionally
|
||||
, "value" JSONB NOT NULL
|
||||
, number_value NUMERIC GENERATED ALWAYS AS (CASE WHEN should_index AND JSONB_TYPEOF("value") = 'number' THEN "value"::NUMERIC ELSE NULL END) STORED
|
||||
, text_value TEXT GENERATED ALWAYS AS (CASE WHEN should_index AND JSONB_TYPEOF("value") = 'string' THEN "value" #>> '{}' ELSE NULL END) STORED
|
||||
, bool_value BOOLEAN GENERATED ALWAYS AS (CASE WHEN should_index AND JSONB_TYPEOF("value") = 'boolean' THEN "value"::BOOLEAN ELSE NULL END) STORED
|
||||
|
||||
-- if true the value must be unique within an instance
|
||||
, value_must_be_unique BOOLEAN
|
||||
-- if set to true the primitive value is indexed
|
||||
, should_index BOOLEAN
|
||||
|
||||
, PRIMARY KEY (instance_id, id)
|
||||
-- TODO: create issue to enable the foreign key as soon as the objects table is implemented
|
||||
-- , CONSTRAINT f_objects_fk FOREIGN KEY (instance_id, resource_owner, object_type, object_id, object_revision) REFERENCES eventstore.objects (instance_id, resource_owner, object_type, object_id, object_revision) ON DELETE CASCADE
|
||||
|
||||
-- the constraint ensures that a primitive value is set if the value must be unique
|
||||
, CONSTRAINT primitive_value_for_unique_check CHECK (
|
||||
CASE
|
||||
WHEN value_must_be_unique THEN num_nonnulls(number_value, text_value, bool_value) = 1
|
||||
ELSE true
|
||||
END
|
||||
)
|
||||
-- the constraint ensures that a primitive value is set if the value must be indexed
|
||||
, CONSTRAINT primitive_value_for_index CHECK (
|
||||
CASE
|
||||
WHEN should_index THEN num_nonnulls(number_value, text_value, bool_value) = 1
|
||||
ELSE true
|
||||
END
|
||||
)
|
||||
);
|
||||
|
||||
-- unique constraints for primitive values
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS f_number_unique_idx ON eventstore.fields (instance_id, field_name, number_value) WHERE value_must_be_unique;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS f_text_unique_idx ON eventstore.fields (instance_id, field_name, text_value) WHERE value_must_be_unique;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS f_bool_unique_idx ON eventstore.fields (instance_id, field_name, bool_value) WHERE value_must_be_unique;
|
||||
|
||||
-- search index for primitive values
|
||||
CREATE INDEX IF NOT EXISTS f_number_value_idx ON eventstore.fields (instance_id, object_type, field_name, number_value)
|
||||
INCLUDE (resource_owner, object_id, object_revision, "value")
|
||||
WHERE number_value IS NOT NULL ;
|
||||
CREATE INDEX IF NOT EXISTS f_text_value_idx ON eventstore.fields (instance_id, object_type, field_name, text_value)
|
||||
INCLUDE (resource_owner, object_id, object_revision, "value")
|
||||
WHERE text_value IS NOT NULL ;
|
||||
CREATE INDEX IF NOT EXISTS f_bool_value_idx ON eventstore.fields (instance_id, object_type, field_name, bool_value)
|
||||
INCLUDE (resource_owner, object_id, object_revision, "value")
|
||||
WHERE bool_value IS NOT NULL ;
|
||||
|
||||
-- search index for object by id
|
||||
CREATE INDEX IF NOT EXISTS f_object_idx ON eventstore.fields (instance_id, object_type, object_id, object_revision)
|
||||
INCLUDE (resource_owner, field_name, "value") ;
|
40
apps/api/cmd/setup/29.go
Normal file
40
apps/api/cmd/setup/29.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
type FillFieldsForProjectGrant struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
|
||||
func (mig *FillFieldsForProjectGrant) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
instances, err := mig.eventstore.InstanceIDs(
|
||||
ctx,
|
||||
eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs).
|
||||
OrderDesc().
|
||||
AddQuery().
|
||||
AggregateTypes("instance").
|
||||
EventTypes(instance.InstanceAddedEventType).
|
||||
Builder(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, instance := range instances {
|
||||
ctx := authz.WithInstanceID(ctx, instance)
|
||||
if err := projection.ProjectGrantFields.Trigger(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *FillFieldsForProjectGrant) String() string {
|
||||
return "29_init_fields_for_project_grant"
|
||||
}
|
40
apps/api/cmd/setup/30.go
Normal file
40
apps/api/cmd/setup/30.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
type FillFieldsForOrgDomainVerified struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
|
||||
func (mig *FillFieldsForOrgDomainVerified) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
instances, err := mig.eventstore.InstanceIDs(
|
||||
ctx,
|
||||
eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs).
|
||||
OrderDesc().
|
||||
AddQuery().
|
||||
AggregateTypes("instance").
|
||||
EventTypes(instance.InstanceAddedEventType).
|
||||
Builder(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, instance := range instances {
|
||||
ctx := authz.WithInstanceID(ctx, instance)
|
||||
if err := projection.OrgDomainVerifiedFields.Trigger(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *FillFieldsForOrgDomainVerified) String() string {
|
||||
return "30_fill_fields_for_org_domain_verified"
|
||||
}
|
27
apps/api/cmd/setup/31.go
Normal file
27
apps/api/cmd/setup/31.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 31.sql
|
||||
addAggregateIndexToFields string
|
||||
)
|
||||
|
||||
type AddAggregateIndexToFields struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddAggregateIndexToFields) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addAggregateIndexToFields)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddAggregateIndexToFields) String() string {
|
||||
return "31_add_aggregate_index_to_fields"
|
||||
}
|
1
apps/api/cmd/setup/31.sql
Normal file
1
apps/api/cmd/setup/31.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS f_aggregate_object_type_idx ON eventstore.fields (aggregate_type, aggregate_id, object_type);
|
27
apps/api/cmd/setup/32.go
Normal file
27
apps/api/cmd/setup/32.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 32.sql
|
||||
addAuthSessionID string
|
||||
)
|
||||
|
||||
type AddAuthSessionID struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddAuthSessionID) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addAuthSessionID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddAuthSessionID) String() string {
|
||||
return "32_add_auth_sessionID"
|
||||
}
|
3
apps/api/cmd/setup/32.sql
Normal file
3
apps/api/cmd/setup/32.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE IF EXISTS auth.user_sessions ADD COLUMN IF NOT EXISTS id TEXT;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS user_session_id ON auth.user_sessions (id, instance_id);
|
27
apps/api/cmd/setup/33.go
Normal file
27
apps/api/cmd/setup/33.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 33.sql
|
||||
addTwilioVerifyServiceSID string
|
||||
)
|
||||
|
||||
type SMSConfigs3TwilioAddVerifyServiceSid struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *SMSConfigs3TwilioAddVerifyServiceSid) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addTwilioVerifyServiceSID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *SMSConfigs3TwilioAddVerifyServiceSid) String() string {
|
||||
return "33_sms_configs3_twilio_add_verification_sid"
|
||||
}
|
1
apps/api/cmd/setup/33.sql
Normal file
1
apps/api/cmd/setup/33.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE IF EXISTS projections.sms_configs3_twilio ADD COLUMN IF NOT EXISTS verify_service_sid TEXT;
|
27
apps/api/cmd/setup/34.go
Normal file
27
apps/api/cmd/setup/34.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 34/34_cache_schema.sql
|
||||
addCacheSchema string
|
||||
)
|
||||
|
||||
type AddCacheSchema struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddCacheSchema) Execute(ctx context.Context, _ eventstore.Event) (err error) {
|
||||
_, err = mig.dbClient.ExecContext(ctx, addCacheSchema)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *AddCacheSchema) String() string {
|
||||
return "34_add_cache_schema"
|
||||
}
|
29
apps/api/cmd/setup/34/34_cache_schema.sql
Normal file
29
apps/api/cmd/setup/34/34_cache_schema.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
create schema if not exists cache;
|
||||
|
||||
create unlogged table if not exists cache.objects (
|
||||
cache_name varchar not null,
|
||||
id uuid not null default gen_random_uuid(),
|
||||
created_at timestamptz not null default now(),
|
||||
last_used_at timestamptz not null default now(),
|
||||
payload jsonb not null,
|
||||
|
||||
primary key(cache_name, id)
|
||||
)
|
||||
partition by list (cache_name);
|
||||
|
||||
create unlogged table if not exists cache.string_keys(
|
||||
cache_name varchar not null check (cache_name <> ''),
|
||||
index_id integer not null check (index_id > 0),
|
||||
index_key varchar not null check (index_key <> ''),
|
||||
object_id uuid not null,
|
||||
|
||||
primary key (cache_name, index_id, index_key),
|
||||
constraint fk_object
|
||||
foreign key(cache_name, object_id)
|
||||
references cache.objects(cache_name, id)
|
||||
on delete cascade
|
||||
)
|
||||
partition by list (cache_name);
|
||||
|
||||
create index if not exists string_keys_object_id_idx
|
||||
on cache.string_keys (cache_name, object_id); -- for delete cascade
|
39
apps/api/cmd/setup/35.go
Normal file
39
apps/api/cmd/setup/35.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 35/*.sql
|
||||
addPositionToEsWmIndex embed.FS
|
||||
)
|
||||
|
||||
type AddPositionToIndexEsWm struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *AddPositionToIndexEsWm) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
statements, err := readStatements(addPositionToEsWmIndex, "35")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range statements {
|
||||
logging.WithFields("file", stmt.file, "migration", mig.String()).Info("execute statement")
|
||||
if _, err := mig.dbClient.ExecContext(ctx, stmt.query); err != nil {
|
||||
return fmt.Errorf("%s %s: %w", mig.String(), stmt.file, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *AddPositionToIndexEsWm) String() string {
|
||||
return "35_add_position_to_index_es_wm"
|
||||
}
|
2
apps/api/cmd/setup/35/00_create_index.sql
Normal file
2
apps/api/cmd/setup/35/00_create_index.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS es_wm_temp
|
||||
ON eventstore.events2 (instance_id, aggregate_id, aggregate_type, event_type, "position");
|
1
apps/api/cmd/setup/35/01_drop_index.sql
Normal file
1
apps/api/cmd/setup/35/01_drop_index.sql
Normal file
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS eventstore.es_wm;
|
1
apps/api/cmd/setup/35/02_alter_index.sql
Normal file
1
apps/api/cmd/setup/35/02_alter_index.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER INDEX eventstore.es_wm_temp RENAME TO es_wm;
|
118
apps/api/cmd/setup/36.go
Normal file
118
apps/api/cmd/setup/36.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/milestone"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 36.sql
|
||||
getProjectedMilestones string
|
||||
)
|
||||
|
||||
type FillV3Milestones struct {
|
||||
dbClient *database.DB
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
|
||||
type instanceMilestone struct {
|
||||
Type milestone.Type
|
||||
Reached time.Time
|
||||
Pushed *time.Time
|
||||
}
|
||||
|
||||
func (mig *FillV3Milestones) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
im, err := mig.getProjectedMilestones(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mig.pushEventsByInstance(ctx, im)
|
||||
}
|
||||
|
||||
func (mig *FillV3Milestones) getProjectedMilestones(ctx context.Context) (map[string][]instanceMilestone, error) {
|
||||
type row struct {
|
||||
InstanceID string
|
||||
Type milestone.Type
|
||||
Reached time.Time
|
||||
Pushed *time.Time
|
||||
}
|
||||
|
||||
rows, _ := mig.dbClient.Pool.Query(ctx, getProjectedMilestones)
|
||||
scanned, err := pgx.CollectRows(rows, pgx.RowToStructByPos[row])
|
||||
var pgError *pgconn.PgError
|
||||
// catch ERROR: relation "projections.milestones" does not exist
|
||||
if errors.As(err, &pgError) && pgError.SQLState() == "42P01" {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("milestones get: %w", err)
|
||||
}
|
||||
milestoneMap := make(map[string][]instanceMilestone)
|
||||
for _, s := range scanned {
|
||||
milestoneMap[s.InstanceID] = append(milestoneMap[s.InstanceID], instanceMilestone{
|
||||
Type: s.Type,
|
||||
Reached: s.Reached,
|
||||
Pushed: s.Pushed,
|
||||
})
|
||||
}
|
||||
return milestoneMap, nil
|
||||
}
|
||||
|
||||
// pushEventsByInstance creates the v2 milestone events by instance.
|
||||
// This prevents we will try to push 6*N(instance) events in one push.
|
||||
func (mig *FillV3Milestones) pushEventsByInstance(ctx context.Context, milestoneMap map[string][]instanceMilestone) error {
|
||||
// keep a deterministic order by instance ID.
|
||||
order := make([]string, 0, len(milestoneMap))
|
||||
for k := range milestoneMap {
|
||||
order = append(order, k)
|
||||
}
|
||||
slices.Sort(order)
|
||||
|
||||
for i, instanceID := range order {
|
||||
logging.WithFields("instance_id", instanceID, "migration", mig.String(), "progress", fmt.Sprintf("%d/%d", i+1, len(order))).Info("filter existing milestone events")
|
||||
|
||||
// because each Push runs in a separate TX, we need to make sure that events
|
||||
// from a partially executed migration are pushed again.
|
||||
model := command.NewMilestonesReachedWriteModel(instanceID)
|
||||
if err := mig.eventstore.FilterToQueryReducer(ctx, model); err != nil {
|
||||
return fmt.Errorf("milestones filter: %w", err)
|
||||
}
|
||||
if model.InstanceCreated {
|
||||
logging.WithFields("instance_id", instanceID, "migration", mig.String()).Info("milestone events already migrated")
|
||||
continue // This instance was migrated, skip
|
||||
}
|
||||
logging.WithFields("instance_id", instanceID, "migration", mig.String()).Info("push milestone events")
|
||||
|
||||
aggregate := milestone.NewInstanceAggregate(instanceID)
|
||||
|
||||
cmds := make([]eventstore.Command, 0, len(milestoneMap[instanceID])*2)
|
||||
for _, m := range milestoneMap[instanceID] {
|
||||
cmds = append(cmds, milestone.NewReachedEventWithDate(ctx, aggregate, m.Type, &m.Reached))
|
||||
if m.Pushed != nil {
|
||||
cmds = append(cmds, milestone.NewPushedEventWithDate(ctx, aggregate, m.Type, nil, "", m.Pushed))
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := mig.eventstore.Push(ctx, cmds...); err != nil {
|
||||
return fmt.Errorf("milestones push: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *FillV3Milestones) String() string {
|
||||
return "36_fill_v3_milestones"
|
||||
}
|
4
apps/api/cmd/setup/36.sql
Normal file
4
apps/api/cmd/setup/36.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
SELECT instance_id, type, reached_date, last_pushed_date
|
||||
FROM projections.milestones
|
||||
WHERE reached_date IS NOT NULL
|
||||
ORDER BY instance_id, reached_date;
|
27
apps/api/cmd/setup/37.go
Normal file
27
apps/api/cmd/setup/37.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 37.sql
|
||||
addBackChannelLogoutURI string
|
||||
)
|
||||
|
||||
type Apps7OIDConfigsBackChannelLogoutURI struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *Apps7OIDConfigsBackChannelLogoutURI) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addBackChannelLogoutURI)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *Apps7OIDConfigsBackChannelLogoutURI) String() string {
|
||||
return "37_apps7_oidc_configs_add_back_channel_logout_uri"
|
||||
}
|
1
apps/api/cmd/setup/37.sql
Normal file
1
apps/api/cmd/setup/37.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE IF EXISTS projections.apps7_oidc_configs ADD COLUMN IF NOT EXISTS back_channel_logout_uri TEXT;
|
27
apps/api/cmd/setup/38.go
Normal file
27
apps/api/cmd/setup/38.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 38.sql
|
||||
backChannelLogoutCurrentState string
|
||||
)
|
||||
|
||||
type BackChannelLogoutNotificationStart struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *BackChannelLogoutNotificationStart) Execute(ctx context.Context, e eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, backChannelLogoutCurrentState, e.Sequence(), e.CreatedAt(), e.Position())
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *BackChannelLogoutNotificationStart) String() string {
|
||||
return "38_back_channel_logout_notification_start_"
|
||||
}
|
20
apps/api/cmd/setup/38.sql
Normal file
20
apps/api/cmd/setup/38.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
INSERT INTO projections.current_states (
|
||||
instance_id
|
||||
, projection_name
|
||||
, last_updated
|
||||
, sequence
|
||||
, event_date
|
||||
, position
|
||||
, filter_offset
|
||||
)
|
||||
SELECT instance_id
|
||||
, 'projections.notifications_back_channel_logout'
|
||||
, now()
|
||||
, $1
|
||||
, $2
|
||||
, $3
|
||||
, 0
|
||||
FROM eventstore.events2
|
||||
WHERE aggregate_type = 'instance'
|
||||
AND event_type = 'instance.added'
|
||||
ON CONFLICT DO NOTHING;
|
51
apps/api/cmd/setup/39.go
Normal file
51
apps/api/cmd/setup/39.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 39.sql
|
||||
deleteStaleOrgFields string
|
||||
)
|
||||
|
||||
type DeleteStaleOrgFields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
|
||||
func (mig *DeleteStaleOrgFields) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
instances, err := mig.eventstore.InstanceIDs(
|
||||
ctx,
|
||||
eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs).
|
||||
OrderDesc().
|
||||
AddQuery().
|
||||
AggregateTypes("instance").
|
||||
EventTypes(instance.InstanceAddedEventType).
|
||||
Builder(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, instance := range instances {
|
||||
logging.WithFields("instance_id", instance, "migration", mig.String(), "progress", fmt.Sprintf("%d/%d", i+1, len(instances))).Info("execute delete query")
|
||||
if _, err := mig.eventstore.Client().ExecContext(ctx, deleteStaleOrgFields, instance); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*DeleteStaleOrgFields) Check(map[string]any) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (*DeleteStaleOrgFields) String() string {
|
||||
return "repeatable_delete_stale_org_fields"
|
||||
}
|
9
apps/api/cmd/setup/39.sql
Normal file
9
apps/api/cmd/setup/39.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
DELETE FROM eventstore.fields
|
||||
WHERE aggregate_type = 'org'
|
||||
AND aggregate_id IN (
|
||||
SELECT aggregate_id
|
||||
FROM eventstore.events2
|
||||
WHERE instance_id = $1
|
||||
AND aggregate_type = 'org'
|
||||
AND event_type = 'org.removed'
|
||||
);
|
115
apps/api/cmd/setup/40.go
Normal file
115
apps/api/cmd/setup/40.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
// query filenames
|
||||
const (
|
||||
fileInTxOrderType = "00_in_tx_order_type.sql"
|
||||
fileType = "01_type.sql"
|
||||
fileFunc = "02_func.sql"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 40/*.sql
|
||||
initPushFunc embed.FS
|
||||
)
|
||||
|
||||
type InitPushFunc struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *InitPushFunc) Execute(ctx context.Context, _ eventstore.Event) (err error) {
|
||||
conn, err := mig.dbClient.Conn(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
closeErr := conn.Close()
|
||||
logging.OnError(closeErr).Debug("failed to release connection")
|
||||
// Force the pool to reopen connections to apply the new types
|
||||
mig.dbClient.Pool.Reset()
|
||||
}()
|
||||
statements, err := mig.prepareStatements(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range statements {
|
||||
logging.WithFields("file", stmt.file, "migration", mig.String()).Info("execute statement")
|
||||
if _, err := conn.ExecContext(ctx, stmt.query); err != nil {
|
||||
return fmt.Errorf("%s %s: %w", mig.String(), stmt.file, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *InitPushFunc) String() string {
|
||||
return "40_init_push_func_v4"
|
||||
}
|
||||
|
||||
func (mig *InitPushFunc) prepareStatements(ctx context.Context) ([]statement, error) {
|
||||
funcTmpl, err := template.ParseFS(initPushFunc, mig.filePath(fileFunc))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("prepare steps: %w", err)
|
||||
}
|
||||
typeName, err := mig.inTxOrderType(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("prepare steps: %w", err)
|
||||
}
|
||||
var funcStep strings.Builder
|
||||
err = funcTmpl.Execute(&funcStep, struct {
|
||||
InTxOrderType string
|
||||
}{
|
||||
InTxOrderType: typeName,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("prepare steps: %w", err)
|
||||
}
|
||||
typeStatement, err := fs.ReadFile(initPushFunc, mig.filePath(fileType))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("prepare steps: %w", err)
|
||||
}
|
||||
return []statement{
|
||||
{
|
||||
file: fileType,
|
||||
query: string(typeStatement),
|
||||
},
|
||||
{
|
||||
file: fileFunc,
|
||||
query: funcStep.String(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (mig *InitPushFunc) inTxOrderType(ctx context.Context) (typeName string, err error) {
|
||||
query, err := fs.ReadFile(initPushFunc, mig.filePath(fileInTxOrderType))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get in_tx_order_type: %w", err)
|
||||
}
|
||||
|
||||
err = mig.dbClient.QueryRowContext(ctx, func(row *sql.Row) error {
|
||||
return row.Scan(&typeName)
|
||||
}, string(query))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get in_tx_order_type: %w", err)
|
||||
}
|
||||
return typeName, nil
|
||||
}
|
||||
|
||||
func (mig *InitPushFunc) filePath(fileName string) string {
|
||||
return path.Join("40", fileName)
|
||||
}
|
5
apps/api/cmd/setup/40/00_in_tx_order_type.sql
Normal file
5
apps/api/cmd/setup/40/00_in_tx_order_type.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
SELECT data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'eventstore'
|
||||
AND table_name = 'events2'
|
||||
AND column_name = 'in_tx_order';
|
15
apps/api/cmd/setup/40/01_type.sql
Normal file
15
apps/api/cmd/setup/40/01_type.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- represents an event to be created.
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE eventstore.command AS (
|
||||
instance_id TEXT
|
||||
, aggregate_type TEXT
|
||||
, aggregate_id TEXT
|
||||
, command_type TEXT
|
||||
, revision INT2
|
||||
, payload JSONB
|
||||
, creator TEXT
|
||||
, owner TEXT
|
||||
);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
92
apps/api/cmd/setup/40/02_func.sql
Normal file
92
apps/api/cmd/setup/40/02_func.sql
Normal file
@@ -0,0 +1,92 @@
|
||||
CREATE OR REPLACE FUNCTION eventstore.latest_aggregate_state(
|
||||
instance_id TEXT
|
||||
, aggregate_type TEXT
|
||||
, aggregate_id TEXT
|
||||
|
||||
, sequence OUT BIGINT
|
||||
, owner OUT TEXT
|
||||
)
|
||||
LANGUAGE 'plpgsql'
|
||||
STABLE PARALLEL SAFE
|
||||
AS $$
|
||||
BEGIN
|
||||
SELECT
|
||||
COALESCE(e.sequence, 0) AS sequence
|
||||
, e.owner
|
||||
INTO
|
||||
sequence
|
||||
, owner
|
||||
FROM
|
||||
eventstore.events2 e
|
||||
WHERE
|
||||
e.instance_id = $1
|
||||
AND e.aggregate_type = $2
|
||||
AND e.aggregate_id = $3
|
||||
ORDER BY
|
||||
e.sequence DESC
|
||||
LIMIT 1;
|
||||
|
||||
RETURN;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION eventstore.commands_to_events(commands eventstore.command[])
|
||||
RETURNS SETOF eventstore.events2
|
||||
LANGUAGE 'plpgsql'
|
||||
STABLE PARALLEL SAFE
|
||||
ROWS 10
|
||||
AS $$
|
||||
DECLARE
|
||||
"aggregate" RECORD;
|
||||
current_sequence BIGINT;
|
||||
current_owner TEXT;
|
||||
BEGIN
|
||||
FOR "aggregate" IN
|
||||
SELECT DISTINCT
|
||||
instance_id
|
||||
, aggregate_type
|
||||
, aggregate_id
|
||||
FROM UNNEST(commands)
|
||||
LOOP
|
||||
SELECT
|
||||
*
|
||||
INTO
|
||||
current_sequence
|
||||
, current_owner
|
||||
FROM eventstore.latest_aggregate_state(
|
||||
"aggregate".instance_id
|
||||
, "aggregate".aggregate_type
|
||||
, "aggregate".aggregate_id
|
||||
);
|
||||
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
c.instance_id
|
||||
, c.aggregate_type
|
||||
, c.aggregate_id
|
||||
, c.command_type -- AS event_type
|
||||
, COALESCE(current_sequence, 0) + ROW_NUMBER() OVER () -- AS sequence
|
||||
, c.revision
|
||||
, NOW() -- AS created_at
|
||||
, c.payload
|
||||
, c.creator
|
||||
, COALESCE(current_owner, c.owner) -- AS owner
|
||||
, EXTRACT(EPOCH FROM NOW()) -- AS position
|
||||
, c.ordinality::{{ .InTxOrderType }} -- AS in_tx_order
|
||||
FROM
|
||||
UNNEST(commands) WITH ORDINALITY AS c
|
||||
WHERE
|
||||
c.instance_id = aggregate.instance_id
|
||||
AND c.aggregate_type = aggregate.aggregate_type
|
||||
AND c.aggregate_id = aggregate.aggregate_id;
|
||||
END LOOP;
|
||||
RETURN;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION eventstore.push(commands eventstore.command[]) RETURNS SETOF eventstore.events2 VOLATILE AS $$
|
||||
INSERT INTO eventstore.events2
|
||||
SELECT * FROM eventstore.commands_to_events(commands)
|
||||
ORDER BY in_tx_order
|
||||
RETURNING *
|
||||
$$ LANGUAGE SQL;
|
44
apps/api/cmd/setup/41.go
Normal file
44
apps/api/cmd/setup/41.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
type FillFieldsForInstanceDomains struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
|
||||
func (mig *FillFieldsForInstanceDomains) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
instances, err := mig.eventstore.InstanceIDs(
|
||||
ctx,
|
||||
eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs).
|
||||
OrderDesc().
|
||||
AddQuery().
|
||||
AggregateTypes("instance").
|
||||
EventTypes(instance.InstanceAddedEventType).
|
||||
Builder(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, instance := range instances {
|
||||
ctx := authz.WithInstanceID(ctx, instance)
|
||||
if err := projection.InstanceDomainFields.Trigger(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *FillFieldsForInstanceDomains) String() string {
|
||||
return "repeatable_fill_fields_for_instance_domains"
|
||||
}
|
||||
|
||||
func (f *FillFieldsForInstanceDomains) Check(lastRun map[string]interface{}) bool {
|
||||
return true
|
||||
}
|
27
apps/api/cmd/setup/42.go
Normal file
27
apps/api/cmd/setup/42.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 42.sql
|
||||
addOIDCAppLoginVersion string
|
||||
)
|
||||
|
||||
type Apps7OIDCConfigsLoginVersion struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *Apps7OIDCConfigsLoginVersion) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, addOIDCAppLoginVersion)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *Apps7OIDCConfigsLoginVersion) String() string {
|
||||
return "40_apps7_oidc_configs_login_version"
|
||||
}
|
2
apps/api/cmd/setup/42.sql
Normal file
2
apps/api/cmd/setup/42.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE IF EXISTS projections.apps7_oidc_configs ADD COLUMN IF NOT EXISTS login_version SMALLINT;
|
||||
ALTER TABLE IF EXISTS projections.apps7_oidc_configs ADD COLUMN IF NOT EXISTS login_base_uri TEXT;
|
39
apps/api/cmd/setup/43.go
Normal file
39
apps/api/cmd/setup/43.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 43/*.sql
|
||||
createFieldsDomainIndex embed.FS
|
||||
)
|
||||
|
||||
type CreateFieldsDomainIndex struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *CreateFieldsDomainIndex) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
statements, err := readStatements(createFieldsDomainIndex, "43")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range statements {
|
||||
logging.WithFields("file", stmt.file, "migration", mig.String()).Info("execute statement")
|
||||
if _, err := mig.dbClient.ExecContext(ctx, stmt.query); err != nil {
|
||||
return fmt.Errorf("%s %s: %w", mig.String(), stmt.file, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *CreateFieldsDomainIndex) String() string {
|
||||
return "43_create_fields_domain_index"
|
||||
}
|
3
apps/api/cmd/setup/43/43.sql
Normal file
3
apps/api/cmd/setup/43/43.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS fields_instance_domains_idx
|
||||
ON eventstore.fields (object_id) INCLUDE (instance_id)
|
||||
WHERE object_type = 'instance_domain' AND field_name = 'domain';
|
39
apps/api/cmd/setup/44.go
Normal file
39
apps/api/cmd/setup/44.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 44/*.sql
|
||||
replaceCurrentSequencesIndex embed.FS
|
||||
)
|
||||
|
||||
type ReplaceCurrentSequencesIndex struct {
|
||||
dbClient *database.DB
|
||||
}
|
||||
|
||||
func (mig *ReplaceCurrentSequencesIndex) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
statements, err := readStatements(replaceCurrentSequencesIndex, "44")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range statements {
|
||||
logging.WithFields("file", stmt.file, "migration", mig.String()).Info("execute statement")
|
||||
if _, err := mig.dbClient.ExecContext(ctx, stmt.query); err != nil {
|
||||
return fmt.Errorf("%s %s: %w", mig.String(), stmt.file, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mig *ReplaceCurrentSequencesIndex) String() string {
|
||||
return "44_replace_current_sequences_index"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user