Add backend

This commit is contained in:
2023-09-23 18:23:46 +02:00
parent a894717e52
commit 95dbb74b47
45 changed files with 6831 additions and 28 deletions

1
backend/.env.example Normal file
View File

@ -0,0 +1 @@
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres

2014
backend/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

17
backend/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "singit_srv"
version = "0.1.0"
authors = ["Joakim Hulthe <joakim@hulthe.net"]
edition = "2021"
[dependencies]
actix-files = "0.6.2"
actix-web = { version = "4.4.0", default-features = false, features = ["macros"] }
clap = { version = "4.4.4", features = ["derive", "env"] }
diesel = "2.1.1"
diesel-async = { version = "0.4.1", features = ["postgres", "deadpool"] }
dotenv = "0.15.0"
env_logger = "0.10.0"
eyre = "0.6.8"
log = "0.4.20"
serde = { version = "1.0.188", default-features = false, features = ["derive", "std"] }

9
backend/diesel.toml Normal file
View File

@ -0,0 +1,9 @@
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]
[migrations_directory]
dir = "migrations"

1
backend/dist Symbolic link
View File

@ -0,0 +1 @@
../frontend/dist

0
backend/migrations/.keep Normal file
View File

View File

@ -0,0 +1,6 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at();

View File

@ -0,0 +1,36 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
-- Sets up a trigger for the given table to automatically set a column called
-- `updated_at` whenever the row is modified (unless `updated_at` was included
-- in the modified columns)
--
-- # Example
--
-- ```sql
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
--
-- SELECT diesel_manage_updated_at('users');
-- ```
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
BEGIN
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
BEGIN
IF (
NEW IS DISTINCT FROM OLD AND
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
) THEN
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

View File

@ -0,0 +1,2 @@
DROP TABLE song;

View File

@ -0,0 +1,13 @@
CREATE TABLE song(
song_hash TEXT NOT NULL PRIMARY KEY,
title TEXT NOT NULL,
artist TEXT NOT NULL,
cover TEXT,
language TEXT,
video TEXT,
year TEXT,
genre TEXT,
bpm TEXT NOT NULL,
duet_singer_1 TEXT,
duet_singer_2 TEXT
);

View File

@ -0,0 +1,2 @@
DROP TABLE custom_list_entry;
DROP TABLE custom_list;

View File

@ -0,0 +1,10 @@
CREATE TABLE custom_list (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL
);
CREATE TABLE custom_list_entry (
list_id SERIAL REFERENCES custom_list(id),
song_hash TEXT NOT NULL REFERENCES song(song_hash),
PRIMARY KEY (list_id, song_hash)
);

View File

