Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
88 lines
5 KiB
JavaScript
88 lines
5 KiB
JavaScript
// backend/database/migrations/knex/043_create_fitness_tables.js
|
|
// ADR-025: Fitness Module — exercises, workouts, sets
|
|
export async function up(knex) {
|
|
// fitness_exercises
|
|
await knex.schema.createTable('fitness_exercises', (table) => {
|
|
table.increments('id').primary();
|
|
table.integer('space_id').references('id').inTable('spaces');
|
|
table.string('name', 255).notNullable();
|
|
table.string('equipment', 100);
|
|
table.string('primary_muscle', 100);
|
|
table.string('secondary_muscle', 100);
|
|
table.string('category', 50);
|
|
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
});
|
|
|
|
// fitness_workouts
|
|
await knex.schema.createTable('fitness_workouts', (table) => {
|
|
table.increments('id').primary();
|
|
table.integer('space_id').references('id').inTable('spaces').notNullable();
|
|
table.string('title', 255);
|
|
table.text('description');
|
|
table.timestamp('started_at').notNullable();
|
|
table.timestamp('ended_at');
|
|
table.text('notes');
|
|
table.string('source', 50).defaultTo('manual');
|
|
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
table.timestamp('updated_at').defaultTo(knex.fn.now());
|
|
|
|
table.index('space_id', 'idx_fitness_workouts_space');
|
|
table.index(['space_id', 'started_at'], 'idx_fitness_workouts_date');
|
|
});
|
|
|
|
// fitness_sets
|
|
await knex.schema.createTable('fitness_sets', (table) => {
|
|
table.increments('id').primary();
|
|
table.integer('workout_id').references('id').inTable('fitness_workouts').onDelete('CASCADE');
|
|
table.integer('exercise_id').references('id').inTable('fitness_exercises');
|
|
table.string('exercise_name', 255);
|
|
table.integer('set_index').notNullable();
|
|
table.string('set_type', 50).defaultTo('normal');
|
|
table.decimal('weight_kg', 7, 2);
|
|
table.integer('reps');
|
|
table.decimal('rpe', 3, 1);
|
|
table.decimal('distance_km', 7, 3);
|
|
table.integer('duration_seconds');
|
|
table.boolean('is_pr').defaultTo(false);
|
|
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
|
|
table.index('workout_id', 'idx_fitness_sets_workout');
|
|
table.index('exercise_name', 'idx_fitness_sets_exercise');
|
|
});
|
|
|
|
// Partial index for PRs
|
|
await knex.raw('CREATE INDEX IF NOT EXISTS idx_fitness_sets_pr ON fitness_sets(is_pr) WHERE is_pr = true');
|
|
|
|
// Unique constraint for exercises per space
|
|
await knex.raw('CREATE UNIQUE INDEX IF NOT EXISTS idx_fitness_exercises_unique ON fitness_exercises(space_id, name) WHERE space_id IS NOT NULL');
|
|
|
|
// Seed global exercises
|
|
await knex('fitness_exercises').insert([
|
|
{ space_id: null, name: 'Bench Press', equipment: 'barbell', primary_muscle: 'Chest', category: 'compound' },
|
|
{ space_id: null, name: 'Squat', equipment: 'barbell', primary_muscle: 'Quads', category: 'compound' },
|
|
{ space_id: null, name: 'Deadlift', equipment: 'barbell', primary_muscle: 'Back', category: 'compound' },
|
|
{ space_id: null, name: 'Overhead Press', equipment: 'barbell', primary_muscle: 'Shoulders', category: 'compound' },
|
|
{ space_id: null, name: 'Barbell Row', equipment: 'barbell', primary_muscle: 'Back', category: 'compound' },
|
|
{ space_id: null, name: 'Pull-up', equipment: 'bodyweight', primary_muscle: 'Back', category: 'compound' },
|
|
{ space_id: null, name: 'Dip', equipment: 'bodyweight', primary_muscle: 'Chest', category: 'compound' },
|
|
{ space_id: null, name: 'Bicep Curl', equipment: 'dumbbell', primary_muscle: 'Biceps', category: 'isolation' },
|
|
{ space_id: null, name: 'Tricep Extension', equipment: 'cable', primary_muscle: 'Triceps', category: 'isolation' },
|
|
{ space_id: null, name: 'Leg Press', equipment: 'machine', primary_muscle: 'Quads', category: 'compound' },
|
|
{ space_id: null, name: 'Lat Pulldown', equipment: 'cable', primary_muscle: 'Back', category: 'compound' },
|
|
{ space_id: null, name: 'Leg Curl', equipment: 'machine', primary_muscle: 'Hamstrings', category: 'isolation' },
|
|
{ space_id: null, name: 'Leg Extension', equipment: 'machine', primary_muscle: 'Quads', category: 'isolation' },
|
|
{ space_id: null, name: 'Lateral Raise', equipment: 'dumbbell', primary_muscle: 'Shoulders', category: 'isolation' },
|
|
{ space_id: null, name: 'Face Pull', equipment: 'cable', primary_muscle: 'Shoulders', category: 'isolation' },
|
|
{ space_id: null, name: 'Calf Raise', equipment: 'machine', primary_muscle: 'Calves', category: 'isolation' },
|
|
{ space_id: null, name: 'Plank', equipment: 'bodyweight', primary_muscle: 'Core', category: 'isolation' },
|
|
{ space_id: null, name: 'Russian Twist', equipment: 'bodyweight', primary_muscle: 'Core', category: 'isolation' },
|
|
{ space_id: null, name: 'Incline Bench Press', equipment: 'barbell', primary_muscle: 'Chest', category: 'compound' },
|
|
{ space_id: null, name: 'Romanian Deadlift', equipment: 'barbell', primary_muscle: 'Hamstrings', category: 'compound' },
|
|
]);
|
|
}
|
|
|
|
export async function down(knex) {
|
|
await knex.schema.dropTableIfExists('fitness_sets');
|
|
await knex.schema.dropTableIfExists('fitness_workouts');
|
|
await knex.schema.dropTableIfExists('fitness_exercises');
|
|
}
|