• Create a package com.endlessuphill.regent.data.model. - Inside this package, create the following Kotlin files:

(Enums - create in a sub-package like com.endlessuphill.regent.data.model.enums)

// Role.kt
package com.endlessuphill.regent.data.model.enums
 
enum class Role {
    ADMIN, USER, VIEWER
}
// JobStatus.kt
package com.endlessuphill.regent.data.model.enums
 
enum class JobStatus {
    PENDING, // Initial state upon submission record creation
    QUEUED,  // Successfully placed in Redis queue
    STARTED, // Picked up by a dispatcher
    SUCCESS, // Executor completed successfully
    FAILURE  // Executor failed or error during dispatch
}
// ScheduleType.kt
package com.endlessuphill.regent.data.model.enums
 
enum class ScheduleType {
    ONE_OFF, RECURRING
}
// LogLevel.kt
package com.endlessuphill.regent.data.model.enums
 
enum class LogLevel {
    INFO, WARN, ERROR
}
// RuntimeType.kt
package com.endlessuphill.regent.data.model.enums
 
// We'll keep this extensible, maybe load from config later, but start with known types
enum class RuntimeType {
    HTTP, CELERY // Add DOCKER, KUBERNETES etc. later
}

(Entities - in com.endlessuphill.regent.data.model)

// User.kt
package com.endlessuphill.regent.data.model
 
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.time.Instant
import java.util.UUID
 
@Table("users") // Explicit table name
data class User(
    @Id
    val id: UUID = UUID.randomUUID(), // Let DB generate or ensure uniqueness if app generates
    val username: String, // Should be unique
    val hashedPassword: String,
    val createdAt: Instant = Instant.now()
)
// Project.kt
package com.endlessuphill.regent.data.model
 
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.time.Instant
import java.util.UUID
 
@Table("projects")
data class Project(
    @Id
    val id: UUID = UUID.randomUUID(),
    val name: String, // Should be unique
    val createdAt: Instant = Instant.now()
)
// ProjectUserRole.kt
package com.endlessuphill.regent.data.model
 
import com.endlessuphill.regent.data.model.enums.Role
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.util.UUID
 
// Using a separate ID might be simpler with R2DBC than composite keys initially
@Table("project_user_roles")
data class ProjectUserRole(
    @Id
    val id: UUID = UUID.randomUUID(), // Surrogate key
    val projectId: UUID,
    val userId: UUID,
    val role: Role
    // Consider adding unique constraint on (projectId, userId) in DB schema
)
// Job.kt
package com.endlessuphill.regent.data.model
 
import com.endlessuphill.regent.data.model.enums.JobStatus
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.time.Instant
import java.util.UUID
 
@Table("jobs")
data class Job(
    @Id
    val id: UUID = UUID.randomUUID(),
    val projectId: UUID,
    val workerDefinitionId: UUID,
    val submittedByUserId: UUID,
    val parametersJson: String, // Input parameters as JSON string
    var status: JobStatus = JobStatus.PENDING,
    var resultJson: String? = null, // Store result data as JSON string
    val createdAt: Instant = Instant.now(),
    var startedAt: Instant? = null,
    var completedAt: Instant? = null
)
 
// Consider adding unique constraint on (projectId, workerDefinitionId) in DB schema
// JobLog.kt
package com.endlessuphill.regent.data.model
 
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.time.Instant
import java.util.UUID
 
@Table("job_logs")
data class JobLog(
    @Id
    val id: UUID = UUID.randomUUID(),
    val jobId: UUID,
    val timestamp: Instant = Instant.now(),
    val message: String
)
// Schedule.kt
package com.endlessuphill.regent.data.model
 
import com.endlessuphill.regent.data.model.enums.ScheduleType
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.time.Instant
import java.util.UUID
 
@Table("schedules")
data class Schedule(
    @Id
    val id: UUID = UUID.randomUUID(),
    val projectId: UUID,
    val workerDefinitionId: UUID,
    val createdByUserId: UUID,
    val type: ScheduleType,
    val runAt: Instant? = null, // For ONE_OFF type
    val cronExpression: String? = null, // For RECURRING type
    val parametersJson: String, // Parameters to use when job is triggered
    var isEnabled: Boolean = true,
    var lastRunAt: Instant? = null,
    var nextRunAt: Instant? = null, // Calculated field for scheduler polling
    val createdAt: Instant = Instant.now(),
    var updatedAt: Instant = Instant.now()
)
// Consider adding unique constraint on (projectId, workerDefinitionId) in DB schema
// RegentLog.kt
package com.endlessuphill.regent.data.model
 
import com.endlessuphill.regent.data.model.enums.LogLevel
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.time.Instant
import java.util.UUID
 
@Table("regent_logs")
data class RegentLog(
    @Id
    val id: UUID = UUID.randomUUID(),
    val timestamp: Instant = Instant.now(),
    val level: LogLevel,
    val message: String,
    val detailsJson: String? = null // Optional structured details
)
// WorkerDefinition.kt
package com.endlessuphill.regent.data.model
 
import com.endlessuphill.regent.data.model.enums.RuntimeType
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import java.time.Instant
import java.util.UUID
 
@Table("worker_definitions")
data class WorkerDefinition(
    @Id
    val id: UUID = UUID.randomUUID(),
    val projectId: UUID,
    val name: String, // Unique within a project
    val runtimeType: RuntimeType,
    val configJson: String, // Store config payload as JSON string
    val parameterSchemaJson: String, // Store JSON schema as string
    val createdAt: Instant = Instant.now(),
    val updatedAt: Instant = Instant.now()
    // Consider adding unique constraint on (projectId, name) in DB schema
)

Next Step