Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
110 lines
3.0 KiB
Go
110 lines
3.0 KiB
Go
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"]++
|
|
}
|
|
}
|