Compare commits

..
7 Commits
Author SHA1 Message Date
phoenix 718cdb617d S3 coverart (#261)
soaricarus_api CI / Check (push) Successful in 6m37s
soaricarus_api CI / Rustfmt (push) Successful in 4m19s
soaricarus_api CI / Clippy (push) Successful in 6m57s
soaricarus_api CI / build (push) Successful in 6m58s
soaricarus_api CI / Test Suite (push) Successful in 3m37s
Reviewed-on: #261
2026-08-15 12:30:44 -04:00
phoenix ff3567f8ef S3 song download (#260)
soaricarus_api CI / Rustfmt (push) Successful in 3m46s
soaricarus_api CI / Check (push) Successful in 5m3s
soaricarus_api CI / Test Suite (push) Successful in 6m21s
soaricarus_api CI / Clippy (push) Successful in 6m46s
soaricarus_api CI / build (push) Successful in 8m45s
pr CI / Test Suite (pull_request) Successful in 5m9s
Reviewed-on: #260
2026-08-14 16:41:56 -04:00
phoenix 695faf7502 Coverart queue (#259)
pr CI / Test Suite (pull_request) Failing after 2m42s
soaricarus_api CI / Check (push) Successful in 7m15s
soaricarus_api CI / Rustfmt (push) Successful in 5m25s
soaricarus_api CI / Clippy (push) Failing after 7m1s
soaricarus_api CI / build (push) Successful in 7m47s
soaricarus_api CI / Test Suite (push) Failing after 2m24s
Reviewed-on: #259
2026-08-14 08:42:38 -04:00
phoenix bf88f249ea Song queue (#258)
soaricarus_api CI / Check (push) Successful in 3m59s
soaricarus_api CI / build (push) Successful in 5m6s
soaricarus_api CI / Rustfmt (push) Successful in 5m27s
soaricarus_api CI / Test Suite (push) Successful in 5m50s
soaricarus_api CI / Clippy (push) Successful in 6m26s
Reviewed-on: #258
2026-08-13 15:27:31 -04:00
phoenix 7188138018 Refactoring tests (#257)
soaricarus_api CI / Rustfmt (push) Successful in 4m43s
soaricarus_api CI / Check (push) Successful in 5m26s
soaricarus_api CI / Test Suite (push) Successful in 6m4s
soaricarus_api CI / Clippy (push) Successful in 6m54s
soaricarus_api CI / build (push) Successful in 8m54s
Reviewed-on: #257
2026-08-12 16:53:24 -04:00
phoenix 20bb7f4dad ci: Fix workflow (#256)
soaricarus_api CI / Check (push) Successful in 3m48s
soaricarus_api CI / Test Suite (push) Successful in 4m47s
soaricarus_api CI / Rustfmt (push) Successful in 3m51s
soaricarus_api CI / Clippy (push) Successful in 3m30s
soaricarus_api CI / build (push) Successful in 1m22s
Reviewed-on: #256
2026-08-08 14:50:04 -04:00
phoenix 19d6ec2190 Obj str do it (#255)
soaricarus_api CI / Clippy (push) Successful in 3m59s
soaricarus_api CI / Rustfmt (push) Successful in 4m32s
soaricarus_api CI / Check (push) Successful in 4m44s
soaricarus_api CI / Test Suite (push) Failing after 5m24s
soaricarus_api CI / build (push) Successful in 6m4s
Reviewed-on: #255
2026-08-08 14:29:14 -04:00
36 changed files with 2779 additions and 1491 deletions
+1
View File
@@ -13,6 +13,7 @@ POSTGRES_MAIN_PORT=5432
DATABASE_URL=postgres://${POSTGRES_MAIN_USER}:${POSTGRES_MAIN_PASSWORD}@${POSTGRES_MAIN_HOST}:${POSTGRES_MAIN_PORT}/${POSTGRES_MAIN_DB} DATABASE_URL=postgres://${POSTGRES_MAIN_USER}:${POSTGRES_MAIN_PASSWORD}@${POSTGRES_MAIN_HOST}:${POSTGRES_MAIN_PORT}/${POSTGRES_MAIN_DB}
S3_BUCKET_NAME=soaricarus-bucket S3_BUCKET_NAME=soaricarus-bucket
S3_ENDPOINT_URL=http://maze:3900
GARAGE_S3_REGION=maze GARAGE_S3_REGION=maze
GARAGE_RPC_SECRET=c2bde5c3f9c04116a7563e25ea5ba6fab25319daa2fe8a952ad57d0b8159b590 GARAGE_RPC_SECRET=c2bde5c3f9c04116a7563e25ea5ba6fab25319daa2fe8a952ad57d0b8159b590
GARAGE_METRICS_TOKEN=sD2g4jiYV66KdZWtIuj2amezuryzv81mZNktpMNcmU4= GARAGE_METRICS_TOKEN=sD2g4jiYV66KdZWtIuj2amezuryzv81mZNktpMNcmU4=
+1
View File
@@ -13,6 +13,7 @@ POSTGRES_MAIN_PORT=5432
DATABASE_URL=postgres://${POSTGRES_MAIN_USER}:${POSTGRES_MAIN_PASSWORD}@${POSTGRES_MAIN_HOST}:${POSTGRES_MAIN_PORT}/${POSTGRES_MAIN_DB} DATABASE_URL=postgres://${POSTGRES_MAIN_USER}:${POSTGRES_MAIN_PASSWORD}@${POSTGRES_MAIN_HOST}:${POSTGRES_MAIN_PORT}/${POSTGRES_MAIN_DB}
S3_BUCKET_NAME=soaricarus-bucket S3_BUCKET_NAME=soaricarus-bucket
S3_ENDPOINT_URL=http://maze:3900
GARAGE_S3_REGION=maze GARAGE_S3_REGION=maze
GARAGE_RPC_SECRET=c2bde5c3f9c04116a7563e25ea5ba6fab25319daa2fe8a952ad57d0b8159b590 GARAGE_RPC_SECRET=c2bde5c3f9c04116a7563e25ea5ba6fab25319daa2fe8a952ad57d0b8159b590
GARAGE_METRICS_TOKEN=sD2g4jiYV66KdZWtIuj2amezuryzv81mZNktpMNcmU4= GARAGE_METRICS_TOKEN=sD2g4jiYV66KdZWtIuj2amezuryzv81mZNktpMNcmU4=
+116
View File
@@ -0,0 +1,116 @@
name: pr CI
on:
pull_request:
branches: [ "main" ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Test Suite
runs-on: ubuntu-24.04
timeout-minutes: 20
services:
postgres:
image: postgres:18.4-alpine
env:
POSTGRES_USER: ${{ secrets.DB_TEST_USER || 'testuser' }}
POSTGRES_PASSWORD: ${{ secrets.DB_TEST_PASSWORD || 'testpassword' }}
POSTGRES_DB: ${{ secrets.DB_TEST_NAME || 'testdb' }}
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v6
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 1.97
- uses: Swatinem/rust-cache@v2
- name: Verify Docker Environment
run: |
echo "Runner User Info:"
id
echo "Checking Docker Version:"
docker --version
echo "Checking Docker Daemon Status (info):"
docker info
echo "Checking Docker Daemon Status (ps):"
docker ps -a
echo "Docker environment check complete."
- name: Setup S3
env:
AWS_ACCESS_KEY_ID: "${{ secrets.GARAGE_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.GARAGE_SECRET }}"
AWS_DEFAULT_REGION: us-east-1
ENDPOINT_URL: "${{ secrets.S3_CI_ENDPOINT_URL}}"
run: |
sudo apt update
echo "Installing curl and unzip"
sudo apt install curl unzip -y
echo "Installing awscli"
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
aws --version
BUCKET="soaricarus-ci-bucket-$(echo $RANDOM | md5sum | head -c 8)"
echo "S3_BUCKET=$BUCKET" >> $GITHUB_ENV
echo "AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION" >> $GITHUB_ENV
echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> $GITHUB_ENV
echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> $GITHUB_ENV
echo "Creating bucket: $BUCKET"
aws --endpoint-url=$ENDPOINT_URL s3 mb s3://$BUCKET --region $AWS_DEFAULT_REGION
- name: Run tests
env:
DATABASE_URL: postgresql://${{ secrets.DB_TEST_USER || 'testuser' }}:${{ secrets.DB_TEST_PASSWORD || 'testpassword' }}@postgres:5432/${{ secrets.DB_TEST_NAME || 'testdb' }}
RUST_LOG: info # Optional: configure test log level
SECRET_MAIN_KEY: ${{ secrets.TOKEN_SECRET_KEY }}
SSH_AUTH_SOCK: ${{ env.SSH_AUTH_SOCK }}
ROOT_DIRECTORY: "/tmp"
AWS_ACCESS_KEY_ID: "${{ secrets.GARAGE_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.GARAGE_SECRET }}"
AWS_DEFAULT_REGION: "${{ env.AWS_DEFAULT_REGION }}"
GARAGE_DEFAULT_ACCESS_KEY: "${{ secrets.GARAGE_KEY_ID }}"
GARAGE_DEFAULT_SECRET_KEY: "${{ secrets.GARAGE_SECRET }}"
GARAGE_S3_REGION: "${{ env.AWS_DEFAULT_REGION }}"
S3_BUCKET_NAME: ${{ env.S3_BUCKET }}
S3_ENDPOINT_URL: "${{ secrets.S3_CI_ENDPOINT_URL }}"
AWS_S3_FORCE_PATH_STYLE: "true"
run: |
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/soaricarus_models_deploy_key
chmod 600 ~/.ssh/soaricarus_models_deploy_key
ssh-keyscan ${{ secrets.MYHOST }} >> ~/.ssh/known_hosts
eval $(ssh-agent -s)
ssh-add -v ~/.ssh/soaricarus_models_deploy_key
cargo test
- name: Cleanup
if: always()
run: |
echo "Cleaning up bucket $BUCK"
echo "Region: $AWS_DEFAULT_REGION"
aws --endpoint-url=$ENDPOINT_URL \
s3 rb s3://$BUCK --force \
--region $AWS_DEFAULT_REGION
env:
ENDPOINT_URL: ${{ secrets.S3_CI_ENDPOINT_URL }}
AWS_ACCESS_KEY_ID: "${{ env.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ env.AWS_SECRET_ACCESS_KEY }}"
AWS_DEFAULT_REGION: "${{ env.AWS_DEFAULT_REGION }}"
BUCK: ${{ env.S3_BUCKET }}
continue-on-error: true
+54 -3
View File
@@ -3,8 +3,6 @@ name: soaricarus_api CI
on: on:
push: push:
branches: [ "main" ] branches: [ "main" ]
pull_request:
branches: [ "main" ]
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
@@ -35,6 +33,7 @@ jobs:
test: test:
name: Test Suite name: Test Suite
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
timeout-minutes: 20
services: services:
postgres: postgres:
image: postgres:18.4-alpine image: postgres:18.4-alpine
@@ -65,14 +64,49 @@ jobs:
echo "Checking Docker Daemon Status (ps):" echo "Checking Docker Daemon Status (ps):"
docker ps -a docker ps -a
echo "Docker environment check complete." echo "Docker environment check complete."
- name: Setup S3
env:
AWS_ACCESS_KEY_ID: "${{ secrets.GARAGE_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.GARAGE_SECRET }}"
AWS_DEFAULT_REGION: us-east-1
ENDPOINT_URL: "${{ secrets.S3_CI_ENDPOINT_URL}}"
run: |
sudo apt update
echo "Installing curl and unzip"
sudo apt install curl unzip -y
echo "Installing awscli"
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
aws --version
BUCKET="soaricarus-ci-bucket-$(echo $RANDOM | md5sum | head -c 8)"
echo "S3_BUCKET=$BUCKET" >> $GITHUB_ENV
echo "AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION" >> $GITHUB_ENV
echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> $GITHUB_ENV
echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> $GITHUB_ENV
echo "Creating bucket: $BUCKET"
aws --endpoint-url=$ENDPOINT_URL s3 mb s3://$BUCKET --region $AWS_DEFAULT_REGION
- name: Run tests - name: Run tests
env: env:
# Define DATABASE_URL for tests to use
DATABASE_URL: postgresql://${{ secrets.DB_TEST_USER || 'testuser' }}:${{ secrets.DB_TEST_PASSWORD || 'testpassword' }}@postgres:5432/${{ secrets.DB_TEST_NAME || 'testdb' }} DATABASE_URL: postgresql://${{ secrets.DB_TEST_USER || 'testuser' }}:${{ secrets.DB_TEST_PASSWORD || 'testpassword' }}@postgres:5432/${{ secrets.DB_TEST_NAME || 'testdb' }}
RUST_LOG: info # Optional: configure test log level RUST_LOG: info # Optional: configure test log level
SECRET_MAIN_KEY: ${{ secrets.TOKEN_SECRET_KEY }} SECRET_MAIN_KEY: ${{ secrets.TOKEN_SECRET_KEY }}
SSH_AUTH_SOCK: ${{ env.SSH_AUTH_SOCK }} SSH_AUTH_SOCK: ${{ env.SSH_AUTH_SOCK }}
ROOT_DIRECTORY: "/tmp" ROOT_DIRECTORY: "/tmp"
AWS_ACCESS_KEY_ID: "${{ secrets.GARAGE_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.GARAGE_SECRET }}"
AWS_DEFAULT_REGION: "${{ env.AWS_DEFAULT_REGION }}"
GARAGE_DEFAULT_ACCESS_KEY: "${{ secrets.GARAGE_KEY_ID }}"
GARAGE_DEFAULT_SECRET_KEY: "${{ secrets.GARAGE_SECRET }}"
GARAGE_S3_REGION: "${{ env.AWS_DEFAULT_REGION }}"
S3_BUCKET_NAME: ${{ env.S3_BUCKET }}
S3_ENDPOINT_URL: "${{ secrets.S3_CI_ENDPOINT_URL }}"
AWS_S3_FORCE_PATH_STYLE: "true"
run: | run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/soaricarus_models_deploy_key echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/soaricarus_models_deploy_key
@@ -84,6 +118,23 @@ jobs:
cargo test cargo test
- name: Cleanup
if: always()
run: |
echo "Cleaning up bucket $BUCK"
echo "Region: $AWS_DEFAULT_REGION"
aws --endpoint-url=$ENDPOINT_URL \
s3 rb s3://$BUCK --force \
--region $AWS_DEFAULT_REGION
env:
ENDPOINT_URL: ${{ secrets.S3_CI_ENDPOINT_URL }}
AWS_ACCESS_KEY_ID: "${{ env.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ env.AWS_SECRET_ACCESS_KEY }}"
AWS_DEFAULT_REGION: "${{ env.AWS_DEFAULT_REGION }}"
BUCK: ${{ env.S3_BUCKET }}
continue-on-error: true
fmt: fmt:
name: Rustfmt name: Rustfmt
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
Generated
+1216 -224
View File
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "soaricarus_api" name = "soaricarus_api"
version = "0.5.2" version = "0.5.7"
edition = "2024" edition = "2024"
rust-version = "1.95" rust-version = "1.95"
license = "MIT" license = "MIT"
@@ -10,25 +10,26 @@ description = "Core API for interacting with soaricarus"
axum = { version = "0.8.9", features = ["multipart"] } axum = { version = "0.8.9", features = ["multipart"] }
axum-extra = { version = "0.12.6", features = ["cookie"] } axum-extra = { version = "0.12.6", features = ["cookie"] }
openssl = { version = "0.10.81", features = ["vendored"] } openssl = { version = "0.10.81", features = ["vendored"] }
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.229", features = ["derive"] }
serde_json = { version = "1.0.150" } serde_json = { version = "1.0.151" }
tokio = { version = "1.52.3", features = ["full"] } tokio = { version = "1.53.1", features = ["full"] }
tokio-util = { version = "0.7.18", features = ["io"] } tokio-util = { version = "0.7.19", features = ["io"] }
tower = { version = "0.5.3", features = ["full"] } tower = { version = "0.5.3", features = ["full"] }
tower-http = { version = "0.7.0", features = ["cors", "timeout"] } tower-http = { version = "0.7.0", features = ["cors", "timeout"] }
tracing-subscriber = "0.3.23" tracing-subscriber = "0.3.23"
futures = { version = "0.3.32" } futures = { version = "0.3.34" }
mime_guess = { version = "2.0.5" } mime_guess = { version = "2.0.5" }
uuid = { version = "1.23.5", features = ["v4", "serde"] } uuid = { version = "1.24.1", features = ["v4", "serde"] }
sqlx = { version = "0.9.0", features = ["postgres", "runtime-tokio", "tls-native-tls", "time", "uuid"] } sqlx = { version = "0.9.0", features = ["postgres", "runtime-tokio", "tls-native-tls", "time", "uuid"] }
time = { version = "0.3.53", features = ["formatting", "macros", "parsing", "serde"] } time = { version = "0.3.55", features = ["formatting", "macros", "parsing", "serde"] }
jsonwebtoken = { version = "10.3.0", features = ["rust_crypto"] } jsonwebtoken = { version = "10.3.0", features = ["rust_crypto"] }
josekit = { version = "0.10.3" } josekit = { version = "0.10.3" }
utoipa = { version = "5.5.0", features = ["axum_extras"] } utoipa = { version = "5.5.0", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] } utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] }
simeta = { git = "ssh://git@git.kundeng.us/phoenix/simeta.git", tag = "v0.6.1-main-b690995806-680" } simeta = { git = "ssh://git@git.kundeng.us/phoenix/simeta.git", tag = "v0.6.2" }
simodels = { git = "ssh://git@git.kundeng.us/phoenix/simodels.git", tag = "v0.11.3-main-fe9d101bd0-111" } simodels = { git = "ssh://git@git.kundeng.us/phoenix/simodels.git", tag = "v0.11.5-main-94d97cb81d-111" }
sienvy = { git = "ssh://git@git.kundeng.us/phoenix/sienvy.git", tag = "v0.8.0-main-d06c8fdf49-006" } sienvy = { git = "ssh://git@git.kundeng.us/phoenix/sienvy.git", tag = "v0.8.1" }
labyrinth = { git = "ssh://git@git.kundeng.us/phoenix/labyrinth.git", tag = "v0.0.5" }
[dev-dependencies] [dev-dependencies]
common-multipart-rfc7578 = { version = "0.7.0" } common-multipart-rfc7578 = { version = "0.7.0" }
-4
View File
@@ -30,10 +30,6 @@ RUN --mount=type=ssh mkdir src && \
COPY src ./src COPY src ./src
COPY .env ./.env COPY .env ./.env
COPY migrations ./migrations COPY migrations ./migrations
COPY scripts/init-garage.sh /scripts/init-garage.sh
# Make it executable
RUN chmod +x /scripts/init-garage.sh
RUN --mount=type=ssh \ RUN --mount=type=ssh \
cargo build --release --quiet cargo build --release --quiet
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2025 Kun Deng Copyright (c) 2026 Kun Deng
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+15
View File
@@ -19,6 +19,21 @@ Copy the `.env.docker.sample` file to `.env`. Ensure that the `ROOT_DIRECTORY` v
and exists on the docker image's filesystem. The credentials for the database doesn't need to be and exists on the docker image's filesystem. The credentials for the database doesn't need to be
changed for development, but if deploying it, it should be modified. changed for development, but if deploying it, it should be modified.
### S3 Setup
Using the copied `.env.docker.sample`, ensure that the following has been properly populated.
```
S3_ENDPOINT_URL - URL of the S3 bucket
S3_BUCKET_NAME - Name of the S3 bucket
GARAGE_S3_REGION - Region of the S3 bucket
GARAGE_RPC_SECRET - A 32-bit hex string
GARAGE_DEFAULT_ACCESS_KEY - Access key starting with GK followed with a 16-bit hex string
GARAGE_DEFAULT_SECRET_KEY - Secret key with a 32-bit hex string
```
The other S3-related environment variables can be left as is for the sake of getting
started.
Build containers Build containers
``` ```
docker compose build --ssh default docker compose build --ssh default
+25 -26
View File
@@ -6,7 +6,7 @@ services:
build: build:
context: . context: .
ssh: ["default"] ssh: ["default"]
container_name: soaricarus container_name: api
ports: ports:
- "8000:8000" - "8000:8000"
env_file: env_file:
@@ -46,23 +46,23 @@ services:
# - ./path/to/your/web-api-repo:/app # - ./path/to/your/web-api-repo:/app
# --- songparser service --- # --- songparser service ---
# songparser: songparser:
# build: build:
# context: ../songparser context: ../songparser
# ssh: ["default"] ssh: ["default"]
# dockerfile: Dockerfile dockerfile: Dockerfile
# container_name: songparser container_name: songparser
# restart: unless-stopped restart: unless-stopped
# env_file: env_file:
# - ../songparser/.env - ../songparser/.env
# depends_on: depends_on:
# - api - api
# - main_db - main_db
# - auth_api - auth_api
# - auth_db - auth_db
# - maze - maze
# networks: networks:
# - soaricarus-network - soaricarus-network
# --- storage service --- # --- storage service ---
maze: maze:
@@ -100,25 +100,24 @@ services:
GARAGE_LOG_LEVEL: "info" GARAGE_LOG_LEVEL: "info"
GARAGE_S3_BUCKET: "${S3_BUCKET_NAME}" GARAGE_S3_BUCKET: "${S3_BUCKET_NAME}"
GARAGE_S3_REGION: "${GARAGE_S3_REGION}"
GARAGE_DEFAULT_ACCESS_KEY: "${GARAGE_DEFAULT_ACCESS_KEY}" GARAGE_DEFAULT_ACCESS_KEY: "${GARAGE_DEFAULT_ACCESS_KEY}"
GARAGE_DEFAULT_SECRET_KEY: "${GARAGE_DEFAULT_SECRET_KEY}" GARAGE_DEFAULT_SECRET_KEY: "${GARAGE_DEFAULT_SECRET_KEY}"
GARAGE_DEFAULT_BUCKET: "${GARAGE_DEFAULT_BUCKET}" GARAGE_DEFAULT_BUCKET: "${GARAGE_DEFAULT_BUCKET}"
AWS_ACCESS_KEY_ID: "${GARAGE_DEFAULT_ACCESS_KEY}"
AWS_SECRET_ACCESS_KEY: "${GARAGE_DEFAULT_SECRET_KEY}"
AWS_DEFAULT_REGION: "${GARAGE_DEFAULT_BUCKET}"
AWS_S3_FORCE_PATH_STYLE: "true"
volumes: volumes:
# - garage_data:/data # Persist Garage data
- ./garage.toml:/etc/garage.toml:ro # Mount config file - ./garage.toml:/etc/garage.toml:ro # Mount config file
- shared_credentials:/data # Persist Garage data - shared_credentials:/data # Persist Garage data
ports: ports:
- "3900:3900" # S3 API - "3900:3900" # S3 API
- "3901:3901" # S3 API public - "3901:3901" # S3 API public
- "3902:3902" # Admin API - "3902:3902" # Admin API
# entrypoint: ["/bin/bash", "-c"]
# command:
# - |
# ls /
# /garage -c /etc/garage.toml server
# command: ["/garage", "-c", "/etc/garage.toml", "server"]
command: ["/garage", "server", "--single-node", "--default-bucket"] command: ["/garage", "server", "--single-node", "--default-bucket"]
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3900/"] test: ["CMD", "curl", "-f", "http://localhost:3900/"]
@@ -129,7 +128,7 @@ services:
networks: networks:
- soaricarus-network - soaricarus-network
extra_hosts: extra_hosts:
- "garage.local:127.0.0.1" # Needed for local domain resolution - "garage.local:127.0.0.1"
# PostgreSQL Database Service # PostgreSQL Database Service
main_db: main_db:
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
# Wait for Garage to be ready
echo "Waiting for Garage to be ready..."
until curl -s http://localhost:3902/health; do
sleep 2
done
# Set up the bucket
echo "Creating bucket..."
docker exec garage garage bucket create soaricarus-data
# Set bucket permissions
echo "Setting bucket permissions..."
docker exec garage garage bucket allow soaricarus-data --read --write --owner
echo "Garage setup complete!"
-18
View File
@@ -1,18 +0,0 @@
# Use a base image with a shell (Alpine)
FROM alpine:latest
# Install necessary tools
RUN apk add --no-cache curl
# Copy the Garage binary from the official image
COPY --from=dxflrs/garage:v2.3.0 /garage /usr/local/bin/garage
# Copy the entrypoint script
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Set the entrypoint
ENTRYPOINT ["/entrypoint.sh"]
# Pass through any arguments to Garage
CMD []
-161
View File
@@ -1,161 +0,0 @@
#!/bin/sh
set -e
BUCKET_NAME="${BUCKET_NAME:-soaricarus-storage}"
CREDENTIALS_FILE="/shared/garage-credentials.env"
SETUP_DONE="/shared/setup.done"
GARAGE_BIN="/usr/local/bin/garage"
# Function to write credentials
write_credentials() {
local access_key="$1"
local secret_key="$2"
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
cat > "$CREDENTIALS_FILE" << EOF
GARAGE_ACCESS_KEY_ID=$access_key
GARAGE_SECRET_ACCESS_KEY=$secret_key
GARAGE_BUCKET_NAME=$BUCKET_NAME
GARAGE_ENDPOINT=http://localhost:3900
EOF
touch "$SETUP_DONE"
echo "✅ Credentials saved to $CREDENTIALS_FILE"
}
sleep 20
echo "Starting garage"
GARAGE_COMMAND_EXEC="$GARAGE_BIN -c /etc/garage.toml server"
sleep 10
# Check if setup already completed
if [ -f "$SETUP_DONE" ]; then
echo "️ Setup already completed. Starting Garage..."
sleep 5
exec $GARAGE_COMMANDS_EXEC
exit
fi
echo "🔧 First-time setup - initializing Garage..."
# Start Garage in background for setup (just the binary, no daemon subcommand)
echo "⏳ Starting Garage in background..."
echo "GARAGE_COMMAND_EXEC: $GARAGE_COMMAND_EXEC"
# $GARAGE_BIN "$@" &
# GARAGE_PID=$!
# $GARAGE_BIN -c /etc/garage.toml & GARAGE_PID=$!
$GARAGE_COMMAND_EXEC & GARAGE_PID=$!
sleep 10
sleep 10
echo "GARAGE_PID: $GARAGE_PID"
# echo "Command: $GARAGE_BIN $@"
# Apply cluster layout
if ! $GARAGE_BIN layout show 2>/dev/null | grep -q "Current cluster layout"; then
sleep 5
echo "📦 Applying cluster layout..."
NODE_ID=$($GARAGE_BIN node id | head -n1)
if [ -z "$NODE_ID" ]; then
echo "$NODE_ID is empty"
sleep 5
exit
fi
echo "Node ID: $NODE_ID"
sleep 10
echo "Assigning layout"
# $GARAGE_BIN layout assign --version 1 -z ny1 -c 4G $NODE_ID
$GARAGE_BIN --rpc-secret "$GARAGE_RPC_SECRET" layout assign -z ny1 -c 4G $NODE_ID
# $GARAGE_BIN status
# sleep 5
# $GARAGE_BIN status
# echo "Show layout"
# $GARAGE_BIN layout show
sleep 10
echo "Applying layout"
$GARAGE_BIN layout apply --version 1
# $GARAGE_BIN layout apply
echo " ✅ Layout applied"
else
echo "️ Layout already applied"
fi
sleep 10
# Create bucket
echo "Bucket: $BUCKET_NAME"
if ! $GARAGE_BIN bucket info "$BUCKET_NAME" 2>/dev/null; then
echo "📦 Creating bucket: $BUCKET_NAME"
sleep 5
$GARAGE_BIN bucket create "$BUCKET_NAME"
sleep 10
else
echo "️ Bucket $BUCKET_NAME already exists"
fi
# Handle key
#if [ -n "$AWS_ACCESS_KEY_ID" ] && [ -n "$AWS_SECRET_ACCESS_KEY" ]; then
# echo "🔑 Using pre-defined credentials from .env"
# if ! $GARAGE_BIN key info "$AWS_ACCESS_KEY_ID" 2>/dev/null; then
# $GARAGE_BIN key import "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" "app-key"
# echo " ✅ Key imported: $AWS_ACCESS_KEY_ID"
# else
# echo " ️ Key already exists: $AWS_ACCESS_KEY_ID"
# fi
# ACCESS_KEY="$AWS_ACCESS_KEY_ID"
# SECRET_KEY="$AWS_SECRET_ACCESS_KEY"
#else
# echo "🔑 Generating new key..."
# KEY_OUTPUT=$($GARAGE_BIN key create --name "app-key")
# echo "$KEY_OUTPUT"
# ACCESS_KEY=$(echo "$KEY_OUTPUT" | grep 'Key ID:' | awk '{print $3}')
# SECRET_KEY=$(echo "$KEY_OUTPUT" | grep 'Secret key:' | awk '{print $3}')
# echo " ✅ Key generated: $ACCESS_KEY"
#fi
echo "🔑 Generating new key..."
KEY_OUTPUT=$($GARAGE_BIN key create "app-key")
echo "$KEY_OUTPUT"
sleep 15
ACCESS_KEY=$(echo "$KEY_OUTPUT" | grep 'Key ID:' | awk '{print $3}')
SECRET_KEY=$(echo "$KEY_OUTPUT" | grep 'Secret key:' | awk '{print $3}')
echo " ✅ Key generated: $ACCESS_KEY"
sleep 5
# Wait for Garage to be ready
# echo "⏳ Waiting for Garage API..."
# until curl -s -f "http://localhost:3900/v0/status" > /dev/null 2>&1; do
# echo " Not ready yet, sleeping 2s..."
# sleep 2
# done
# echo "✅ Garage API is responding!"
# Grant permissions
$GARAGE_BIN bucket allow --read --write --owner "$BUCKET_NAME" --key "$ACCESS_KEY"
echo " ✅ Permissions granted"
sleep 10
# Save credentials
write_credentials "$ACCESS_KEY" "$SECRET_KEY"
sleep 10
echo "🎉 Setup complete!"
echo " Bucket: $BUCKET_NAME"
echo " Access Key: $ACCESS_KEY"
# Stop background Garage
echo "⏹️ Stopping background Garage..."
kill "$GARAGE_PID"
wait "$GARAGE_PID" 2>/dev/null || true
# Start Garage in foreground
echo "🚀 Starting Garage in foreground..."
# exec $GARAGE_BIN "$@"
sleep 20
exec $GARAGE_COMMANDS_EXEC
-109
View File
@@ -1,109 +0,0 @@
#!/bin/sh
set -e
BUCKET_NAME="${BUCKET_NAME:-soaricarus-storage}"
CREDENTIALS_FILE="/shared/garage-credentials.env"
SETUP_DONE="/shared/setup.done"
# Function to write credentials to shared volume
write_credentials() {
local access_key="$1"
local secret_key="$2"
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
cat > "$CREDENTIALS_FILE" << EOF
GARAGE_ACCESS_KEY_ID=$access_key
GARAGE_SECRET_ACCESS_KEY=$secret_key
GARAGE_BUCKET_NAME=$BUCKET_NAME
GARAGE_ENDPOINT=http://maze:3900
EOF
touch "$SETUP_DONE"
echo "✅ Credentials saved to $CREDENTIALS_FILE"
}
# Check if setup already done
if [ -f "$SETUP_DONE" ]; then
echo "️ Setup already completed. Starting Garage..."
# Source credentials if needed for the main Garage process
# shellcheck source=/dev/null
[ -f "$CREDENTIALS_FILE" ] && . "$CREDENTIALS_FILE"
exec /usr/local/bin/garage "$@"
fi
echo "🔧 Running initial setup..."
# Start Garage in background for setup
echo "⏳ Starting Garage in background..."
/usr/local/bin/garage "$@" &
GARAGE_PID=$!
# Wait for Garage API to be ready
echo "⏳ Waiting for Garage API..."
# until curl -s -f "http://localhost:3900/v0/status" > /dev/null 2>&1; do
# echo " Not ready yet, sleeping 2s..."
# sleep 2
#done
echo "✅ Garage API is responding!"
# Apply layout if not already applied
if ! /usr/local/bin/garage layout show 2>/dev/null | grep -q "Current cluster layout"; then
echo "📦 Applying cluster layout..."
NODE_ID=$(/usr/local/bin/garage node id | head -n1)
/usr/local/bin/garage layout assign --version 1 "$NODE_ID"
/usr/local/bin/garage layout apply --version 1
echo " ✅ Layout applied"
sleep 3
else
echo "️ Layout already applied"
fi
echo "Laid out"
# Create bucket if it doesn't exist
if ! /usr/local/bin/garage bucket info "$BUCKET_NAME" 2>/dev/null; then
echo "📦 Creating bucket: $BUCKET_NAME"
/usr/local/bin/garage bucket create "$BUCKET_NAME"
else
echo "️ Bucket $BUCKET_NAME already exists"
fi
# Create or import key
# We'll use the credentials from .env if provided, otherwise generate new ones
if [ -n "$AWS_ACCESS_KEY_ID" ] && [ -n "$AWS_SECRET_ACCESS_KEY" ]; then
echo "🔑 Using pre-defined AWS credentials from .env"
if ! /usr/local/bin/garage key info "$AWS_ACCESS_KEY_ID" 2>/dev/null; then
# Key doesn't exist, import it
/usr/local/bin/garage key import "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" "app-key"
echo " ✅ Key imported: $AWS_ACCESS_KEY_ID"
else
echo " ️ Key already exists: $AWS_ACCESS_KEY_ID"
fi
ACCESS_KEY="$AWS_ACCESS_KEY_ID"
SECRET_KEY="$AWS_SECRET_ACCESS_KEY"
else
echo "🔑 Generating new key..."
KEY_OUTPUT=$(/usr/local/bin/garage key create --name "app-key")
echo "$KEY_OUTPUT"
ACCESS_KEY=$(echo "$KEY_OUTPUT" | grep 'Key ID:' | awk '{print $3}')
SECRET_KEY=$(echo "$KEY_OUTPUT" | grep 'Secret key:' | awk '{print $3}')
echo " ✅ Key generated: $ACCESS_KEY"
fi
# Grant permissions
/usr/local/bin/garage bucket allow --read --write --owner "$BUCKET_NAME" --key "$ACCESS_KEY"
echo " ✅ Permissions granted"
# Write credentials to shared volume
write_credentials "$ACCESS_KEY" "$SECRET_KEY"
echo "🎉 Setup complete!"
echo " Bucket: $BUCKET_NAME"
echo " Access Key: $ACCESS_KEY"
# Stop the background Garage process
echo "⏹️ Stopping background Garage process..."
kill "$GARAGE_PID"
wait "$GARAGE_PID" 2>/dev/null || true
echo "🚀 Starting Garage in foreground..."
exec /usr/local/bin/garage "$@"
-36
View File
@@ -1,36 +0,0 @@
#!/bin/sh
set -e
BUCKET_NAME="${BUCKET_NAME:-soaricarus-storage}"
CREDENTIALS_FILE="/shared/garage-credentials.env"
SETUP_DONE="/shared/setup.done"
echo "🔧 Running initial setup..."
# Start Garage in background for setup
echo "⏳ Starting Garage in background..."
/usr/local/bin/garage "$@" &
GARAGE_PID=$!
# Wait for Garage API to be ready
echo "⏳ Waiting for Garage API..."
# until curl -s -f "http://localhost:3900/v0/status" > /dev/null 2>&1; do
# echo " Not ready yet, sleeping 2s..."
# sleep 2
#done
echo "✅ Garage API is responding!"
# Apply layout if not already applied
#if ! /usr/local/bin/garage layout show 2>/dev/null | grep -q "Current cluster layout"; then
# echo "📦 Applying cluster layout..."
# NODE_ID=$(/usr/local/bin/garage node id | head -n1)
# echo $NODE_ID
# /usr/local/bin/garage layout assign --version 1 "$NODE_ID"
# /usr/local/bin/garage layout apply --version 1
# echo " ✅ Layout applied"
# sleep 3
#else
# echo "️ Layout already applied"
# fi
echo "Laid out"
-19
View File
@@ -1,19 +0,0 @@
# Use a base image with a shell (Alpine)
FROM alpine:latest
# Install necessary tools
RUN apk add --no-cache curl
# Copy the Garage binary from the official image
COPY --from=dxflrs/garage:v2.3.0 /garage /usr/local/bin/garage
# Copy the entrypoint script
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# Set the entrypoint
ENTRYPOINT ["/entrypoint.sh"]
# Pass through any arguments to Garage
CMD []
View File
-19
View File
@@ -1,19 +0,0 @@
# Use a base image with a shell (Alpine)
FROM alpine:latest
# Install necessary tools
RUN apk add --no-cache curl
# Copy the Garage binary from the official image
COPY --from=dxflrs/garage:v2.3.0 /garage /usr/local/bin/garage
# Copy the entrypoint script
COPY run.sh /run.sh
RUN chmod +x /run.sh
# Set the run
ENTRYPOINT ["/run.sh"]
# Pass through any arguments to Garage
CMD []
-41
View File
@@ -1,41 +0,0 @@
#!/bin/sh
set -e
BUCKET_NAME="${BUCKET_NAME:-soaricarus-storage}"
CREDENTIALS_FILE="/shared/garage-credentials.env"
SETUP_DONE="/shared/setup.done"
GARAGE_BIN="/usr/local/bin/garage"
# Function to write credentials
write_credentials() {
local access_key="$1"
local secret_key="$2"
mkdir -p "$(dirname "$CREDENTIALS_FILE")"
cat > "$CREDENTIALS_FILE" << EOF
GARAGE_ACCESS_KEY_ID=$access_key
GARAGE_SECRET_ACCESS_KEY=$secret_key
GARAGE_BUCKET_NAME=$BUCKET_NAME
GARAGE_ENDPOINT=http://localhost:3900
EOF
touch "$SETUP_DONE"
echo "✅ Credentials saved to $CREDENTIALS_FILE"
}
sleep 5
echo "Starting garage"
GARAGE_COMMAND_EXEC="$GARAGE_BIN -c /etc/garage.toml server"
echo "Running"
# Start Garage in background for setup (just the binary, no daemon subcommand)
echo "⏳ Starting Garage in background..."
echo "GARAGE_COMMAND_EXEC: $GARAGE_COMMAND_EXEC"
# $GARAGE_BIN "$@" &
# GARAGE_PID=$!
# $GARAGE_BIN -c /etc/garage.toml & GARAGE_PID=$!
$GARAGE_COMMAND_EXEC & GARAGE_PID=$!
sleep 10
echo "GARAGE_PID: $GARAGE_PID"
+18 -2
View File
@@ -6,10 +6,17 @@ CREATE TABLE IF NOT EXISTS "songQueue" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
filename TEXT NOT NULL, filename TEXT NOT NULL,
status TEXT CHECK (status IN ('pending', 'ready', 'processing', 'done')), status TEXT CHECK (status IN ('pending', 'ready', 'processing', 'done')),
data BYTEA NULL,
user_id UUID NULL user_id UUID NULL
); );
CREATE TABLE IF NOT EXISTS "songQueueData" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
file_key TEXT NOT NULL,
bucket TEXT NOT NULL,
region TEXT NOT NULL,
song_queue_id UUID NOT NULL
);
-- Table to store queued metadata -- Table to store queued metadata
CREATE TABLE IF NOT EXISTS "metadataQueue" ( CREATE TABLE IF NOT EXISTS "metadataQueue" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -21,11 +28,18 @@ CREATE TABLE IF NOT EXISTS "metadataQueue" (
-- Table to store queued coverart -- Table to store queued coverart
CREATE TABLE IF NOT EXISTS "coverartQueue" ( CREATE TABLE IF NOT EXISTS "coverartQueue" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
data BYTEA NULL,
file_type TEXT NOT NULL, file_type TEXT NOT NULL,
song_queue_id UUID NULL song_queue_id UUID NULL
); );
CREATE TABLE IF NOT EXISTS "coverartQueueData" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
file_key TEXT NOT NULL,
bucket TEXT NOT NULL,
region TEXT NOT NULL,
coverart_queue_id UUID NOT NULL
);
-- Create an index for better query performance -- Create an index for better query performance
CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata); CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata);
@@ -49,6 +63,7 @@ CREATE TABLE IF NOT EXISTS "song" (
date_created timestamptz DEFAULT now(), date_created timestamptz DEFAULT now(),
filename TEXT NOT NULL, filename TEXT NOT NULL,
directory TEXT NOT NULL, directory TEXT NOT NULL,
file_key TEXT NOT NULL,
user_id UUID NULL user_id UUID NULL
-- TODO: Add coverart id later. This will allow multiple songs to be linked to a single cover art -- TODO: Add coverart id later. This will allow multiple songs to be linked to a single cover art
); );
@@ -59,5 +74,6 @@ CREATE TABLE IF NOT EXISTS "coverart" (
directory TEXT NOT NULL, directory TEXT NOT NULL,
filename TEXT NOT NULL, filename TEXT NOT NULL,
file_type TEXT NOT NULL, file_type TEXT NOT NULL,
file_key TEXT NOT NULL,
song_id UUID NOT NULL song_id UUID NOT NULL
); );
-89
View File
@@ -1,89 +0,0 @@
#!/bin/sh
set -e
BUCKET_NAME="soaricarus-storage"
sleep 15
echo $GARAGE_RPC_SECRET
echo "Yes"
sleep 10
echo "⏳ Waiting for Garage to be ready..."
# until docker exec maze curl -s -f "http://localhost:3901/v0/status" > /dev/null 2>&1; do
until docker exec maze nc -z "localhost 3900" > /dev/null 2>&1; do
echo " Garage not ready yet, retrying in 2s..."
sleep 2
done
echo "✅ Garage API is responding!"
sleep 3
# Check if layout is already applied
if docker exec maze garage layout show 2>/dev/null | grep -q "Current cluster layout"; then
echo "️ Layout already applied, checking bucket..."
else
echo "📦 Applying cluster layout..."
# Get node ID
MAX_RETRIES=10
RETRY_COUNT=0
NODE_ID=""
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
NODE_ID=$(docker exec maze garage node id 2>/dev/null | head -n1)
if [ -n "$NODE_ID" ]; then
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo " Waiting for node ID (attempt $RETRY_COUNT/$MAX_RETRIES)..."
sleep 2
done
if [ -z "$NODE_ID" ]; then
echo "❌ Failed to get node ID after $MAX_RETRIES attempts"
exit 1
fi
echo " Node ID: $NODE_ID"
docker exec maze garage layout assign --version 1 "$NODE_ID"
docker exec maze garage layout apply --version 1
echo " ✅ Layout applied"
sleep 3
fi
# Check if bucket exists
if docker exec maze garage bucket info "${BUCKET_NAME}" 2>/dev/null; then
echo "️ Bucket '${BUCKET_NAME}' already exists. Skipping setup."
exit 0
fi
echo "📦 Creating bucket and configuring permissions..."
# Create the bucket
docker exec maze garage bucket create "${BUCKET_NAME}"
echo " ✅ Bucket created: ${BUCKET_NAME}"
# Check if key exists
if docker exec maze garage key info "${AWS_ACCESS_KEY_ID}" 2>/dev/null; then
echo " ️ Key already exists: ${AWS_ACCESS_KEY_ID}"
else
docker exec maze garage key create \
--name "app-key" \
"${AWS_ACCESS_KEY_ID}" \
"${AWS_SECRET_ACCESS_KEY}"
echo " ✅ Key created: ${AWS_ACCESS_KEY_ID}"
fi
# Grant permissions
docker exec maze garage bucket allow \
--read \
--write \
--owner \
"${BUCKET_NAME}" \
--key "${AWS_ACCESS_KEY_ID}"
echo " ✅ Permissions granted"
echo "🎉 Setup complete!"
echo " Bucket: ${BUCKET_NAME}"
echo " Access Key: ${AWS_ACCESS_KEY_ID}"
echo " Endpoint: http://maze:3900"
+55 -17
View File
@@ -63,14 +63,16 @@ pub mod endpoint {
let mut response = super::response::create_coverart::Response::default(); let mut response = super::response::create_coverart::Response::default();
let id = payload.coverart_queue_id; let id = payload.coverart_queue_id;
match repo_queue::coverart::get_coverart_queue_data_with_id(&pool, &id).await { match repo_queue::data::queue::coverart::get_with_coverart_queue_id(&pool, &id).await {
Ok((_id, file_key, _bucket, _region, _)) => {
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match lr.download(&file_key).await {
Ok(data) => { Ok(data) => {
let song_id = payload.song_id;
match crate::repo::song::get_song(&pool, &song_id).await {
Ok(song) => {
let directory = sienvy::environment::get_root_directory().value;
let file_type = let file_type =
simeta::detection::coverart::file_type_from_data(&data).unwrap(); simeta::detection::coverart::file_type_from_data(&data).unwrap();
let coverart_type = if file_type.file_type let coverart_type = if file_type.file_type
== simeta::detection::coverart::constants::JPEG_TYPE == simeta::detection::coverart::constants::JPEG_TYPE
{ {
@@ -93,18 +95,45 @@ pub mod endpoint {
let filename = let filename =
simodels::coverart::generate_filename(coverart_type, true).unwrap(); simodels::coverart::generate_filename(coverart_type, true).unwrap();
let mut coverart = simodels::coverart::init::init_coverart_dir_and_filename( let data = labyrinth::Data {
raw_data: data,
..Default::default()
};
let new_file_key = format!("processed/coverart/{filename}");
match lr.upload(&new_file_key, &data).await {
Ok(_resp) => {
// TODO: Change it to the s3 bucket
let directory = sienvy::environment::get_root_directory().value;
let mut coverart =
simodels::coverart::init::init_coverart_dir_and_filename(
&directory, &filename, &directory, &filename,
); );
let song = match crate::repo::song::get_song(
&pool,
&payload.song_id,
)
.await
{
Ok(song) => song,
Err(err) => {
eprintln!("Error: {err:?}");
return (
axum::http::StatusCode::BAD_REQUEST,
axum::Json(response),
);
}
};
coverart.title = song.album.clone(); coverart.title = song.album.clone();
coverart.file_type = file_type.file_type; coverart.file_type = file_type.file_type;
coverart.data = data; coverart.data = data.raw_data;
coverart.file_key = new_file_key;
match coverart.save_to_filesystem() { match repo::coverart::create(&pool, &coverart, &payload.song_id)
Ok(_) => { .await
match repo::coverart::create(&pool, &coverart, &song.id).await { {
Ok(id) => { Ok(id) => {
coverart.song_id = song_id; coverart.song_id = payload.song_id;
coverart.id = id; coverart.id = id;
println!("Cover Art created"); println!("Cover Art created");
@@ -120,7 +149,7 @@ pub mod endpoint {
} }
} }
Err(err) => { Err(err) => {
response.message = err.to_string(); eprintln!("Error: {err:?}");
( (
axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response), axum::Json(response),
@@ -129,14 +158,20 @@ pub mod endpoint {
} }
} }
Err(err) => { Err(err) => {
response.message = err.to_string(); eprintln!("Error: {err:?}");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) (
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
} }
} }
} }
Err(err) => { Err(err) => {
response.message = err.to_string(); eprintln!("Error: {err:?}");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) (
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
} }
} }
} }
@@ -197,8 +232,11 @@ pub mod endpoint {
axum::Extension(pool): axum::Extension<sqlx::PgPool>, axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>, axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
) -> (axum::http::StatusCode, axum::response::Response) { ) -> (axum::http::StatusCode, axum::response::Response) {
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match repo::coverart::get_coverart(&pool, &id).await { match repo::coverart::get_coverart(&pool, &id).await {
Ok(coverart) => match simodels::coverart::io::to_data(&coverart) { Ok(coverart) => match lr.download(&coverart.file_key).await {
Ok(data) => { Ok(data) => {
let (file_type, img_type) = let (file_type, img_type) =
match simeta::detection::coverart::file_type_from_data(&data) { match simeta::detection::coverart::file_type_from_data(&data) {
+110 -15
View File
@@ -178,14 +178,60 @@ pub mod endpoint {
file_type file_type
); );
match repo::coverart::insert(&pool, &raw_data, &file_type.file_type).await { let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
let data = labyrinth::Data {
raw_data,
..Default::default()
};
let filename = simodels::coverart::generate_filename(
simodels::types::CoverArtType::JpegExtension,
true,
)
.unwrap();
let file_path = format!("queued/coverart/{filename}");
println!("Key: {file_path:?}");
match lr.upload(&file_path, &data).await {
Ok(_res) => {
match repo::coverart::insert(&pool, &file_type.file_type).await {
Ok(id) => { Ok(id) => {
println!("Inserting queued data");
match repo::data::queue::coverart::insert(
&pool,
&file_path,
&lr.config.bucket,
&lr.config.region,
&id,
)
.await
{
Ok(_id) => {
println!("Successfully did it");
response.message = String::from("Successful"); response.message = String::from("Successful");
response.data.push(id); response.data.push(id);
(axum::http::StatusCode::OK, axum::Json(response)) (axum::http::StatusCode::OK, axum::Json(response))
} }
Err(err) => { Err(err) => {
response.message = err.to_string(); response.message = err.to_string();
(
axum::http::StatusCode::BAD_REQUEST,
axum::Json(response),
)
}
}
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
Err(err) => {
eprintln!("Error: {err:?}");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) (axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
} }
} }
@@ -313,11 +359,19 @@ pub mod endpoint {
)] )]
pub async fn fetch_coverart_with_data( pub async fn fetch_coverart_with_data(
axum::Extension(pool): axum::Extension<sqlx::PgPool>, axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>, axum::extract::Path(coverart_queue_id): axum::extract::Path<uuid::Uuid>,
) -> (axum::http::StatusCode, axum::response::Response) { ) -> (axum::http::StatusCode, axum::response::Response) {
match repo::coverart::get_coverart_queue_data_with_id(&pool, &id).await { match repo::data::queue::coverart::get_with_coverart_queue_id(&pool, &coverart_queue_id)
.await
{
Ok((_id, file_key, _bucket, _region, _)) => {
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match lr.download(&file_key).await {
Ok(data) => { Ok(data) => {
let file_type = simeta::detection::coverart::file_type_from_data(&data).unwrap(); let file_type =
simeta::detection::coverart::file_type_from_data(&data).unwrap();
let bytes = axum::body::Bytes::from(data); let bytes = axum::body::Bytes::from(data);
let mut response = bytes.into_response(); let mut response = bytes.into_response();
let headers = response.headers_mut(); let headers = response.headers_mut();
@@ -330,9 +384,13 @@ pub mod endpoint {
== simeta::detection::coverart::constants::JPEG_TYPE == simeta::detection::coverart::constants::JPEG_TYPE
{ {
simodels::types::CoverArtType::JpegExtension simodels::types::CoverArtType::JpegExtension
} else if file_type.file_type == simeta::detection::coverart::constants::JPG_TYPE { } else if file_type.file_type
== simeta::detection::coverart::constants::JPG_TYPE
{
simodels::types::CoverArtType::JpgExtension simodels::types::CoverArtType::JpgExtension
} else if file_type.file_type == simeta::detection::coverart::constants::PNG_TYPE { } else if file_type.file_type
== simeta::detection::coverart::constants::PNG_TYPE
{
simodels::types::CoverArtType::PngExtension simodels::types::CoverArtType::PngExtension
} else { } else {
return ( return (
@@ -340,7 +398,8 @@ pub mod endpoint {
axum::response::Response::default(), axum::response::Response::default(),
); );
}; };
let filename = simodels::coverart::generate_filename(coverart_type, true).unwrap(); let filename =
simodels::coverart::generate_filename(coverart_type, true).unwrap();
headers.insert( headers.insert(
axum::http::header::CONTENT_DISPOSITION, axum::http::header::CONTENT_DISPOSITION,
format!("attachment; filename=\"{filename}\"") format!("attachment; filename=\"{filename}\"")
@@ -350,10 +409,23 @@ pub mod endpoint {
(axum::http::StatusCode::OK, response) (axum::http::StatusCode::OK, response)
} }
Err(_err) => ( Err(err) => {
axum::http::StatusCode::BAD_REQUEST, eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::response::Response::default(), axum::response::Response::default(),
), )
}
}
}
Err(err) => {
eprintln!("Record not found");
eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::response::Response::default(),
)
}
} }
} }
@@ -383,16 +455,39 @@ pub mod endpoint {
let coverart_queue_id = payload.coverart_queue_id; let coverart_queue_id = payload.coverart_queue_id;
match repo::coverart::get_coverart_queue_with_id(&pool, &coverart_queue_id).await { match repo::coverart::get_coverart_queue_with_id(&pool, &coverart_queue_id).await {
Ok(coverart_queue) => { Ok(_coverart_queue) => {
match repo::coverart::wipe_data(&pool, &coverart_queue.id).await { match repo::data::queue::coverart::get_with_coverart_queue_id(
Ok(id) => { &pool,
&coverart_queue_id,
)
.await
{
Ok((_id, file_key, _bucket, _region, _)) => {
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match lr.delete(&file_key).await {
Ok(_) => {
response.message = String::from("Success"); response.message = String::from("Success");
response.data.push(id); response.data.push(coverart_queue_id);
(axum::http::StatusCode::OK, axum::Json(response)) (axum::http::StatusCode::OK, axum::Json(response))
} }
Err(err) => {
eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
}
}
}
Err(err) => { Err(err) => {
response.message = err.to_string(); response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) (
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
} }
} }
} }
+150 -16
View File
@@ -156,12 +156,12 @@ pub mod endpoint {
); );
let raw_data: Vec<u8> = data.to_vec(); let raw_data: Vec<u8> = data.to_vec();
let copied_raw_data = data.to_vec();
match super::is_song_valid(&raw_data).await { match super::is_song_valid(&raw_data).await {
Ok(valid) => { Ok(valid) => {
if valid { if valid {
match repo::song::insert( match repo::song::insert(
&pool, &pool,
&raw_data,
&file_name, &file_name,
&crate::repo::queue::song::status::PENDING.to_string(), &crate::repo::queue::song::status::PENDING.to_string(),
) )
@@ -169,6 +169,51 @@ pub mod endpoint {
{ {
Ok(queued_song) => { Ok(queued_song) => {
results.push(queued_song); results.push(queued_song);
println!("Uploading to bucket");
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
let data = labyrinth::Data {
raw_data: copied_raw_data,
..Default::default()
};
println!("Filename: {file_name:?}");
let file_path = format!("queued/song/{file_name}");
println!("Path: {file_path:?}");
match lr.upload(&file_path, &data).await {
Ok(_res) => {
match repo::data::insert(
&pool,
&file_path,
&lr.config.bucket,
&lr.config.region,
&queued_song,
)
.await
{
Ok(_id) => {}
Err(err) => {
eprintln!("Error: {err:?}");
return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response));
}
}
}
Err(err) => match err {
labyrinth::Error::Info(err_str) => {
eprintln!("Error: {:?}", err_str);
response.message = err_str.to_string();
return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response));
}
labyrinth::Error::SError(err_s) => {
eprintln!("Error: {:?}", err_s);
response.message = err_s.to_string();
return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response));
}
},
}
} }
Err(err) => { Err(err) => {
response.message = err.to_string(); response.message = err.to_string();
@@ -297,11 +342,15 @@ pub mod endpoint {
)] )]
pub async fn download_queued_song( pub async fn download_queued_song(
axum::Extension(pool): axum::Extension<sqlx::PgPool>, axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>, axum::extract::Path(song_queue_id): axum::extract::Path<uuid::Uuid>,
) -> (axum::http::StatusCode, axum::response::Response) { ) -> (axum::http::StatusCode, axum::response::Response) {
println!("Id: {id}"); println!("Id: {song_queue_id}");
match repo::song::get_data(&pool, &id).await { let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match repo::data::get_with_song_queue_id(&pool, &song_queue_id).await {
Ok((id, file_key, _bucket, _region, _)) => match lr.download(&file_key).await {
Ok(data) => { Ok(data) => {
let by = axum::body::Bytes::from(data); let by = axum::body::Bytes::from(data);
let mut response = by.into_response(); let mut response = by.into_response();
@@ -319,6 +368,14 @@ pub mod endpoint {
(axum::http::StatusCode::OK, response) (axum::http::StatusCode::OK, response)
} }
Err(err) => {
eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::response::Response::default(),
)
}
},
Err(_err) => ( Err(_err) => (
axum::http::StatusCode::BAD_REQUEST, axum::http::StatusCode::BAD_REQUEST,
axum::response::Response::default(), axum::response::Response::default(),
@@ -406,7 +463,7 @@ pub mod endpoint {
) )
)] )]
pub async fn update_song_queue( pub async fn update_song_queue(
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>, axum::extract::Path(song_queue_id): axum::extract::Path<uuid::Uuid>,
axum::Extension(pool): axum::Extension<sqlx::PgPool>, axum::Extension(pool): axum::Extension<sqlx::PgPool>,
mut multipart: axum::extract::Multipart, mut multipart: axum::extract::Multipart,
) -> ( ) -> (
@@ -437,12 +494,45 @@ pub mod endpoint {
match super::is_song_valid(&raw_data).await { match super::is_song_valid(&raw_data).await {
Ok(valid) => { Ok(valid) => {
if valid { if valid {
match repo::song::update(&pool, &raw_data, &id).await { let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
println!("Valid song");
match repo::data::get_with_song_queue_id(&pool, &song_queue_id).await {
Ok((id, file_key, _bucket, _region, _)) => {
match lr.delete(&file_key).await {
Ok(_response) => {
let data = labyrinth::Data {
raw_data,
..Default::default()
};
let filename = simodels::song::generate_filename(
simodels::types::MusicType::FlacExtension,
true,
)
.unwrap();
let new_file_key = format!("queued/song/{filename}");
println!("New key: {new_file_key:?}");
match lr.upload(&new_file_key, &data).await {
Ok(_) => {
match repo::data::update_file_key(
&pool,
&id,
&new_file_key,
)
.await
{
Ok(_) => { Ok(_) => {
response.message = response.message =
String::from(super::super::super::response::SUCCESSFUL); super::super::super::response::SUCCESSFUL.to_string();
response.data.push(id); response.data.push(song_queue_id);
(axum::http::StatusCode::OK, axum::Json(response)) (
axum::http::StatusCode::OK,
axum::Json(response),
)
} }
Err(err) => { Err(err) => {
response.message = err.to_string(); response.message = err.to_string();
@@ -452,6 +542,33 @@ pub mod endpoint {
) )
} }
} }
}
Err(err) => {
eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
}
}
}
Err(err) => {
eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
}
}
}
Err(err) => {
eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
}
}
} else { } else {
response.message = String::from("Invalid song type"); response.message = String::from("Invalid song type");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) (axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
@@ -482,7 +599,8 @@ pub mod endpoint {
), ),
responses( responses(
(status = 200, description = "Queued song data wiped", body = super::response::wipe_data_from_song_queue::Response), (status = 200, description = "Queued song data wiped", body = super::response::wipe_data_from_song_queue::Response),
(status = 404, description = "Queued song cannot be found", body = super::response::wipe_data_from_song_queue::Response) (status = 404, description = "Queued song cannot be found", body = super::response::wipe_data_from_song_queue::Response),
(status = 500, description = "Error wiping song data", body = super::response::wipe_data_from_song_queue::Response)
) )
)] )]
pub async fn wipe_data_from_song_queue( pub async fn wipe_data_from_song_queue(
@@ -493,21 +611,37 @@ pub mod endpoint {
axum::Json<super::response::wipe_data_from_song_queue::Response>, axum::Json<super::response::wipe_data_from_song_queue::Response>,
) { ) {
let mut response = super::response::wipe_data_from_song_queue::Response::default(); let mut response = super::response::wipe_data_from_song_queue::Response::default();
let id = payload.song_queue_id; let song_queue_id = payload.song_queue_id;
match repo::song::get_song_queue(&pool, &id).await { match repo::song::get_song_queue(&pool, &song_queue_id).await {
Ok(song_queue) => match repo::song::wipe_data(&pool, &song_queue.id).await { Ok(_song_queue) => {
Ok(wiped_id) => { match repo::data::get_with_song_queue_id(&pool, &song_queue_id).await {
Ok((_id, file_key, _bucket, _region, _)) => {
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match lr.delete(&file_key).await {
Ok(_) => {
response.message = String::from("Success"); response.message = String::from("Success");
response.data.push(wiped_id); response.data.push(song_queue_id);
(axum::http::StatusCode::OK, axum::Json(response)) (axum::http::StatusCode::OK, axum::Json(response))
} }
Err(err) => {
eprintln!("Error: {err:?}");
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response),
)
}
}
}
Err(err) => { Err(err) => {
response.message = err.to_string(); response.message = err.to_string();
(axum::http::StatusCode::NOT_FOUND, axum::Json(response)) (axum::http::StatusCode::NOT_FOUND, axum::Json(response))
} }
}, }
}
Err(err) => { Err(err) => {
response.message = err.to_string(); response.message = err.to_string();
(axum::http::StatusCode::NOT_FOUND, axum::Json(response)) (axum::http::StatusCode::NOT_FOUND, axum::Json(response))
+84 -13
View File
@@ -139,7 +139,11 @@ pub mod endpoint {
.unwrap(); .unwrap();
song.directory = sienvy::environment::get_root_directory().value; song.directory = sienvy::environment::get_root_directory().value;
match repo_queue::song::get_data(&pool, &payload.song_queue_id).await { let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match repo_queue::data::get_with_song_queue_id(&pool, &payload.song_queue_id).await {
Ok((_id, file_key, _bucket, _region, _)) => match lr.download(&file_key).await {
Ok(data) => { Ok(data) => {
song.data = data; song.data = data;
let dir = std::path::Path::new(&song.directory); let dir = std::path::Path::new(&song.directory);
@@ -155,8 +159,14 @@ pub mod endpoint {
} }
} }
match song.save_to_filesystem() { song.file_key = format!("processed/song/{}", song.filename);
Ok(_) => match repo::song::insert(&pool, &song).await { let data_of_song = labyrinth::Data {
raw_data: song.data.clone(),
..Default::default()
};
match lr.upload(&song.file_key, &data_of_song).await {
Ok(_resp) => match repo::song::insert(&pool, &song).await {
Ok((date_created, id)) => { Ok((date_created, id)) => {
song.id = id; song.id = id;
song.date_created = Some(date_created); song.date_created = Some(date_created);
@@ -166,12 +176,13 @@ pub mod endpoint {
(axum::http::StatusCode::OK, axum::Json(response)) (axum::http::StatusCode::OK, axum::Json(response))
} }
Err(err) => { Err(err) => {
response.message = format!("{:?} song {:?}", err.to_string(), song); response.message =
format!("{:?} song {:?}", err.to_string(), song);
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) (axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
} }
}, },
Err(err) => { Err(err) => {
response.message = err.to_string(); eprintln!("Error: {err:?}");
( (
axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::http::StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(response), axum::Json(response),
@@ -179,6 +190,11 @@ pub mod endpoint {
} }
} }
} }
Err(err) => {
eprintln!("Error: {err:?}");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
},
Err(err) => { Err(err) => {
response.message = err.to_string(); response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) (axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
@@ -280,17 +296,41 @@ pub mod endpoint {
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>, axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
) -> impl IntoResponse { ) -> impl IntoResponse {
match repo::song::get_song(&pool, &id).await { match repo::song::get_song(&pool, &id).await {
Ok(song) => { Ok(mut song) => {
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match lr.download(&song.file_key).await {
Ok(data) => {
song.filename = simodels::song::generate_filename(
simodels::types::MusicType::FlacExtension,
true,
)
.unwrap();
// TODO: At some point, directory will no longer be needed with s3
song.directory = sienvy::environment::get_root_directory().value;
song.data = data;
match song.save_to_filesystem() {
Ok(_) => {
let song_path = song.song_path().unwrap(); let song_path = song.song_path().unwrap();
let path = std::path::Path::new(&song_path); let path = std::path::Path::new(&song_path);
if !path.starts_with(&song.directory) || !path.exists() { if !path.starts_with(&song.directory) || !path.exists() {
return Err((axum::http::StatusCode::NOT_FOUND, "File not found")); return Err((
axum::http::StatusCode::NOT_FOUND,
"File not found",
));
} }
let file = match tokio::fs::File::open(&path).await { let file = match tokio::fs::File::open(&path).await {
Ok(file) => file, Ok(file) => file,
Err(_) => return Err((axum::http::StatusCode::NOT_FOUND, "File not found")), Err(_) => {
return Err((
axum::http::StatusCode::NOT_FOUND,
"File not found",
));
}
}; };
let file_size = match file.metadata().await { let file_size = match file.metadata().await {
@@ -312,8 +352,34 @@ pub mod endpoint {
.header("content-length", file_size.to_string()) .header("content-length", file_size.to_string())
.body(axum::body::Body::from_stream(stream)) .body(axum::body::Body::from_stream(stream))
.unwrap(); .unwrap();
match song.remove_from_filesystem() {
Ok(rep) Ok(_) => Ok(rep),
Err(err) => {
eprintln!("Error: {err:?}");
Err((
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
"Could not read file",
))
}
}
}
Err(err) => {
eprintln!("Error: {err:?}");
Err((
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
"Could not find file",
))
}
}
}
Err(err) => {
eprintln!("Error: {err:?}");
Err((
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
"Could not find file",
))
}
}
} }
Err(_err) => Err(( Err(_err) => Err((
axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::http::StatusCode::INTERNAL_SERVER_ERROR,
@@ -337,8 +403,11 @@ pub mod endpoint {
axum::Extension(pool): axum::Extension<sqlx::PgPool>, axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>, axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
) -> (axum::http::StatusCode, axum::response::Response) { ) -> (axum::http::StatusCode, axum::response::Response) {
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match repo::song::get_song(&pool, &id).await { match repo::song::get_song(&pool, &id).await {
Ok(song) => match simodels::song::io::to_data(&song) { Ok(song) => match lr.download(&song.file_key).await {
Ok(data) => { Ok(data) => {
let bytes = axum::body::Bytes::from(data); let bytes = axum::body::Bytes::from(data);
let mut response = bytes.into_response(); let mut response = bytes.into_response();
@@ -387,6 +456,8 @@ pub mod endpoint {
axum::Json<super::response::delete_song::Response>, axum::Json<super::response::delete_song::Response>,
) { ) {
let mut response = super::response::delete_song::Response::default(); let mut response = super::response::delete_song::Response::default();
let lab_config = crate::util::maze::get_config();
let lr = labyrinth::Labyrinth { config: lab_config };
match repo::song::get_song(&pool, &id).await { match repo::song::get_song(&pool, &id).await {
Ok(song) => { Ok(song) => {
@@ -410,7 +481,7 @@ pub mod endpoint {
match repo::coverart::delete_coverart(&pool, &coverart.id).await match repo::coverart::delete_coverart(&pool, &coverart.id).await
{ {
Ok(deleted_coverart) => { Ok(deleted_coverart) => {
match song.remove_from_filesystem() { match lr.delete(&song.file_key).await {
Ok(_) => match coverart.remove_from_filesystem() { Ok(_) => match coverart.remove_from_filesystem() {
Ok(_) => { Ok(_) => {
response.message = String::from( response.message = String::from(
@@ -428,7 +499,7 @@ pub mod endpoint {
} }
}, },
Err(err) => { Err(err) => {
response.message = err.to_string(); eprintln!("Error: {err:?}");
(axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response)) (axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response))
} }
} }
+6
View File
@@ -0,0 +1,6 @@
pub mod auth;
pub mod callers;
pub mod config;
pub mod db;
pub mod repo;
pub mod util;
+459 -339
View File
File diff suppressed because it is too large Load Diff
+27 -8
View File
@@ -7,13 +7,14 @@ pub async fn create(
) -> Result<uuid::Uuid, sqlx::Error> { ) -> Result<uuid::Uuid, sqlx::Error> {
let result = sqlx::query( let result = sqlx::query(
r#" r#"
INSERT INTO "coverart" (title, directory, filename, file_type, song_id) VALUES($1, $2, $3, $4, $5) RETURNING id; INSERT INTO "coverart" (title, directory, filename, file_type, file_key, song_id) VALUES($1, $2, $3, $4, $5, $6) RETURNING id;
"#, "#,
) )
.bind(&coverart.title) .bind(&coverart.title)
.bind(&coverart.directory) .bind(&coverart.directory)
.bind(&coverart.filename) .bind(&coverart.filename)
.bind(&coverart.file_type) .bind(&coverart.file_type)
.bind(&coverart.file_key)
.bind(song_id) .bind(song_id)
.fetch_one(pool) .fetch_one(pool)
.await .await
@@ -39,7 +40,7 @@ pub async fn get_coverart(
) -> Result<simodels::coverart::CoverArt, sqlx::Error> { ) -> Result<simodels::coverart::CoverArt, sqlx::Error> {
let result = sqlx::query( let result = sqlx::query(
r#" r#"
SELECT id, title, directory, filename, file_type, song_id FROM "coverart" WHERE id = $1; SELECT id, title, directory, filename, file_type, file_key, song_id FROM "coverart" WHERE id = $1;
"#, "#,
) )
.bind(id) .bind(id)
@@ -71,6 +72,10 @@ pub async fn get_coverart(
.try_get("file_type") .try_get("file_type")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(), .unwrap(),
file_key: row
.try_get("file_key")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(),
song_id: row song_id: row
.try_get("song_id") .try_get("song_id")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
@@ -87,7 +92,7 @@ pub async fn get_coverart_with_song_id(
) -> Result<simodels::coverart::CoverArt, sqlx::Error> { ) -> Result<simodels::coverart::CoverArt, sqlx::Error> {
let result = sqlx::query( let result = sqlx::query(
r#" r#"
SELECT id, title, directory, filename, file_type, song_id FROM "coverart" WHERE song_id = $1; SELECT id, title, directory, filename, file_type, file_key, song_id FROM "coverart" WHERE song_id = $1;
"#, "#,
) )
.bind(song_id) .bind(song_id)
@@ -119,6 +124,10 @@ pub async fn get_coverart_with_song_id(
.try_get("file_type") .try_get("file_type")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(), .unwrap(),
file_key: row
.try_get("file_key")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(),
data: Vec::new(), data: Vec::new(),
song_id: row song_id: row
.try_get("song_id") .try_get("song_id")
@@ -137,7 +146,7 @@ pub async fn delete_coverart(
r#" r#"
DELETE FROM "coverart" DELETE FROM "coverart"
WHERE id = $1 WHERE id = $1
RETURNING id, title, directory, filename, file_type, song_id RETURNING id, title, directory, filename, file_type, file_key, song_id
"#, "#,
) )
.bind(id) .bind(id)
@@ -148,7 +157,15 @@ pub async fn delete_coverart(
}); });
match result { match result {
Ok(row) => Ok(simodels::coverart::CoverArt { Ok(row) => parse_row(&row).await,
Err(_err) => Err(sqlx::Error::RowNotFound),
}
}
async fn parse_row(
row: &sqlx::postgres::PgRow,
) -> Result<simodels::coverart::CoverArt, sqlx::Error> {
Ok(simodels::coverart::CoverArt {
id: row id: row
.try_get("id") .try_get("id")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
@@ -169,12 +186,14 @@ pub async fn delete_coverart(
.try_get("file_type") .try_get("file_type")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(), .unwrap(),
file_key: row
.try_get("file_key")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(),
song_id: row song_id: row
.try_get("song_id") .try_get("song_id")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(), .unwrap(),
data: Vec::new(), data: Vec::new(),
}), })
Err(_err) => Err(sqlx::Error::RowNotFound),
}
} }
+2 -82
View File
@@ -1,16 +1,11 @@
use sqlx::Row; use sqlx::Row;
pub async fn insert( pub async fn insert(pool: &sqlx::PgPool, file_type: &str) -> Result<uuid::Uuid, sqlx::Error> {
pool: &sqlx::PgPool,
data: &Vec<u8>,
file_type: &str,
) -> Result<uuid::Uuid, sqlx::Error> {
let result = sqlx::query( let result = sqlx::query(
r#" r#"
INSERT INTO "coverartQueue" (data, file_type) VALUES($1, $2) RETURNING id; INSERT INTO "coverartQueue" (file_type) VALUES($1) RETURNING id;
"#, "#,
) )
.bind(data)
.bind(file_type) .bind(file_type)
.fetch_one(pool) .fetch_one(pool)
.await .await
@@ -120,78 +115,3 @@ pub async fn get_coverart_queue_with_song_queue_id(
Err(_) => Err(sqlx::Error::RowNotFound), Err(_) => Err(sqlx::Error::RowNotFound),
} }
} }
pub async fn get_coverart_queue_data_with_id(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
) -> Result<Vec<u8>, sqlx::Error> {
let result = sqlx::query(
r#"
SELECT data FROM "coverartQueue" WHERE id = $1;
"#,
)
.bind(id)
.fetch_one(pool)
.await
.map_err(|e| {
eprintln!("Error querying data: {e:?}");
});
match result {
Ok(row) => Ok(row
.try_get("data")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap()),
Err(_) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn get_coverart_queue_data_with_song_queue_id(
pool: &sqlx::PgPool,
song_queue_id: &uuid::Uuid,
) -> Result<Vec<u8>, sqlx::Error> {
let result = sqlx::query(
r#"
SELECT data FROM "coverartQueue" WHERE song_queue_id = $1;
"#,
)
.bind(song_queue_id)
.fetch_one(pool)
.await
.map_err(|e| {
eprintln!("Error querying data: {e}");
});
match result {
Ok(row) => Ok(row
.try_get("data")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap()),
Err(_) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn wipe_data(
pool: &sqlx::PgPool,
coverart_queue_id: &uuid::Uuid,
) -> Result<uuid::Uuid, sqlx::Error> {
let result = sqlx::query(
r#"
UPDATE "coverartQueue" SET data = NULL WHERE id = $1 RETURNING id;
"#,
)
.bind(coverart_queue_id)
.fetch_one(pool)
.await
.map_err(|e| {
eprintln!("Error updating query: {e}");
});
match result {
Ok(row) => Ok(row
.try_get("id")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap()),
Err(_) => Err(sqlx::Error::RowNotFound),
}
}
+204
View File
@@ -0,0 +1,204 @@
use sqlx::Row;
pub async fn insert(
pool: &sqlx::PgPool,
file_key: &str,
bucket: &str,
region: &str,
song_queue_id: &uuid::Uuid,
) -> Result<uuid::Uuid, sqlx::Error> {
match sqlx::query(
r#"
INSERT INTO "songQueueData" (file_key, bucket, region, song_queue_id) VALUES($1, $2, $3, $4) RETURNING id;
"#,
)
.bind(file_key)
.bind(bucket)
.bind(region)
.bind(song_queue_id)
.fetch_one(pool).await {
Ok(row) => {
let id: uuid::Uuid = row.try_get("id")?;
Ok(id)
}
Err(_err) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn update_file_key(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
file_key: &str,
) -> Result<(), sqlx::Error> {
match sqlx::query(
r#"
UPDATE "songQueueData" SET file_key = $1 WHERE id = $2;
"#,
)
.bind(file_key)
.bind(id)
.execute(pool)
.await
{
Ok(_row) => Ok(()),
Err(_) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn get(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
) -> Result<(uuid::Uuid, String, String, String, uuid::Uuid), sqlx::Error> {
match sqlx::query(
r#"
SELECT id, file_key, bucket, region, song_queue_id FROM "songQueueData"
WHERE id = $1
"#,
)
.bind(id)
.fetch_one(pool)
.await
{
Ok(row) => {
let file_key: String = row.try_get("file_key")?;
let bucket: String = row.try_get("bucket")?;
let region: String = row.try_get("region")?;
let song_queue_id: uuid::Uuid = row.try_get("song_queue_id")?;
Ok((*id, file_key, bucket, region, song_queue_id))
}
Err(err) => Err(err),
}
}
pub async fn get_with_song_queue_id(
pool: &sqlx::PgPool,
song_queue_id: &uuid::Uuid,
) -> Result<(uuid::Uuid, String, String, String, uuid::Uuid), sqlx::Error> {
match sqlx::query(
r#"
SELECT id, file_key, bucket, region, song_queue_id FROM "songQueueData"
WHERE song_queue_id = $1
"#,
)
.bind(song_queue_id)
.fetch_one(pool)
.await
{
Ok(row) => {
let id: uuid::Uuid = row.try_get("id")?;
let file_key: String = row.try_get("file_key")?;
let bucket: String = row.try_get("bucket")?;
let region: String = row.try_get("region")?;
Ok((id, file_key, bucket, region, *song_queue_id))
}
Err(err) => Err(err),
}
}
pub mod queue {
pub mod coverart {
use sqlx::Row;
pub async fn insert(
pool: &sqlx::PgPool,
file_key: &str,
bucket: &str,
region: &str,
coverart_queue_id: &uuid::Uuid,
) -> Result<uuid::Uuid, sqlx::Error> {
match sqlx::query(
r#"
INSERT INTO "coverartQueueData" (file_key, bucket, region, coverart_queue_id)
VALUES($1, $2, $3, $4) RETURNING id;
"#,
)
.bind(file_key)
.bind(bucket)
.bind(region)
.bind(coverart_queue_id)
.fetch_one(pool)
.await
{
Ok(row) => {
let id: uuid::Uuid = row.try_get("id")?;
Ok(id)
}
Err(_err) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn update_file_key(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
file_key: &str,
) -> Result<(), sqlx::Error> {
match sqlx::query(
r#"
UPDATE "coverartQueueData" SET file_key = $1 WHERE id = $2;
"#,
)
.bind(file_key)
.bind(id)
.execute(pool)
.await
{
Ok(_row) => Ok(()),
Err(_) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn get(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
) -> Result<(uuid::Uuid, String, String, String, uuid::Uuid), sqlx::Error> {
match sqlx::query(
r#"
SELECT id, file_key, bucket, region, coverart_queue_id FROM "coverartQueueData"
WHERE id = $1
"#,
)
.bind(id)
.fetch_one(pool)
.await
{
Ok(row) => {
let file_key: String = row.try_get("file_key")?;
let bucket: String = row.try_get("bucket")?;
let region: String = row.try_get("region")?;
let coverart_queue_id: uuid::Uuid = row.try_get("coverart_queue_id")?;
Ok((*id, file_key, bucket, region, coverart_queue_id))
}
Err(err) => Err(err),
}
}
pub async fn get_with_coverart_queue_id(
pool: &sqlx::PgPool,
coverart_queue_id: &uuid::Uuid,
) -> Result<(uuid::Uuid, String, String, String, uuid::Uuid), sqlx::Error> {
match sqlx::query(
r#"
SELECT id, file_key, bucket, region, coverart_queue_id FROM "coverartQueueData"
WHERE coverart_queue_id = $1
"#,
)
.bind(coverart_queue_id)
.fetch_one(pool)
.await
{
Ok(row) => {
let id: uuid::Uuid = row.try_get("id")?;
let file_key: String = row.try_get("file_key")?;
let bucket: String = row.try_get("bucket")?;
let region: String = row.try_get("region")?;
Ok((id, file_key, bucket, region, *coverart_queue_id))
}
Err(err) => Err(err),
}
}
}
}
+1
View File
@@ -1,3 +1,4 @@
pub mod coverart; pub mod coverart;
pub mod data;
pub mod metadata; pub mod metadata;
pub mod song; pub mod song;
+1 -51
View File
@@ -24,16 +24,14 @@ pub mod dbtype {
pub async fn insert( pub async fn insert(
pool: &sqlx::PgPool, pool: &sqlx::PgPool,
data: &Vec<u8>,
filename: &String, filename: &String,
status: &String, status: &String,
) -> Result<uuid::Uuid, sqlx::Error> { ) -> Result<uuid::Uuid, sqlx::Error> {
let result = sqlx::query( let result = sqlx::query(
r#" r#"
INSERT INTO "songQueue" (data, filename, status) VALUES($1, $2, $3) RETURNING id; INSERT INTO "songQueue" (filename, status) VALUES($1, $2) RETURNING id;
"#, "#,
) )
.bind(data)
.bind(filename) .bind(filename)
.bind(status) .bind(status)
.fetch_one(pool) .fetch_one(pool)
@@ -253,51 +251,3 @@ pub async fn get_song_queue(
Err(_err) => Err(sqlx::Error::RowNotFound), Err(_err) => Err(sqlx::Error::RowNotFound),
} }
} }
pub async fn wipe_data(pool: &sqlx::PgPool, id: &uuid::Uuid) -> Result<uuid::Uuid, sqlx::Error> {
let result = sqlx::query(
r#"
UPDATE "songQueue" SET data = NULL WHERE id = $1 RETURNING id;
"#,
)
.bind(id)
.fetch_one(pool)
.await
.map_err(|e| {
eprintln!("Error updating record: {e}");
});
match result {
Ok(row) => Ok(row
.try_get("id")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap()),
Err(_) => Err(sqlx::Error::RowNotFound),
}
}
pub async fn get_data(pool: &sqlx::PgPool, id: &uuid::Uuid) -> Result<Vec<u8>, sqlx::Error> {
let result = sqlx::query(
r#"
SELECT data FROM "songQueue"
WHERE id = $1;
"#,
)
.bind(id)
.fetch_one(pool)
.await
.map_err(|e| {
eprintln!("Error inserting: {e}");
});
match result {
Ok(row) => {
let data = row
.try_get("data")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap();
Ok(data)
}
Err(_err) => Err(sqlx::Error::RowNotFound),
}
}
+16 -3
View File
@@ -6,8 +6,8 @@ pub async fn insert(
) -> Result<(time::OffsetDateTime, uuid::Uuid), sqlx::Error> { ) -> Result<(time::OffsetDateTime, uuid::Uuid), sqlx::Error> {
let result = sqlx::query( let result = sqlx::query(
r#" r#"
INSERT INTO "song" (title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, filename, directory, user_id) INSERT INTO "song" (title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, filename, directory, file_key, user_id)
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING date_created, id; VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING date_created, id;
"# "#
) )
.bind(&song.title) .bind(&song.title)
@@ -24,6 +24,7 @@ pub async fn insert(
.bind(&song.audio_type) .bind(&song.audio_type)
.bind(&song.filename) .bind(&song.filename)
.bind(&song.directory) .bind(&song.directory)
.bind(&song.file_key)
.bind(song.user_id) .bind(song.user_id)
.fetch_one(pool) .fetch_one(pool)
.await .await
@@ -133,6 +134,10 @@ pub async fn get_song(
.try_get("directory") .try_get("directory")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(), .unwrap(),
file_key: row
.try_get("file_key")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(),
date_created: Some(date_created_time), date_created: Some(date_created_time),
user_id: row user_id: row
.try_get("user_id") .try_get("user_id")
@@ -228,6 +233,10 @@ pub async fn get_all_songs(pool: &sqlx::PgPool) -> Result<Vec<simodels::song::So
.try_get("directory") .try_get("directory")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(), .unwrap(),
file_key: row
.try_get("file_key")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(),
date_created: Some(date_created_time), date_created: Some(date_created_time),
user_id: row user_id: row
.try_get("user_id") .try_get("user_id")
@@ -254,7 +263,7 @@ pub async fn delete_song(
r#" r#"
DELETE FROM "song" DELETE FROM "song"
WHERE id = $1 WHERE id = $1
RETURNING id, title, artist, album, album_artist, genre, year, disc, track, track_count, disc_count, duration, audio_type, date_created, filename, directory, user_id RETURNING id, title, artist, album, album_artist, genre, year, disc, track, track_count, disc_count, duration, audio_type, date_created, filename, directory, file_key, user_id
"#, "#,
) )
.bind(id) .bind(id)
@@ -332,6 +341,10 @@ pub async fn delete_song(
.try_get("directory") .try_get("directory")
.map_err(|_e| sqlx::Error::RowNotFound) .map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(), .unwrap(),
file_key: row
.try_get("file_key")
.map_err(|_e| sqlx::Error::RowNotFound)
.unwrap(),
date_created: Some(date_created_time), date_created: Some(date_created_time),
user_id: row user_id: row
.try_get("user_id") .try_get("user_id")
+17
View File
@@ -0,0 +1,17 @@
pub mod maze {
pub fn get_config() -> labyrinth::config::Config {
let s3_endpoint_url = sienvy::environment::get_env("S3_ENDPOINT_URL");
let bucket_name = sienvy::environment::get_env("S3_BUCKET_NAME");
let region = sienvy::environment::get_env("GARAGE_S3_REGION");
let access_key_id = sienvy::environment::get_env("GARAGE_DEFAULT_ACCESS_KEY");
let secret_key = sienvy::environment::get_env("GARAGE_DEFAULT_SECRET_KEY");
labyrinth::config::Config {
url: s3_endpoint_url.value,
bucket: bucket_name.value,
region: region.value,
access_key_id: access_key_id.value,
secret_key: secret_key.value,
}
}
}
+18 -2
View File
@@ -6,10 +6,17 @@ CREATE TABLE IF NOT EXISTS "songQueue" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
filename TEXT NOT NULL, filename TEXT NOT NULL,
status TEXT CHECK (status IN ('pending', 'ready', 'processing', 'done')), status TEXT CHECK (status IN ('pending', 'ready', 'processing', 'done')),
data BYTEA NULL,
user_id UUID NULL user_id UUID NULL
); );
CREATE TABLE IF NOT EXISTS "songQueueData" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
file_key TEXT NOT NULL,
bucket TEXT NOT NULL,
region TEXT NOT NULL,
song_queue_id UUID NOT NULL
);
-- Table to store queued metadata -- Table to store queued metadata
CREATE TABLE IF NOT EXISTS "metadataQueue" ( CREATE TABLE IF NOT EXISTS "metadataQueue" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -21,11 +28,18 @@ CREATE TABLE IF NOT EXISTS "metadataQueue" (
-- Table to store queued coverart -- Table to store queued coverart
CREATE TABLE IF NOT EXISTS "coverartQueue" ( CREATE TABLE IF NOT EXISTS "coverartQueue" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
data BYTEA NULL,
file_type TEXT NOT NULL, file_type TEXT NOT NULL,
song_queue_id UUID NULL song_queue_id UUID NULL
); );
CREATE TABLE IF NOT EXISTS "coverartQueueData" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
file_key TEXT NOT NULL,
bucket TEXT NOT NULL,
region TEXT NOT NULL,
coverart_queue_id UUID NOT NULL
);
-- Create an index for better query performance -- Create an index for better query performance
CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata); CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata);
@@ -47,6 +61,7 @@ CREATE TABLE IF NOT EXISTS "song" (
date_created timestamptz DEFAULT now(), date_created timestamptz DEFAULT now(),
filename TEXT NOT NULL, filename TEXT NOT NULL,
directory TEXT NOT NULL, directory TEXT NOT NULL,
file_key TEXT NOT NULL,
user_id UUID NULL user_id UUID NULL
); );
@@ -57,5 +72,6 @@ CREATE TABLE IF NOT EXISTS "coverart" (
directory TEXT NOT NULL, directory TEXT NOT NULL,
filename TEXT NOT NULL, filename TEXT NOT NULL,
file_type TEXT NOT NULL, file_type TEXT NOT NULL,
file_key TEXT NOT NULL,
song_id UUID NOT NULL song_id UUID NOT NULL
); );
@@ -1,6 +1,11 @@
-- Add migration script here -- Add migration script here
INSERT INTO "song" (id, title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, date_created, filename, directory, user_id) VALUES('44cf7940-34ff-489f-9124-d0ec90a55af9', 'Hypocrite Like The Rest', 'Kuoth', 'Kuoth', 'I', 'Alternative Hip-Hop', 2020, 1, 1, 9, 1, 139, 'flac', '2020-01-01 13:00:00-05', 'track01.flac', 'tests/I', '47491f9b-725a-4ba4-b9a5-711e1be46670'); INSERT INTO "song" (id, title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, date_created, filename, directory, file_key, user_id)
INSERT INTO "coverart" (id, title, directory, filename, file_type, song_id) VALUES('996122cd-5ae9-4013-9934-60768d3006ed', 'I', 'tests/I', 'Coverart-1.jpg', 'jpeg', '44cf7940-34ff-489f-9124-d0ec90a55af9'); VALUES('44cf7940-34ff-489f-9124-d0ec90a55af9', 'Hypocrite Like The Rest', 'Kuoth', 'Kuoth', 'I', 'Alternative Hip-Hop', 2020, 1, 1, 9, 1, 139, 'flac', '2020-01-01 13:00:00-05', 'track01.flac', 'tests/I', 'processed/song/track01.flac', '47491f9b-725a-4ba4-b9a5-711e1be46670');
INSERT INTO "song" (id, title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, date_created, filename, directory, user_id) VALUES('94cf7940-34ff-489f-9124-d0ec90a55af4', 'It''s Too Late', 'Kuoth', 'Kuoth', 'I', 'Alternative Hip-Hop', 2020, 2, 1, 9, 1, 116, 'flac', '2020-01-01 13:01:00-05', 'track02.flac', 'tests/I', '47491f9b-725a-4ba4-b9a5-711e1be46670'); INSERT INTO "coverart" (id, title, directory, filename, file_type, file_key, song_id)
INSERT INTO "coverart" (id, title, directory, filename, file_type, song_id) VALUES('d96122cd-5ae9-4013-9934-60768d3006e9', 'I', 'tests/I', 'Coverart-2.jpg', 'jpeg', '94cf7940-34ff-489f-9124-d0ec90a55af4'); VALUES('996122cd-5ae9-4013-9934-60768d3006ed', 'I', 'tests/I', 'Coverart-1.jpg', 'jpeg', 'processed/coverart/Coverart-1.jpg', '44cf7940-34ff-489f-9124-d0ec90a55af9');
INSERT INTO "song" (id, title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, date_created, filename, directory, file_key, user_id)
VALUES('94cf7940-34ff-489f-9124-d0ec90a55af4', 'It''s Too Late', 'Kuoth', 'Kuoth', 'I', 'Alternative Hip-Hop', 2020, 2, 1, 9, 1, 116, 'flac', '2020-01-01 13:01:00-05', 'track02.flac', 'tests/I', 'processed/song/track02.flac', '47491f9b-725a-4ba4-b9a5-711e1be46670');
INSERT INTO "coverart" (id, title, directory, filename, file_type, file_key, song_id)
VALUES('d96122cd-5ae9-4013-9934-60768d3006e9', 'I', 'tests/I', 'Coverart-2.jpg', 'jpeg', 'processed/coverart/Coverart-2.jpg', '94cf7940-34ff-489f-9124-d0ec90a55af4');