Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// migrateFiles copies file *metadata* only.
|
||||
//
|
||||
// Blobs strategy (cutover):
|
||||
// - Do NOT stream MySQL/local blob bytes through the migrator.
|
||||
// - Preserve legacy url/path in files.path and legacy id in metadata._legacy_file_id.
|
||||
// - Operators re-attach object storage / local volumes under the same relative paths,
|
||||
// or run a separate rsync/S3 sync keyed by legacy id after DNS freeze.
|
||||
// - raw_products.file_id is left unset until a follow-up remapper exists.
|
||||
func migrateFiles(
|
||||
ctx context.Context,
|
||||
mysqlDB *sql.DB,
|
||||
pg *pgxpool.Pool,
|
||||
companyMap, userMap, fileMap map[string]string,
|
||||
allow map[string]bool,
|
||||
report map[string]int,
|
||||
dryRun bool,
|
||||
) {
|
||||
if !mysqlTableExists(ctx, mysqlDB, "files") {
|
||||
log.Printf("files skipped: table missing (blobs strategy: metadata-only when present)")
|
||||
return
|
||||
}
|
||||
|
||||
clause, cargs := mysqlCompanyFilter("company_id", allow)
|
||||
rows, err := mysqlDB.QueryContext(ctx, `
|
||||
SELECT id, company_id, COALESCE(user_id, ''), file_name,
|
||||
COALESCE(file_type, ''), COALESCE(file_size, 0),
|
||||
COALESCE(status, 'uploaded'), url, metadata
|
||||
FROM files WHERE 1=1`+clause, cargs...)
|
||||
if err != nil {
|
||||
// Older dumps may lack metadata/url/status.
|
||||
rows, err = mysqlDB.QueryContext(ctx, `
|
||||
SELECT id, company_id, COALESCE(user_id, ''), file_name,
|
||||
COALESCE(file_type, ''), COALESCE(file_size, 0),
|
||||
'uploaded', NULL, NULL
|
||||
FROM files WHERE 1=1`+clause, cargs...)
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("files skipped: %v", err)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var legacyID int64
|
||||
var companyLegacy, userLegacy, name, fileType, status string
|
||||
var size int64
|
||||
var url sql.NullString
|
||||
var metadata []byte
|
||||
if err := rows.Scan(&legacyID, &companyLegacy, &userLegacy, &name, &fileType, &size, &status, &url, &metadata); err != nil {
|
||||
report["files_skipped"]++
|
||||
continue
|
||||
}
|
||||
cid, ok := companyMap[companyLegacy]
|
||||
if !ok {
|
||||
report["files_skipped"]++
|
||||
continue
|
||||
}
|
||||
meta := map[string]any{}
|
||||
if len(metadata) > 0 {
|
||||
_ = json.Unmarshal(metadata, &meta)
|
||||
}
|
||||
meta["_legacy_file_id"] = legacyID
|
||||
meta["_blob_strategy"] = "metadata_only_resync_paths"
|
||||
if fileType != "" {
|
||||
meta["file_type"] = fileType
|
||||
}
|
||||
metaBytes, _ := json.Marshal(meta)
|
||||
|
||||
newID := uuid.New()
|
||||
legacyKey := strconv.FormatInt(legacyID, 10)
|
||||
fileMap[legacyKey] = newID.String()
|
||||
|
||||
var uid *string
|
||||
if userLegacy != "" {
|
||||
if mapped, ok := userMap[userLegacy]; ok {
|
||||
uid = &mapped
|
||||
}
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
report["files"]++
|
||||
continue
|
||||
}
|
||||
_, err = pg.Exec(ctx, `
|
||||
INSERT INTO files (id, company_id, user_id, name, path, content_type, size_bytes, status, metadata)
|
||||
VALUES ($1, $2, $3::uuid, $4, $5, $6, $7, $8, $9::jsonb)`,
|
||||
newID, cid, uid, name, nullString(url), nullStr(fileType), size, status, string(metaBytes))
|
||||
if err != nil {
|
||||
log.Printf("files insert %d: %v", legacyID, err)
|
||||
delete(fileMap, legacyKey)
|
||||
report["files_skipped"]++
|
||||
continue
|
||||
}
|
||||
report["files"]++
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user