- шаги PocketBase переехали из файла в пакет internal/adapter/repo/pocketbase/migrations, файл на шаг с именем зарегистрированного шага; туда же имена коллекций, срок сессии — в provider.go - ключ migrations в docs/.docs.json наведён на этот каталог: прежнее значение указывало на несуществующий migrations/, и шаг гейта проходил зелёным при всякой правке схемы - app.go подключает пакет шагов явным пустым импортом: пропавшая ссылка на константы унесла бы регистрацию, и хранилище поднялось бы без коллекций
110 lines
4.7 KiB
Go
110 lines
4.7 KiB
Go
package migrations
|
||
|
||
import (
|
||
"github.com/pocketbase/pocketbase/core"
|
||
|
||
"git.vakhrushev.me/av/transcriber/internal/entity"
|
||
)
|
||
|
||
func up202608110001(app core.App) error {
|
||
files := core.NewBaseCollection(FilesCollection)
|
||
files.Fields.Add(
|
||
// Сам файл. Защищённым поле не помечено намеренно: право прочитать
|
||
// запись даёт знание её идентификатора, и файл встаёт вровень с опросом
|
||
// готовности задачи, а не ниже.
|
||
//
|
||
// Потолок задан **числом**: нулём библиотека читает не «без предела», а
|
||
// своё умолчание в 5 МиБ, и на нём отваливалось бы всё длиннее пяти
|
||
// минут. Число выведено из расчётного потолка записи в шесть часов с
|
||
// запасом на видео; оно же стоит строкой в docs/database.md.
|
||
&core.FileField{Name: "file", MaxSelect: 1, MaxSize: entity.MaxRecordSize},
|
||
// Где лежит копия. Поле названо `location`, а не `storage`: последним
|
||
// словом зовут само хранилище, и третий смысл развёл бы одно слово по
|
||
// разным вещам.
|
||
&core.SelectField{
|
||
Name: "location",
|
||
Values: []string{entity.LocationLocal, entity.LocationS3},
|
||
MaxSelect: 1,
|
||
Required: true,
|
||
},
|
||
// Ключ объекта во внешнем хранилище; у местной копии пуст.
|
||
&core.TextField{Name: "object_key"},
|
||
&core.NumberField{Name: "size", OnlyInt: true},
|
||
&core.AutodateField{Name: "created", OnCreate: true},
|
||
&core.AutodateField{Name: "updated", OnCreate: true, OnUpdate: true},
|
||
)
|
||
|
||
if err := app.Save(files); err != nil {
|
||
return err
|
||
}
|
||
|
||
jobs := core.NewBaseCollection(JobsCollection)
|
||
jobs.Fields.Add(
|
||
// Перечень состояний закрыт схемой: задача, заведённая в панели руками,
|
||
// не должна попасть в выборку с состоянием, которого конвейер не знает.
|
||
&core.SelectField{
|
||
Name: "state",
|
||
Values: []string{
|
||
entity.StateCreated,
|
||
entity.StateConverted,
|
||
entity.StateTranscribe,
|
||
entity.StateDone,
|
||
entity.StateFailed,
|
||
entity.StateDead,
|
||
},
|
||
MaxSelect: 1,
|
||
Required: true,
|
||
},
|
||
&core.SelectField{
|
||
Name: "source",
|
||
Values: []string{entity.SourceUnknown, entity.SourceApi, entity.SourceTelegram},
|
||
MaxSelect: 1,
|
||
Required: true,
|
||
},
|
||
// Текущий файл задачи: шаг конвейера переставляет ссылку на свой
|
||
// результат.
|
||
// Обязательна: задача без записи не может пройти ни одного шага, и
|
||
// заведённая в панели руками она дошла бы до шага только затем, чтобы
|
||
// отказать. Компилятор этого не держит — держит схема.
|
||
&core.RelationField{
|
||
Name: "file",
|
||
CollectionId: files.Id,
|
||
MaxSelect: 1,
|
||
Required: true,
|
||
},
|
||
&core.TextField{Name: "error_text"},
|
||
&core.TextField{Name: "acquisition_id"},
|
||
&core.DateField{Name: "acquire_time"},
|
||
&core.DateField{Name: "delay_time"},
|
||
// Число попыток: растёт при каждом захвате, обнуляется на шаге,
|
||
// завершившемся без отказа.
|
||
&core.NumberField{Name: "attempts", OnlyInt: true, Min: ptr(0.0)},
|
||
&core.TextField{Name: "recognition_op_id"},
|
||
&core.EditorField{Name: "transcription_text"},
|
||
&core.NumberField{Name: "tg_chat_id", OnlyInt: true},
|
||
&core.NumberField{Name: "tg_reply_message_id", OnlyInt: true},
|
||
&core.AutodateField{Name: "created", OnCreate: true},
|
||
&core.AutodateField{Name: "updated", OnCreate: true, OnUpdate: true},
|
||
)
|
||
|
||
// Выборка воркера идёт по состоянию, паузе и сроку захвата — индекс по
|
||
// состоянию снимает полный перебор, который был у прежней таблицы.
|
||
jobs.AddIndex("idx_transcribe_jobs_state", false, "state", "")
|
||
|
||
return app.Save(jobs)
|
||
}
|
||
|
||
func down202608110001(app core.App) error {
|
||
// Порядок обратный порядку заведения: задачи ссылаются на файлы.
|
||
for _, name := range []string{JobsCollection, FilesCollection} {
|
||
collection, err := app.FindCollectionByNameOrId(name)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
if err := app.Delete(collection); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|