@ -0,0 +1,253 @@
insert into custom_list(id, name) VALUES (1, 'tux');
insert into custom_list_entry(list_id, song_hash) VALUES
(1, '415e7f2a9ca15306f462493dea011328'),
(1, 'c2ada59d4465e891565ed6480c95402b'),
(1, 'b64e1a6f02bc3dbc9cf42ec51465f8a4'),
(1, '61611e2ae7da9f5fa307c677aa768749'),
(1, '331b7741323bb299558d2889662bc90c'),
(1, '83c8eb8a644c01085406611a54c746b3'),
(1, '5305707bae7a4072d6f72d2c83c6fe19'),
(1, '2a405748c627c30bfdf029361d09618c'),
(1, '24bf0d992eb9bfe83f0355574e52cd66'),
(1, '128d3a0b6109b61e7c5266942e04e45a'),
(1, '4cda857d6a4d1365a98f0f4b0f6292da'),
(1, '8fd5f78019ef7cdfa595d5c51a147ba7'),
(1, '138a95285762edcfb7916b9aef329083'),
(1, '8e9c0d935b93569858817a4ae9513875'),
(1, 'b865f24ced688b6932c6131d644f7975'),
(1, 'f83893a4322289818328b42468728314'),
(1, '92d04dbea98658cd07d9b2c29356ab52'),
(1, '9e2975f1cb67bd0ff76c83e9d2b3c7a4'),
(1, 'cf9eaf021b3d78cd2491be771abbe13b'),
(1, '3a04e1eec93c224af777c7f951dd2d98'),
(1, 'b20b9c8f2bdb43086ec9d46b9a6bada0'),
(1, '85ad23ce6d40d27656a9cbd54e3f6ab4'),
(1, 'ab32d01ba3d2a557c51c7b26f08f61f0'),
(1, '989118dd28898b84962858ec44e2f7e2'),
(1, 'bf7a666e3d37a1d7388ed838bb6f020a'),
(1, 'd30cf94ad68c50f660a31bcf637c76bd'),
(1, '053161d0a98c27ea4a9ee5e0e404e848'),
(1, 'c9cf0764a339f79b783087db605fe8f0'),
(1, 'd5c4c3513cb8869643f42e1350474ad3'),
(1, '0a8a99dc8ffafaf837f0755fa2e54009'),
(1, 'fcea59940c64e1257ce9264254ba8e03'),
(1, '63c18435bbeef45e6fafc947ca5b1ff5'),
(1, '6620e5b50f4f1ee7358bbe0d609edfe2'),
(1, '265a22dc97ee56e2e1a4ce525f3e79c2'),
(1, '9adb1f7ec660e0f2643cd151fad69b82'),
(1, '6443a7047288aab14ee4694ebe87b79b'),
(1, '80a356c273b7d52049a23e91e36aa9f0'),
(1, '0f73fa30f7f20d5235dcac8eabff472d'),
(1, '5a4b69e047a949744a81715a83f20ee3'),
(1, '3407c6f6ee7b1970c094b0c3144bf54a'),
(1, '9a4fbaee92151f345680abe2fc9ee747'),
(1, '1a049f2fe3a59d840c5157600c8743b5'),
(1, 'd6f4b7dd36e9ddb4e654d2d6836ff15e'),
(1, 'be090ec6b8d3dcd206c29c2620b673d6'),
(1, '2de6e630dc402b3f1dd565e25376fa6e'),
(1, '8527cc85dfb71405e0c509d60d530fa1'),
(1, '495b964e442823eb695e6e73d59ed100'),
(1, '09bfeb3ac6fc284b0ef8ab47c055e2b0'),
(1, 'a5f09a3e3aae08d5576d8c24656ea1da'),
(1, 'e7110b6577a733df0ef96559c72fdc3a'),
(1, '61a8dacb968a51d010af72cd9ba0fcc7'),
(1, '126c7aa03977ba6179c14d1286d95987'),
(1, '7bafa94e8b192112fcc13285733fed21'),
(1, '55efe418039d6b4c1a0a3033a505a6d5'),
(1, 'd6cd321817e8d2ed7ebb2a7282d7ba08'),
(1, '56f7f540f406a8f4feabdc976ffc02a5'),
(1, '5bd19f7edc9a5aceb165bf982777fbef'),
(1, 'e8f3382284dd8f3c14f74e543cbe1365'),
(1, '8b1bef88f60a945f23695a98ab461a32'),
(1, '34340558fe5254ec8d632b28412c1df6'),
(1, '133c04455080e1e700ea9168eb6e9154'),
(1, '40b939fea1cc7bca69f94ba98a9a8966'),
(1, '17110ec3c9490c2d7212bac578178f82'),
(1, '4013a6b73babbe4ddb7c94308f607981'),
(1, '606aa3c2fd587ba49e06e2ba96634d3e'),
(1, 'f3ddf171f30f886bb65f2eb9b5e9b631'),
(1, 'b441c2178c3ea7b470905972a8542a45'),
(1, '8c0bcc859b936e07125a8a1496fc5cff'),
(1, '37903e02cd6f096b7c18130ef322f245'),
(1, '246a6ba918b31591b8a3bb39e89e57ca'),
(1, 'df864a07605f68f58d454991035887ad'),
(1, '4407108e5fcce7999b4ed6a02235829e'),
(1, '8504e0862ec12cca41caa1f78681ec03'),
(1, '75a3f6fbf821d473bf17025e043a5964'),
(1, '0bc6e109374f04db38ec70d292371d19'),
(1, '5fb96e336c571545a9e3a1afa0ce25c2'),
(1, 'def1a11db7a5899c9b0ba357fc71a37f'),
(1, '581e463cee62d8adafd84b13deeb507d'),
(1, '503a857d480a4ee4c30b132b80e895d1'),
(1, 'cf60d6a32e7a1256f1e69b518fe158ff'),
(1, '980817c5a574eb50f3ab26f390b84b4f'),
(1, '2aa8f43167062239fa955a6fd5bf209b'),
(1, '16318fbae71adefaf80991849a181d11'),
(1, '43fd446c68528a56931f755525b8bafc'),
(1, 'abf514722acd55a48f5c1ec8badd899d'),
(1, 'fb3dd61449de05c2b84d3d75cf346d81'),
(1, '55a069e51045adab0adf4d81ca36011e'),
(1, '5694db971c54e0872defef4a300343f9'),
(1, 'd67f527b382394095a3ca40ac8449904'),
(1, 'db52ed089f2008181515e3f8be54bf90'),
(1, 'f2fffd34cef9a1878754640688487c82'),
(1, 'd6ddd5009d865979febbfb36bc2d99b7'),
(1, '362c2a15b7e8aaf8f1dfefd717fe7e29'),
(1, 'b2c13e3d09706d5fde0243a44058f81c'),
(1, '37d139647cca8827ad1205695a3daa33'),
(1, 'c5732364b28b208d22e0f4cab71b5907'),
(1, '236197f68795dea908d97fa61d8bb1c5'),
(1, 'f54e7bc914d7c5fab66b371399acfd41'),
(1, 'c2563f5a17677676ec45a0d487a2a871'),
(1, '08f30c15ce2135cdf575e71aa9fadee8'),
(1, 'ebd1f487cdb42b7d444b23c68a79918b'),
(1, 'ae2739faa6e4b4c9de6962071be9fec6'),
(1, '29c04079f0a8cdd22d95ff605847ccce'),
(1, '3a0ffffb65779005d55f8d8f3d0a7091'),
(1, '14c06f589bb0a772387888641b52454b'),
(1, 'e0f3f3e0afff4e71d8a2e3a959a8effa'),
(1, '2b843c3751dde9cfadfd392cac900487'),
(1, '8c876d8344b468cfb89a5cc91c4a5218'),
(1, '15917f7b815dce661db415ee1c488813'),
(1, '54fb8b43f54e36e9a6a546d3c45aa4cf'),
(1, 'a9430cf8d75c891c6c27ca6750faee8e'),
(1, 'eb50f5eee7f570e976ab423fb44edde9'),
(1, 'ed48d149f7ffa48d8c7eed5270f48949'),
(1, '6fa37a33a1baea4875c9e342f8091dc3'),
(1, '9ce446680bea61864df9d9fec5d54a93'),
(1, 'f7ea025406d2645f86da52dde64807d0'),
(1, '114fb59b3fee8ebf8c36fd2bf7e59a55'),
(1, 'c1729df7dc9452a496c7be7f043ce88d'),
(1, '92db823664bfa36f7fdde309fe69eb26'),
(1, 'df833bc0ae6fd919e0db280a8f0f5b9e'),
(1, '7d88370d2be9063ef347351781e3ad38'),
(1, '1dbcab635439ff5d3432407fedb94da1'),
(1, 'c79b580b5ba99bdf4669a61379747740'),
(1, 'ce3722d28640ab845396d37933c66b1a'),
(1, 'dd55c007590f65a011febd5c5ec17262'),
(1, 'bb51d1b9fb2ca91fab7e63066f797c97'),
(1, '597e0dc97c39604cbac73a887f89bf94'),
(1, '2ed7517269cece052f7cbe4d8ec6f50c'),
(1, '9c4adad2919051e6fe586354b9af132b'),
(1, '2f9cc67c261ee31a0ecdd154168a987a'),
(1, 'bb1a82b02f0de5a90fcb44edd2cccdf9'),
(1, '9ec7df7febfce66dc43941e2c259904b'),
(1, '1fd38f24222630b30cca1978e2acdb85'),
(1, '9d051739db959a5dfb5b98890b4e6ae6'),
(1, '9ae26319e307759e6e27c85ddff8f5fa'),
(1, '3e3ac2c042b18fcc7f9610f654778740'),
(1, '92aeb6900e0e06603dd846889c5d8ded'),
(1, '096d2a19ddf0911a66c4fe4b41f992c7'),
(1, 'cde2d982ab2caee5f0715c341bcae991'),
(1, 'aa545b027a6fa39e5c278fc366ad9016'),
(1, '1c5b8e85a66a415e39d996b967f92e67'),
(1, '1b4673e2376ecfa02bae442cdc5b3c90'),
(1, '9e542ff1cb9fb17f5f7a60af1fe9e66b'),
(1, '8aa0b348759b2c56e17074b65a50f04a'),
(1, '4931f242aeb4b1b641d813c6a2fbfadd'),
(1, '2e3cfa5057486d0759cee8ee477a1678'),
(1, '40444256345769c7a911e8710f0f5733'),
(1, '54f85dd1796f5a8f5797c3965e8736f4'),
(1, 'd8b2250234456105c3f1452afcffbfd5'),
(1, '038e1f5299c2e7c4c87b54f655429666'),
(1, '2ba74b69dc4671aa0fa705306c7cf072'),
(1, 'fef173c794fec61b65089321673d0370'),
(1, '366eff873b9665852f08b3d134dfd87c'),
(1, '1bdc4632029c88612f481ab75ebc4062'),
(1, 'b3acb1a516ee6949dd0f764200e80287'),
(1, '00d59e67b764386960d8b423567cf9bc'),
(1, 'b78fa5394c788492c5ddcfdbdfe4ae15'),
(1, 'e8a7d9cb2aca242bf3f0c59640ddd620'),
(1, '6059a22be14860cff767d679c98ed1df'),
(1, 'b23ce25c9b6d789953b43d6a7f6f9266'),
(1, '0499534b7a0545e81354fd6e814f4ad3'),
(1, '77ea72cb592979d8aa617f1a06181600'),
(1, '482de10e7a8d0688dd9ac27eef4cddb8'),
(1, '570b9443265e8614befdb3c84bf66095'),
(1, '5f572916554e6053ee9a59c2fbadb7c7'),
(1, '79a9316e23134dc7632c51ec305af756'),
(1, '4656a71e7802cff7757cc3e4a9281927'),
(1, 'f986b0490311d17c909d8b8cceb6756d'),
(1, '11c183d403657cafd9fe8f22921686cc'),
(1, 'cbbf9ec130e723aa24e600d16471570b'),
(1, 'e9a45f7858204bfc7b9ae196c3d9d7a7'),
(1, '47097d94df0cb3b5f4c281e4d1077ce5'),
(1, '09241a765e64d1f2280a888f255add7a'),
(1, 'fb8f0d2dd52a205aa367d3f1b549f9c1'),
(1, '97f260fceebdedcdc9f5ab99fef8a78e'),
(1, '86a91d11239bc96df9c917c99bb19050'),
(1, 'a1bac8f28c3378e56d0485cc880098d2'),
(1, 'f563a6a7d1a20fc4dddca1dab785bd1f'),
(1, '49159657f8644de0ea1c2dca29b11482'),
(1, 'd94065d725bbdeedeebe1c5a8693873f'),
(1, '03563f756de4a637b2bfa6c33b9ba3cd'),
(1, '97b51c9df42b8e8754a6127073b6cf82'),
(1, '865a527fc94bf609d2a86e6e7c2ca481'),
(1, 'cb877f34c2a65534a458bf54ea823f08'),
(1, '9d3b3d092d27b3ed0b76281225f98bf7'),
(1, 'b27e8ad3b0d538abe256169cb3363b4a'),
(1, '51c3819eb7ced02c46c22acda2c74205'),
(1, '7ba6a2f608295d678aea4195b46a0219'),
(1, 'db9785d69f04ab00266814b8e992da91'),
(1, '2db8d58d0f3062f9d7e1259492652c1d'),
(1, 'e2065c855899f8ed49e6ce89162683e8'),
(1, '1319c9749c2ffdc6832e5aa98b54e659'),
(1, 'fd34b1095c30bde6ce7aab8ec0683bb7'),
(1, '7ad81d348b958714dea07ece9fd522c2'),
(1, '2fb87de8954d03f91e6771846e2a8d6f'),
(1, '6597cb67e75e5991ceb2afe448db3c35'),
(1, '6c2619df4ae1e9377d47116d88eeeb5d'),
(1, 'd25074818bd0a543482f55ff21330676'),
(1, '8adf0a8c2be11dac6ec490bfebcc8d30'),
(1, 'b1b6e78d3e26e1aed5a408ddb975547b'),
(1, '1c8437de40bafcd6ba379ac53f1b8169'),
(1, 'bb03494e04ca2cd3918de70f74498fcf'),
(1, 'e00752cbe957fb707c6a6ac574452cd5'),
(1, 'e1360e0256b6ce58c3f8591bb93a70dc'),
(1, 'fd6c48fc6006b8276d12f8859daec350'),
(1, '75b513f7b143b20a53c9a6d415bb02d1'),
(1, 'ed2eff479f6e1333c83561125f8e64b9'),
(1, '90a269caf890d1fae8fe57dc4ed19e56'),
(1, 'b553b566dbb94a712641f346db436ad4'),
(1, '76f9f24634d3590cbdc3dbfc2d84cb6c'),
(1, '30c94206e5aac9e6936c42a2f60cc426'),
(1, '692915e5a20c90c4c0d73f3db9c1154a'),
(1, '836c2fa67a8d62c3b8c04bccd2ae31be'),
(1, 'a514416bfa9ec0a46b98de23194398a4'),
(1, '6abd9038780dd1f005df9fc31feb1560'),
(1, 'e35237b0b1595f75b6e641669d67cfce'),
(1, 'cbd15cc24b2892beb3444780fdbb19be'),
(1, '26c91da37713481245b08bd74565f06b'),
(1, '95cbbc0c9529592bee188dd2f3e7b8c7'),
(1, 'f5d7f236ed25c850ec5e2f462eed7c4f'),
(1, 'e5d7e266169ff20581413412b970c66c'),
(1, 'e1e60351f1d5c9b62ad373e6343ccb78'),
(1, '8c4a8899f75122dece210f306fc50de7'),
(1, '70ec05034e36531f8cc15a995ee2a1ca'),
(1, '1a9f9cbdf1adedcba49592fb48fb23f1'),
(1, '1369618c25eb074761f2f9dd12cd082e'),
(1, 'bcdd94a0c273aa1b0700de7e5e4213d0'),
(1, '65248f0a7165ab82ee03e312f20526e7'),
(1, '4c494372395a62e213ebecfe4dff643b'),
(1, 'c270582f05844d3adef31a226386100f'),
(1, '97047972139aa66b73ad3af35ca45b44'),
(1, '40242bfc27f4b1a1af097bf88cc9619d'),
(1, '604cabd06bd3b31fc5b8dd3802fcde2c'),
(1, '687dc02afb37f19b29ceb2339eb52676'),
(1, '581294986a59b3403d05de69c6be96d7'),
(1, '38ec3bbef46ad213aef4731fe049f5f2'),
(1, 'b50c594fc7e5603d5178b33109f6fb02'),
(1, '7988cb4fd66dd5acf3c8e4ed87a1ec5f'),
(1, '53a805ec4780ff7641acdfe374373e80'),
(1, '226b3556a1804e38029fd237a66b1db0'),
(1, 'b36725f672acde360df83bb877028d73'),
(1, '6ba01a6240c18488040f9385de557081'),
(1, '3b35edddcab0cac1aec5615dc86cd1f8'),
(1, '8a9d0cf19bd5111f45b75ac99ef298e8'),
(1, '1fa8e36ea62adfd1dbe8943a5cbd7457'),
(1, '38304b8fd2f1552ee09b14be3c1d3f2e'),
(1, 'acc5ce8a0c88cb4c5f74140e36f83e1f'),
(1, '948fb19cc466d95cd9fc7188ad5411bf'),
(1, 'c5024d345c3453b92b627bfc212dbf0e'),
(1, 'ef5ca491b2c6298026a4eb425ec63c6b'),
(1, '9b661821b671d44f7221a4a36ab80d8d');

