はじまりの大地

このコミットが含まれているのは:
2024-07-15 09:14:04 +09:00
コミット 6632905f32
3501個のファイルの変更1439465行の追加0行の削除
+366
ファイルの表示
@@ -0,0 +1,366 @@
import {
RunnerJob,
RunnerJobAdmin,
RunnerJobState,
type RunnerJobPayload,
type RunnerJobPrivatePayload,
type RunnerJobStateType,
type RunnerJobType
} from '@peertube/peertube-models'
import { isArray, isUUIDValid } from '@server/helpers/custom-validators/misc.js'
import { CONSTRAINTS_FIELDS, RUNNER_JOB_STATES } from '@server/initializers/constants.js'
import { MRunnerJob, MRunnerJobRunner, MRunnerJobRunnerParent } from '@server/types/models/runners/index.js'
import { Op, Transaction } from 'sequelize'
import {
AllowNull,
BelongsTo,
Column,
CreatedAt,
DataType,
Default,
ForeignKey,
IsUUID, Scopes,
Table,
UpdatedAt
} from 'sequelize-typescript'
import { SequelizeModel, getSort, searchAttribute } from '../shared/index.js'
import { RunnerModel } from './runner.js'
enum ScopeNames {
WITH_RUNNER = 'WITH_RUNNER',
WITH_PARENT = 'WITH_PARENT'
}
@Scopes(() => ({
[ScopeNames.WITH_RUNNER]: {
include: [
{
model: RunnerModel.unscoped(),
required: false
}
]
},
[ScopeNames.WITH_PARENT]: {
include: [
{
model: RunnerJobModel.unscoped(),
required: false
}
]
}
}))
@Table({
tableName: 'runnerJob',
indexes: [
{
fields: [ 'uuid' ],
unique: true
},
{
fields: [ 'processingJobToken' ],
unique: true
},
{
fields: [ 'runnerId' ]
}
]
})
export class RunnerJobModel extends SequelizeModel<RunnerJobModel> {
@AllowNull(false)
@IsUUID(4)
@Column(DataType.UUID)
uuid: string
@AllowNull(false)
@Column
type: RunnerJobType
@AllowNull(false)
@Column(DataType.JSONB)
payload: RunnerJobPayload
@AllowNull(false)
@Column(DataType.JSONB)
privatePayload: RunnerJobPrivatePayload
@AllowNull(false)
@Column
state: RunnerJobStateType
@AllowNull(false)
@Default(0)
@Column
failures: number
@AllowNull(true)
@Column(DataType.STRING(CONSTRAINTS_FIELDS.RUNNER_JOBS.ERROR_MESSAGE.max))
error: string
// Less has priority
@AllowNull(false)
@Column
priority: number
// Used to fetch the appropriate job when the runner wants to post the result
@AllowNull(true)
@Column
processingJobToken: string
@AllowNull(true)
@Column
progress: number
@AllowNull(true)
@Column
startedAt: Date
@AllowNull(true)
@Column
finishedAt: Date
@CreatedAt
createdAt: Date
@UpdatedAt
updatedAt: Date
@ForeignKey(() => RunnerJobModel)
@Column
dependsOnRunnerJobId: number
@BelongsTo(() => RunnerJobModel, {
foreignKey: {
name: 'dependsOnRunnerJobId',
allowNull: true
},
onDelete: 'cascade'
})
DependsOnRunnerJob: Awaited<RunnerJobModel>
@ForeignKey(() => RunnerModel)
@Column
runnerId: number
@BelongsTo(() => RunnerModel, {
foreignKey: {
name: 'runnerId',
allowNull: true
},
onDelete: 'SET NULL'
})
Runner: Awaited<RunnerModel>
// ---------------------------------------------------------------------------
static loadWithRunner (uuid: string) {
const query = {
where: { uuid }
}
return RunnerJobModel.scope(ScopeNames.WITH_RUNNER).findOne<MRunnerJobRunner>(query)
}
static loadByRunnerAndJobTokensWithRunner (options: {
uuid: string
runnerToken: string
jobToken: string
}) {
const { uuid, runnerToken, jobToken } = options
const query = {
where: {
uuid,
processingJobToken: jobToken
},
include: {
model: RunnerModel.unscoped(),
required: true,
where: {
runnerToken
}
}
}
return RunnerJobModel.findOne<MRunnerJobRunner>(query)
}
static listAvailableJobs () {
const query = {
limit: 10,
order: getSort('priority'),
where: {
state: RunnerJobState.PENDING
}
}
return RunnerJobModel.findAll<MRunnerJob>(query)
}
static listStalledJobs (options: {
staleTimeMS: number
types: RunnerJobType[]
}) {
const before = new Date(Date.now() - options.staleTimeMS)
return RunnerJobModel.findAll<MRunnerJob>({
where: {
type: {
[Op.in]: options.types
},
state: RunnerJobState.PROCESSING,
updatedAt: {
[Op.lt]: before
}
}
})
}
static listChildrenOf (job: MRunnerJob, transaction?: Transaction) {
const query = {
where: {
dependsOnRunnerJobId: job.id
},
transaction
}
return RunnerJobModel.findAll<MRunnerJob>(query)
}
static listForApi (options: {
start: number
count: number
sort: string
search?: string
stateOneOf?: RunnerJobStateType[]
}) {
const { start, count, sort, search, stateOneOf } = options
const query = {
offset: start,
limit: count,
order: getSort(sort),
where: []
}
if (search) {
if (isUUIDValid(search)) {
query.where.push({ uuid: search })
} else {
query.where.push({
[Op.or]: [
searchAttribute(search, 'type'),
searchAttribute(search, '$Runner.name$')
]
})
}
}
if (isArray(stateOneOf) && stateOneOf.length !== 0) {
query.where.push({
state: {
[Op.in]: stateOneOf
}
})
}
return Promise.all([
RunnerJobModel.scope([ ScopeNames.WITH_RUNNER ]).count(query),
RunnerJobModel.scope([ ScopeNames.WITH_RUNNER, ScopeNames.WITH_PARENT ]).findAll<MRunnerJobRunnerParent>(query)
]).then(([ total, data ]) => ({ total, data }))
}
static updateDependantJobsOf (runnerJob: MRunnerJob) {
const where = {
dependsOnRunnerJobId: runnerJob.id
}
return RunnerJobModel.update({ state: RunnerJobState.PENDING }, { where })
}
static cancelAllNonFinishedJobs (options: { type: RunnerJobType }) {
const where = {
type: options.type,
state: {
[Op.in]: [ RunnerJobState.COMPLETING, RunnerJobState.PENDING, RunnerJobState.PROCESSING, RunnerJobState.WAITING_FOR_PARENT_JOB ]
}
}
return RunnerJobModel.update({ state: RunnerJobState.CANCELLED }, { where })
}
// ---------------------------------------------------------------------------
resetToPending () {
this.state = RunnerJobState.PENDING
this.processingJobToken = null
this.progress = null
this.startedAt = null
this.runnerId = null
}
setToErrorOrCancel (
// eslint-disable-next-line max-len
state: typeof RunnerJobState.PARENT_ERRORED | typeof RunnerJobState.ERRORED | typeof RunnerJobState.CANCELLED | typeof RunnerJobState.PARENT_CANCELLED
) {
this.state = state
this.processingJobToken = null
this.finishedAt = new Date()
}
toFormattedJSON (this: MRunnerJobRunnerParent): RunnerJob {
const runner = this.Runner
? {
id: this.Runner.id,
name: this.Runner.name,
description: this.Runner.description
}
: null
const parent = this.DependsOnRunnerJob
? {
id: this.DependsOnRunnerJob.id,
uuid: this.DependsOnRunnerJob.uuid,
type: this.DependsOnRunnerJob.type,
state: {
id: this.DependsOnRunnerJob.state,
label: RUNNER_JOB_STATES[this.DependsOnRunnerJob.state]
}
}
: undefined
return {
uuid: this.uuid,
type: this.type,
state: {
id: this.state,
label: RUNNER_JOB_STATES[this.state]
},
progress: this.progress,
priority: this.priority,
failures: this.failures,
error: this.error,
payload: this.payload,
startedAt: this.startedAt?.toISOString(),
finishedAt: this.finishedAt?.toISOString(),
createdAt: this.createdAt.toISOString(),
updatedAt: this.updatedAt.toISOString(),
parent,
runner
}
}
toFormattedAdminJSON (this: MRunnerJobRunnerParent): RunnerJobAdmin {
return {
...this.toFormattedJSON(),
privatePayload: this.privatePayload
}
}
}
+102
ファイルの表示
@@ -0,0 +1,102 @@
import { FindOptions, literal } from 'sequelize'
import { AllowNull, Column, CreatedAt, HasMany, Table, UpdatedAt } from 'sequelize-typescript'
import { MRunnerRegistrationToken } from '@server/types/models/runners/index.js'
import { RunnerRegistrationToken } from '@peertube/peertube-models'
import { SequelizeModel, getSort } from '../shared/index.js'
import { RunnerModel } from './runner.js'
/**
*
* Tokens used by PeerTube runners to register themselves to the PeerTube instance
*
*/
@Table({
tableName: 'runnerRegistrationToken',
indexes: [
{
fields: [ 'registrationToken' ],
unique: true
}
]
})
export class RunnerRegistrationTokenModel extends SequelizeModel<RunnerRegistrationTokenModel> {
@AllowNull(false)
@Column
registrationToken: string
@CreatedAt
createdAt: Date
@UpdatedAt
updatedAt: Date
@HasMany(() => RunnerModel, {
foreignKey: {
allowNull: true
},
onDelete: 'cascade'
})
Runners: Awaited<RunnerModel>[]
static load (id: number) {
return RunnerRegistrationTokenModel.findByPk(id)
}
static loadByRegistrationToken (registrationToken: string) {
const query = {
where: { registrationToken }
}
return RunnerRegistrationTokenModel.findOne(query)
}
static countTotal () {
return RunnerRegistrationTokenModel.unscoped().count()
}
static listForApi (options: {
start: number
count: number
sort: string
}) {
const { start, count, sort } = options
const query: FindOptions = {
attributes: {
include: [
[
literal('(SELECT COUNT(*) FROM "runner" WHERE "runner"."runnerRegistrationTokenId" = "RunnerRegistrationTokenModel"."id")'),
'registeredRunnersCount'
]
]
},
offset: start,
limit: count,
order: getSort(sort)
}
return Promise.all([
RunnerRegistrationTokenModel.count(query),
RunnerRegistrationTokenModel.findAll<MRunnerRegistrationToken>(query)
]).then(([ total, data ]) => ({ total, data }))
}
// ---------------------------------------------------------------------------
toFormattedJSON (this: MRunnerRegistrationToken): RunnerRegistrationToken {
const registeredRunnersCount = this.get('registeredRunnersCount') as number
return {
id: this.id,
registrationToken: this.registrationToken,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
registeredRunnersCount
}
}
}
+123
ファイルの表示
@@ -0,0 +1,123 @@
import { FindOptions } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript'
import { MRunner } from '@server/types/models/runners/index.js'
import { Runner } from '@peertube/peertube-models'
import { SequelizeModel, getSort } from '../shared/index.js'
import { RunnerRegistrationTokenModel } from './runner-registration-token.js'
import { CONSTRAINTS_FIELDS } from '@server/initializers/constants.js'
@Table({
tableName: 'runner',
indexes: [
{
fields: [ 'runnerToken' ],
unique: true
},
{
fields: [ 'runnerRegistrationTokenId' ]
},
{
fields: [ 'name' ],
unique: true
}
]
})
export class RunnerModel extends SequelizeModel<RunnerModel> {
// Used to identify the appropriate runner when it uses the runner REST API
@AllowNull(false)
@Column
runnerToken: string
@AllowNull(false)
@Column
name: string
@AllowNull(true)
@Column(DataType.STRING(CONSTRAINTS_FIELDS.RUNNERS.DESCRIPTION.max))
description: string
@AllowNull(false)
@Column
lastContact: Date
@AllowNull(false)
@Column
ip: string
@CreatedAt
createdAt: Date
@UpdatedAt
updatedAt: Date
@ForeignKey(() => RunnerRegistrationTokenModel)
@Column
runnerRegistrationTokenId: number
@BelongsTo(() => RunnerRegistrationTokenModel, {
foreignKey: {
allowNull: false
},
onDelete: 'cascade'
})
RunnerRegistrationToken: Awaited<RunnerRegistrationTokenModel>
// ---------------------------------------------------------------------------
static load (id: number) {
return RunnerModel.findByPk(id)
}
static loadByToken (runnerToken: string) {
const query = {
where: { runnerToken }
}
return RunnerModel.findOne(query)
}
static loadByName (name: string) {
const query = {
where: { name }
}
return RunnerModel.findOne(query)
}
static listForApi (options: {
start: number
count: number
sort: string
}) {
const { start, count, sort } = options
const query: FindOptions = {
offset: start,
limit: count,
order: getSort(sort)
}
return Promise.all([
RunnerModel.count(query),
RunnerModel.findAll<MRunner>(query)
]).then(([ total, data ]) => ({ total, data }))
}
// ---------------------------------------------------------------------------
toFormattedJSON (this: MRunner): Runner {
return {
id: this.id,
name: this.name,
description: this.description,
ip: this.ip,
lastContact: this.lastContact,
createdAt: this.createdAt,
updatedAt: this.updatedAt
}
}
}