module.exports = { /** * @param {QueryInterface} queryInterface * @param {Sequelize} Sequelize * @returns {Promise} */ async up(queryInterface, Sequelize) { /** * @type {Transaction} */ const transaction = await queryInterface.sequelize.transaction(); try { await queryInterface.createTable('users', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('roles', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('permissions', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('devices', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('motorcycles', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('route_collections', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('routes', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('route_points', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('rides', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('ride_trackpoints', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('groups', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('group_memberships', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('group_rides', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('live_locations', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('map_layers', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('navigation_sessions', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('shared_links', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.createTable('import_jobs', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.DataTypes.UUIDV4, primaryKey: true, }, createdById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, updatedById: { type: Sequelize.DataTypes.UUID, references: { key: 'id', model: 'users', }, }, createdAt: { type: Sequelize.DataTypes.DATE }, updatedAt: { type: Sequelize.DataTypes.DATE }, deletedAt: { type: Sequelize.DataTypes.DATE }, importHash: { type: Sequelize.DataTypes.STRING(255), allowNull: true, unique: true, }, }, { transaction }); await queryInterface.addColumn( 'users', 'firstName', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'lastName', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'phoneNumber', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'email', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'disabled', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'users', 'password', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'emailVerified', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'users', 'emailVerificationToken', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'emailVerificationTokenExpiresAt', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'users', 'passwordResetToken', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'passwordResetTokenExpiresAt', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'users', 'provider', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'users', 'app_roleId', { type: Sequelize.DataTypes.UUID, references: { model: 'roles', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'roles', 'name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'roles', 'role_customization', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'permissions', 'name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'devices', 'ownerId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'devices', 'device_type', { type: Sequelize.DataTypes.ENUM, values: ['phone','tablet','gps_unit','browser'], }, { transaction } ); await queryInterface.addColumn( 'devices', 'device_name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'devices', 'platform', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'devices', 'os_version', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'devices', 'app_version', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'devices', 'push_token', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'devices', 'last_seen_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'devices', 'is_active', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'ownerId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'nickname', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'make', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'model', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'year', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'engine_cc', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'category', { type: Sequelize.DataTypes.ENUM, values: ['sport','touring','adventure','cruiser','naked','scooter','other'], }, { transaction } ); await queryInterface.addColumn( 'motorcycles', 'is_primary', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'route_collections', 'ownerId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'route_collections', 'name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'route_collections', 'description', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'route_collections', 'is_public', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'route_collections', 'visibility', { type: Sequelize.DataTypes.ENUM, values: ['private','unlisted','public'], }, { transaction } ); await queryInterface.addColumn( 'routes', 'ownerId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'routes', 'collectionId', { type: Sequelize.DataTypes.UUID, references: { model: 'route_collections', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'routes', 'name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'routes', 'description', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'routes', 'route_type', { type: Sequelize.DataTypes.ENUM, values: ['planned','imported','recorded'], }, { transaction } ); await queryInterface.addColumn( 'routes', 'surface_preference', { type: Sequelize.DataTypes.ENUM, values: ['any','paved','mixed','gravel'], }, { transaction } ); await queryInterface.addColumn( 'routes', 'avoid_highways', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'routes', 'avoid_tolls', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'routes', 'avoid_ferries', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'routes', 'distance_km', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'routes', 'ascent_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'routes', 'descent_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'routes', 'estimated_duration_sec', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'routes', 'privacy', { type: Sequelize.DataTypes.ENUM, values: ['private','unlisted','public'], }, { transaction } ); await queryInterface.addColumn( 'routes', 'imported_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'routeId', { type: Sequelize.DataTypes.UUID, references: { model: 'routes', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'sequence', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'point_type', { type: Sequelize.DataTypes.ENUM, values: ['trackpoint','waypoint','via','stop','poi'], }, { transaction } ); await queryInterface.addColumn( 'route_points', 'latitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'longitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'elevation_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'timestamp', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'label', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'route_points', 'note', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'rides', 'riderId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'rides', 'motorcycleId', { type: Sequelize.DataTypes.UUID, references: { model: 'motorcycles', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'rides', 'planned_routeId', { type: Sequelize.DataTypes.UUID, references: { model: 'routes', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'rides', 'title', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'rides', 'status', { type: Sequelize.DataTypes.ENUM, values: ['planned','in_progress','paused','completed','aborted'], }, { transaction } ); await queryInterface.addColumn( 'rides', 'started_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'rides', 'ended_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'rides', 'distance_km', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'rides', 'moving_time_sec', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'rides', 'elapsed_time_sec', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'rides', 'max_speed_kmh', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'rides', 'avg_speed_kmh', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'rides', 'ascent_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'rides', 'descent_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'rides', 'map_view_mode', { type: Sequelize.DataTypes.ENUM, values: ['map_2d','map_3d'], }, { transaction } ); await queryInterface.addColumn( 'rides', 'is_live_shared', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'rides', 'notes', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'rideId', { type: Sequelize.DataTypes.UUID, references: { model: 'rides', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'sequence', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'recorded_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'latitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'longitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'elevation_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'speed_kmh', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'heading_deg', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'accuracy_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'ride_trackpoints', 'battery_percent', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'groups', 'ownerId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'groups', 'name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'groups', 'description', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'groups', 'visibility', { type: Sequelize.DataTypes.ENUM, values: ['private','public'], }, { transaction } ); await queryInterface.addColumn( 'groups', 'requires_approval', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'group_memberships', 'groupId', { type: Sequelize.DataTypes.UUID, references: { model: 'groups', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'group_memberships', 'memberId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'group_memberships', 'membership_role', { type: Sequelize.DataTypes.ENUM, values: ['member','admin'], }, { transaction } ); await queryInterface.addColumn( 'group_memberships', 'status', { type: Sequelize.DataTypes.ENUM, values: ['invited','requested','active','blocked'], }, { transaction } ); await queryInterface.addColumn( 'group_memberships', 'joined_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'groupId', { type: Sequelize.DataTypes.UUID, references: { model: 'groups', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'organizerId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'routeId', { type: Sequelize.DataTypes.UUID, references: { model: 'routes', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'title', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'meeting_point_name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'meeting_latitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'meeting_longitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'meet_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'start_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'end_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'status', { type: Sequelize.DataTypes.ENUM, values: ['draft','scheduled','in_progress','completed','cancelled'], }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'live_tracking_enabled', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'group_rides', 'notes', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'group_rideId', { type: Sequelize.DataTypes.UUID, references: { model: 'group_rides', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'userId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'deviceId', { type: Sequelize.DataTypes.UUID, references: { model: 'devices', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'captured_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'latitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'longitude', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'elevation_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'speed_kmh', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'heading_deg', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'live_locations', 'accuracy_m', { type: Sequelize.DataTypes.DECIMAL, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'name', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'layer_type', { type: Sequelize.DataTypes.ENUM, values: ['raster','vector','terrain','satellite'], }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'provider', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'tile_url_template', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'min_zoom', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'max_zoom', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'supports_3d', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'is_default', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'map_layers', 'attribution', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'userId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'deviceId', { type: Sequelize.DataTypes.UUID, references: { model: 'devices', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'routeId', { type: Sequelize.DataTypes.UUID, references: { model: 'routes', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'status', { type: Sequelize.DataTypes.ENUM, values: ['active','paused','ended'], }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'started_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'ended_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'guidance_mode', { type: Sequelize.DataTypes.ENUM, values: ['route_following','free_ride'], }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'map_view_mode', { type: Sequelize.DataTypes.ENUM, values: ['map_2d','map_3d'], }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'voice_guidance_enabled', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'navigation_sessions', 'reroute_enabled', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'shared_links', 'ownerId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'shared_links', 'resource_type', { type: Sequelize.DataTypes.ENUM, values: ['route','ride','group_ride'], }, { transaction } ); await queryInterface.addColumn( 'shared_links', 'token', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await queryInterface.addColumn( 'shared_links', 'expires_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'shared_links', 'is_revoked', { type: Sequelize.DataTypes.BOOLEAN, defaultValue: false, allowNull: false, }, { transaction } ); await queryInterface.addColumn( 'shared_links', 'max_views', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'shared_links', 'view_count', { type: Sequelize.DataTypes.INTEGER, }, { transaction } ); await queryInterface.addColumn( 'import_jobs', 'requested_byId', { type: Sequelize.DataTypes.UUID, references: { model: 'users', key: 'id', }, }, { transaction } ); await queryInterface.addColumn( 'import_jobs', 'import_type', { type: Sequelize.DataTypes.ENUM, values: ['gpx_route','gpx_track'], }, { transaction } ); await queryInterface.addColumn( 'import_jobs', 'status', { type: Sequelize.DataTypes.ENUM, values: ['queued','processing','completed','failed'], }, { transaction } ); await queryInterface.addColumn( 'import_jobs', 'queued_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'import_jobs', 'completed_at', { type: Sequelize.DataTypes.DATE, }, { transaction } ); await queryInterface.addColumn( 'import_jobs', 'error_message', { type: Sequelize.DataTypes.TEXT, }, { transaction } ); await transaction.commit(); } catch (err) { await transaction.rollback(); throw err; } }, /** * @param {QueryInterface} queryInterface * @param {Sequelize} Sequelize * @returns {Promise} */ async down(queryInterface, Sequelize) { /** * @type {Transaction} */ const transaction = await queryInterface.sequelize.transaction(); try { await queryInterface.removeColumn( 'import_jobs', 'error_message', { transaction } ); await queryInterface.removeColumn( 'import_jobs', 'completed_at', { transaction } ); await queryInterface.removeColumn( 'import_jobs', 'queued_at', { transaction } ); await queryInterface.removeColumn( 'import_jobs', 'status', { transaction } ); await queryInterface.removeColumn( 'import_jobs', 'import_type', { transaction } ); await queryInterface.removeColumn( 'import_jobs', 'requested_byId', { transaction } ); await queryInterface.removeColumn( 'shared_links', 'view_count', { transaction } ); await queryInterface.removeColumn( 'shared_links', 'max_views', { transaction } ); await queryInterface.removeColumn( 'shared_links', 'is_revoked', { transaction } ); await queryInterface.removeColumn( 'shared_links', 'expires_at', { transaction } ); await queryInterface.removeColumn( 'shared_links', 'token', { transaction } ); await queryInterface.removeColumn( 'shared_links', 'resource_type', { transaction } ); await queryInterface.removeColumn( 'shared_links', 'ownerId', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'reroute_enabled', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'voice_guidance_enabled', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'map_view_mode', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'guidance_mode', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'ended_at', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'started_at', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'status', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'routeId', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'deviceId', { transaction } ); await queryInterface.removeColumn( 'navigation_sessions', 'userId', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'attribution', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'is_default', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'supports_3d', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'max_zoom', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'min_zoom', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'tile_url_template', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'provider', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'layer_type', { transaction } ); await queryInterface.removeColumn( 'map_layers', 'name', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'accuracy_m', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'heading_deg', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'speed_kmh', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'elevation_m', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'longitude', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'latitude', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'captured_at', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'deviceId', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'userId', { transaction } ); await queryInterface.removeColumn( 'live_locations', 'group_rideId', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'notes', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'live_tracking_enabled', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'status', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'end_at', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'start_at', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'meet_at', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'meeting_longitude', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'meeting_latitude', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'meeting_point_name', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'title', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'routeId', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'organizerId', { transaction } ); await queryInterface.removeColumn( 'group_rides', 'groupId', { transaction } ); await queryInterface.removeColumn( 'group_memberships', 'joined_at', { transaction } ); await queryInterface.removeColumn( 'group_memberships', 'status', { transaction } ); await queryInterface.removeColumn( 'group_memberships', 'membership_role', { transaction } ); await queryInterface.removeColumn( 'group_memberships', 'memberId', { transaction } ); await queryInterface.removeColumn( 'group_memberships', 'groupId', { transaction } ); await queryInterface.removeColumn( 'groups', 'requires_approval', { transaction } ); await queryInterface.removeColumn( 'groups', 'visibility', { transaction } ); await queryInterface.removeColumn( 'groups', 'description', { transaction } ); await queryInterface.removeColumn( 'groups', 'name', { transaction } ); await queryInterface.removeColumn( 'groups', 'ownerId', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'battery_percent', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'accuracy_m', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'heading_deg', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'speed_kmh', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'elevation_m', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'longitude', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'latitude', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'recorded_at', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'sequence', { transaction } ); await queryInterface.removeColumn( 'ride_trackpoints', 'rideId', { transaction } ); await queryInterface.removeColumn( 'rides', 'notes', { transaction } ); await queryInterface.removeColumn( 'rides', 'is_live_shared', { transaction } ); await queryInterface.removeColumn( 'rides', 'map_view_mode', { transaction } ); await queryInterface.removeColumn( 'rides', 'descent_m', { transaction } ); await queryInterface.removeColumn( 'rides', 'ascent_m', { transaction } ); await queryInterface.removeColumn( 'rides', 'avg_speed_kmh', { transaction } ); await queryInterface.removeColumn( 'rides', 'max_speed_kmh', { transaction } ); await queryInterface.removeColumn( 'rides', 'elapsed_time_sec', { transaction } ); await queryInterface.removeColumn( 'rides', 'moving_time_sec', { transaction } ); await queryInterface.removeColumn( 'rides', 'distance_km', { transaction } ); await queryInterface.removeColumn( 'rides', 'ended_at', { transaction } ); await queryInterface.removeColumn( 'rides', 'started_at', { transaction } ); await queryInterface.removeColumn( 'rides', 'status', { transaction } ); await queryInterface.removeColumn( 'rides', 'title', { transaction } ); await queryInterface.removeColumn( 'rides', 'planned_routeId', { transaction } ); await queryInterface.removeColumn( 'rides', 'motorcycleId', { transaction } ); await queryInterface.removeColumn( 'rides', 'riderId', { transaction } ); await queryInterface.removeColumn( 'route_points', 'note', { transaction } ); await queryInterface.removeColumn( 'route_points', 'label', { transaction } ); await queryInterface.removeColumn( 'route_points', 'timestamp', { transaction } ); await queryInterface.removeColumn( 'route_points', 'elevation_m', { transaction } ); await queryInterface.removeColumn( 'route_points', 'longitude', { transaction } ); await queryInterface.removeColumn( 'route_points', 'latitude', { transaction } ); await queryInterface.removeColumn( 'route_points', 'point_type', { transaction } ); await queryInterface.removeColumn( 'route_points', 'sequence', { transaction } ); await queryInterface.removeColumn( 'route_points', 'routeId', { transaction } ); await queryInterface.removeColumn( 'routes', 'imported_at', { transaction } ); await queryInterface.removeColumn( 'routes', 'privacy', { transaction } ); await queryInterface.removeColumn( 'routes', 'estimated_duration_sec', { transaction } ); await queryInterface.removeColumn( 'routes', 'descent_m', { transaction } ); await queryInterface.removeColumn( 'routes', 'ascent_m', { transaction } ); await queryInterface.removeColumn( 'routes', 'distance_km', { transaction } ); await queryInterface.removeColumn( 'routes', 'avoid_ferries', { transaction } ); await queryInterface.removeColumn( 'routes', 'avoid_tolls', { transaction } ); await queryInterface.removeColumn( 'routes', 'avoid_highways', { transaction } ); await queryInterface.removeColumn( 'routes', 'surface_preference', { transaction } ); await queryInterface.removeColumn( 'routes', 'route_type', { transaction } ); await queryInterface.removeColumn( 'routes', 'description', { transaction } ); await queryInterface.removeColumn( 'routes', 'name', { transaction } ); await queryInterface.removeColumn( 'routes', 'collectionId', { transaction } ); await queryInterface.removeColumn( 'routes', 'ownerId', { transaction } ); await queryInterface.removeColumn( 'route_collections', 'visibility', { transaction } ); await queryInterface.removeColumn( 'route_collections', 'is_public', { transaction } ); await queryInterface.removeColumn( 'route_collections', 'description', { transaction } ); await queryInterface.removeColumn( 'route_collections', 'name', { transaction } ); await queryInterface.removeColumn( 'route_collections', 'ownerId', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'is_primary', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'category', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'engine_cc', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'year', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'model', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'make', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'nickname', { transaction } ); await queryInterface.removeColumn( 'motorcycles', 'ownerId', { transaction } ); await queryInterface.removeColumn( 'devices', 'is_active', { transaction } ); await queryInterface.removeColumn( 'devices', 'last_seen_at', { transaction } ); await queryInterface.removeColumn( 'devices', 'push_token', { transaction } ); await queryInterface.removeColumn( 'devices', 'app_version', { transaction } ); await queryInterface.removeColumn( 'devices', 'os_version', { transaction } ); await queryInterface.removeColumn( 'devices', 'platform', { transaction } ); await queryInterface.removeColumn( 'devices', 'device_name', { transaction } ); await queryInterface.removeColumn( 'devices', 'device_type', { transaction } ); await queryInterface.removeColumn( 'devices', 'ownerId', { transaction } ); await queryInterface.removeColumn( 'permissions', 'name', { transaction } ); await queryInterface.removeColumn( 'roles', 'role_customization', { transaction } ); await queryInterface.removeColumn( 'roles', 'name', { transaction } ); await queryInterface.removeColumn( 'users', 'app_roleId', { transaction } ); await queryInterface.removeColumn( 'users', 'provider', { transaction } ); await queryInterface.removeColumn( 'users', 'passwordResetTokenExpiresAt', { transaction } ); await queryInterface.removeColumn( 'users', 'passwordResetToken', { transaction } ); await queryInterface.removeColumn( 'users', 'emailVerificationTokenExpiresAt', { transaction } ); await queryInterface.removeColumn( 'users', 'emailVerificationToken', { transaction } ); await queryInterface.removeColumn( 'users', 'emailVerified', { transaction } ); await queryInterface.removeColumn( 'users', 'password', { transaction } ); await queryInterface.removeColumn( 'users', 'disabled', { transaction } ); await queryInterface.removeColumn( 'users', 'email', { transaction } ); await queryInterface.removeColumn( 'users', 'phoneNumber', { transaction } ); await queryInterface.removeColumn( 'users', 'lastName', { transaction } ); await queryInterface.removeColumn( 'users', 'firstName', { transaction } ); await queryInterface.dropTable('import_jobs', { transaction }); await queryInterface.dropTable('shared_links', { transaction }); await queryInterface.dropTable('navigation_sessions', { transaction }); await queryInterface.dropTable('map_layers', { transaction }); await queryInterface.dropTable('live_locations', { transaction }); await queryInterface.dropTable('group_rides', { transaction }); await queryInterface.dropTable('group_memberships', { transaction }); await queryInterface.dropTable('groups', { transaction }); await queryInterface.dropTable('ride_trackpoints', { transaction }); await queryInterface.dropTable('rides', { transaction }); await queryInterface.dropTable('route_points', { transaction }); await queryInterface.dropTable('routes', { transaction }); await queryInterface.dropTable('route_collections', { transaction }); await queryInterface.dropTable('motorcycles', { transaction }); await queryInterface.dropTable('devices', { transaction }); await queryInterface.dropTable('permissions', { transaction }); await queryInterface.dropTable('roles', { transaction }); await queryInterface.dropTable('users', { transaction }); await transaction.commit(); } catch (err) { await transaction.rollback(); throw err; } } };