2144
backend/mock/songs.sql Normal file

File diff suppressed because it is too large Load Diff

23
backend/src/db.rs Normal file
View File

@ -0,0 +1,23 @@
use diesel_async::{
pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager},
AsyncPgConnection,
};
use log::info;
use crate::Opt;
pub type DbPool = Pool<AsyncPgConnection>;
pub async fn setup(opt: &Opt) -> eyre::Result<DbPool> {
let manager = AsyncDieselConnectionManager::new(&opt.database_url);
info!("setting up database pool");
let pool = Pool::builder(manager).build()?;
info!("testing database connection");
let _db_test = pool.get().await?;
// TODO: migrations
Ok(pool)
}

178
backend/src/main.rs Normal file
View File

@ -0,0 +1,178 @@
mod db;
mod schema;
use std::{
path::{Path, PathBuf},
sync::Arc,
};
use actix_files::NamedFile;
use actix_web::{
get,
middleware::Logger,
web::{self, Json},
App, HttpRequest, HttpServer, Responder,
};
use clap::Parser;
use diesel::{ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper};
use diesel_async::RunQueryDsl;
use dotenv::dotenv;
use serde::{Deserialize, Serialize};
use crate::db::DbPool;
#[derive(
Serialize,
Deserialize,
Debug,
Clone,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Queryable,
Selectable,
)]
#[diesel(table_name = crate::schema::song)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct Song {
pub song_hash: String,
pub title: String,
pub artist: String,
pub cover: Option<String>,
pub language: Option<String>,
pub video: Option<String>,
pub year: Option<String>,
pub genre: Option<String>,
pub bpm: String,
#[serde(rename = "duetsingerp1")]
pub duet_singer_1: Option<String>,
#[serde(rename = "duetsingerp2")]
pub duet_singer_2: Option<String>,
}
#[derive(Serialize, Deserialize, Queryable, Selectable, Debug, Clone, Default)]
#[diesel(table_name = crate::schema::custom_list)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct CustomList {
id: i32,
name: String,
}
#[get("/")]
async fn root() -> actix_web::Result<NamedFile> {
let path: &Path = "dist/index.html".as_ref();
Ok(NamedFile::open(path)?)
}
async fn index(req: HttpRequest) -> actix_web::Result<NamedFile> {
let path: PathBuf = req.match_info().query("filename").parse().unwrap();
let path = Path::new("dist").join(path);
Ok(NamedFile::open(path)?)
}
#[get("/images/songs/{image}")]
async fn song_image(
path: web::Path<String>,
opt: web::Data<Arc<Opt>>,
) -> actix_web::Result<NamedFile> {
let image = path.into_inner();
let path = opt.covers_dir.join(image);
Ok(NamedFile::open(path)?)
}
#[get("/songs")]
async fn songs(pool: web::Data<DbPool>) -> impl Responder {
use schema::song::dsl::*;
let mut db = pool.get().await.unwrap();
let songs = song.select(Song::as_select()).load(&mut db).await.unwrap();
Json(songs)
}
#[get("/custom/lists")]
async fn custom_lists(pool: web::Data<DbPool>) -> impl Responder {
use schema::custom_list::dsl::*;
let mut db = pool.get().await.unwrap();
let lists: Vec<String> = custom_list.select(name).load(&mut db).await.unwrap();
Json(lists)
}
#[get("/custom/list/{list}")]
async fn custom_list(pool: web::Data<DbPool>, path: web::Path<String>) -> impl Responder {
use schema::custom_list::dsl::*;
use schema::custom_list_entry::dsl::*;
let mut db = pool.get().await.unwrap();
let list: CustomList = custom_list
.select(CustomList::as_select())
.filter(name.eq(&*path))
.get_result(&mut db)
.await
.unwrap();
let list_entries: Vec<String> = custom_list_entry
.select(song_hash)
.filter(list_id.eq(list.id))
.load(&mut db)
.await
.unwrap();
Json(list_entries)
}
#[derive(Parser)]
pub struct Opt {
/// Address to bind to.
#[clap(short, long, env = "BIND_ADDRESS", default_value = "0.0.0.0")]
address: String,
/// Port to bind to.
#[clap(short, long, env = "BIND_PORT", default_value = "8080")]
port: u16,
/// Postgresql URL.
#[clap(short, long, env = "DATABASE_URL")]
database_url: String,
/// Directory where song covers are stored.
#[clap(short, long, env = "COVERS_DIR")]
covers_dir: PathBuf,
}
#[actix_web::main]
async fn main() -> eyre::Result<()> {
dotenv().ok();
let opt = Arc::new(Opt::parse());
env_logger::init();
let db_pool = db::setup(&opt).await?;
let app = {
let opt = Arc::clone(&opt);
move || {
let logger = Logger::default();
App::new()
.wrap(logger)
.app_data(web::Data::new(db_pool.clone()))
.app_data(web::Data::new(Arc::clone(&opt)))
.service(root)
.service(songs)
.service(song_image)
.service(custom_list)
.service(custom_lists)
.route("/{filename:.*}", web::get().to(index))
}
};
HttpServer::new(app)
.bind((opt.address.as_str(), opt.port))?
.run()
.await?;
Ok(())
}

36
backend/src/schema.rs Normal file
View File

@ -0,0 +1,36 @@
// @generated automatically by Diesel CLI.
diesel::table! {
custom_list (id) {
id -> Int4,
name -> Text,
}
}
diesel::table! {
custom_list_entry (list_id, song_hash) {
list_id -> Int4,
song_hash -> Text,
}
}
diesel::table! {
song (song_hash) {
song_hash -> Text,
title -> Text,
artist -> Text,
cover -> Nullable<Text>,
language -> Nullable<Text>,
video -> Nullable<Text>,
year -> Nullable<Text>,
genre -> Nullable<Text>,
bpm -> Text,
duet_singer_1 -> Nullable<Text>,
duet_singer_2 -> Nullable<Text>,
}
}
diesel::joinable!(custom_list_entry -> custom_list (list_id));
diesel::joinable!(custom_list_entry -> song (song_hash));
diesel::allow_tables_to_appear_in_same_query!(custom_list, custom_list_entry, song,);