Compare commits
2 Commits
f03f8f0362
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f983bbecf | ||
|
|
4fda9071c7 |
@@ -19,6 +19,9 @@ pub struct Model {
|
|||||||
pub username: String,
|
pub username: String,
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub password_hash: String,
|
pub password_hash: String,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
#[sea_orm(nullable)]
|
||||||
|
pub subsonic_password: Option<String>,
|
||||||
pub role: UserRole,
|
pub role: UserRole,
|
||||||
pub created_at: chrono::NaiveDateTime,
|
pub created_at: chrono::NaiveDateTime,
|
||||||
pub updated_at: chrono::NaiveDateTime,
|
pub updated_at: chrono::NaiveDateTime,
|
||||||
|
|||||||
35
src/migration/m20260320_000015_add_subsonic_password.rs
Normal file
35
src/migration/m20260320_000015_add_subsonic_password.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Users::Table)
|
||||||
|
.add_column(ColumnDef::new(Users::SubsonicPassword).text())
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Users::Table)
|
||||||
|
.drop_column(Users::SubsonicPassword)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum Users {
|
||||||
|
Table,
|
||||||
|
SubsonicPassword,
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ mod m20260319_000011_create_users;
|
|||||||
mod m20260319_000012_add_user_id_to_wanted_items;
|
mod m20260319_000012_add_user_id_to_wanted_items;
|
||||||
mod m20260320_000013_add_artist_monitoring;
|
mod m20260320_000013_add_artist_monitoring;
|
||||||
mod m20260320_000014_create_playlists;
|
mod m20260320_000014_create_playlists;
|
||||||
|
mod m20260320_000015_add_subsonic_password;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ impl MigratorTrait for Migrator {
|
|||||||
Box::new(m20260319_000012_add_user_id_to_wanted_items::Migration),
|
Box::new(m20260319_000012_add_user_id_to_wanted_items::Migration),
|
||||||
Box::new(m20260320_000013_add_artist_monitoring::Migration),
|
Box::new(m20260320_000013_add_artist_monitoring::Migration),
|
||||||
Box::new(m20260320_000014_create_playlists::Migration),
|
Box::new(m20260320_000014_create_playlists::Migration),
|
||||||
|
Box::new(m20260320_000015_add_subsonic_password::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,3 +103,76 @@ pub async fn delete(db: &DatabaseConnection, id: i32) -> DbResult<()> {
|
|||||||
Playlists::delete_by_id(id).exec(db).await?;
|
Playlists::delete_by_id(id).exec(db).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn add_track(db: &DatabaseConnection, playlist_id: i32, track_id: i32) -> DbResult<()> {
|
||||||
|
// Get max position for this playlist
|
||||||
|
let max_pos = PlaylistTracks::find()
|
||||||
|
.filter(playlist_track::Column::PlaylistId.eq(playlist_id))
|
||||||
|
.order_by_desc(playlist_track::Column::Position)
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
.map(|pt| pt.position)
|
||||||
|
.unwrap_or(-1);
|
||||||
|
|
||||||
|
let pt = TrackActiveModel {
|
||||||
|
playlist_id: Set(playlist_id),
|
||||||
|
track_id: Set(track_id),
|
||||||
|
position: Set(max_pos + 1),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
pt.insert(db).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_track(
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
playlist_id: i32,
|
||||||
|
track_id: i32,
|
||||||
|
) -> DbResult<()> {
|
||||||
|
// Delete the playlist_track row
|
||||||
|
PlaylistTracks::delete_many()
|
||||||
|
.filter(playlist_track::Column::PlaylistId.eq(playlist_id))
|
||||||
|
.filter(playlist_track::Column::TrackId.eq(track_id))
|
||||||
|
.exec(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Renumber remaining positions
|
||||||
|
let remaining = PlaylistTracks::find()
|
||||||
|
.filter(playlist_track::Column::PlaylistId.eq(playlist_id))
|
||||||
|
.order_by_asc(playlist_track::Column::Position)
|
||||||
|
.all(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
for (i, pt) in remaining.into_iter().enumerate() {
|
||||||
|
let mut active: TrackActiveModel = pt.into();
|
||||||
|
active.position = Set(i as i32);
|
||||||
|
active.update(db).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reorder_tracks(
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
playlist_id: i32,
|
||||||
|
track_ids: Vec<i32>,
|
||||||
|
) -> DbResult<()> {
|
||||||
|
// Delete all playlist_tracks for this playlist
|
||||||
|
PlaylistTracks::delete_many()
|
||||||
|
.filter(playlist_track::Column::PlaylistId.eq(playlist_id))
|
||||||
|
.exec(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Re-insert with positions matching the vec order
|
||||||
|
for (pos, track_id) in track_ids.iter().enumerate() {
|
||||||
|
let pt = TrackActiveModel {
|
||||||
|
playlist_id: Set(playlist_id),
|
||||||
|
track_id: Set(*track_id),
|
||||||
|
position: Set(pos as i32),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
pt.insert(db).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,19 @@ pub async fn count(db: &DatabaseConnection) -> DbResult<u64> {
|
|||||||
Ok(Users::find().count(db).await?)
|
Ok(Users::find().count(db).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the Subsonic password for a user. Stored as plaintext per the Subsonic protocol.
|
||||||
|
pub async fn set_subsonic_password(
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
user_id: i32,
|
||||||
|
password: &str,
|
||||||
|
) -> DbResult<User> {
|
||||||
|
let existing = get_by_id(db, user_id).await?;
|
||||||
|
let mut active: ActiveModel = existing.into();
|
||||||
|
active.subsonic_password = Set(Some(password.to_string()));
|
||||||
|
active.updated_at = Set(Utc::now().naive_utc());
|
||||||
|
Ok(active.update(db).await?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Assign all orphaned wanted items (user_id IS NULL) to the given user.
|
/// Assign all orphaned wanted items (user_id IS NULL) to the given user.
|
||||||
pub async fn adopt_orphaned_wanted_items(db: &DatabaseConnection, user_id: i32) -> DbResult<u64> {
|
pub async fn adopt_orphaned_wanted_items(db: &DatabaseConnection, user_id: i32) -> DbResult<u64> {
|
||||||
use crate::entities::wanted_item;
|
use crate::entities::wanted_item;
|
||||||
|
|||||||
Reference in New Issue
Block a user