diff --git a/.github/WORKFLOWS.md b/.github/WORKFLOWS.md new file mode 100644 index 0000000..3cbca2b --- /dev/null +++ b/.github/WORKFLOWS.md @@ -0,0 +1,201 @@ +# GitHub Actions Workflows Documentation + +This document describes the CI/CD workflows configured for the Teal project. + +## Overview + +The project uses GitHub Actions for continuous integration, deployment, and security scanning. The workflows are designed to handle a polyglot codebase with Rust services, Node.js packages, and a React Native application. + +## Workflows + +### 🔧 CI (`ci.yml`) + +**Triggers:** Push/PR to `main` or `develop` branches + +**Purpose:** Primary continuous integration workflow that runs tests, linting, and type checking. + +**Jobs:** +- **rust-check**: Formats, lints (clippy), and tests all Rust code in both `services/` and `apps/` +- **node-check**: Type checking, linting, building, and testing Node.js packages +- **lexicon-check**: Validates lexicon files and ensures generated code is up to date + +**Key Features:** +- Caches Rust and Node.js dependencies for faster builds +- Runs in parallel for optimal performance +- Fails fast if any check fails + +### 🚀 Aqua (`aqua.yml`) + +**Triggers:** Push/PR to `main` with changes to `apps/aqua/**` + +**Purpose:** Builds and pushes the Aqua Rust application Docker image. + +**Features:** +- Multi-platform builds (linux/amd64, linux/arm64) +- Pushes to GitHub Container Registry (ghcr.io) +- Only pushes on main branch (not PRs) +- Uses GitHub Actions cache for Docker layers + +### 🤖 Cadet (`cadet.yml`) + +**Triggers:** Push/PR to `main` with changes to `services/cadet/**` + +**Purpose:** Builds and pushes the Cadet Rust service Docker image. + +**Features:** +- Multi-platform builds (linux/amd64, linux/arm64) +- Pushes to GitHub Container Registry (ghcr.io) +- Only pushes on main branch (not PRs) +- Uses GitHub Actions cache for Docker layers + +### 🔮 Amethyst (`amethyst.yml`) + +**Triggers:** Push/PR to `main` with changes to `apps/amethyst/**` + +**Purpose:** Builds the React Native/Expo application for different platforms. + +**Jobs:** +- **build-web**: Builds web version and uploads artifacts +- **build-ios**: Builds iOS version (only on main branch pushes, requires macOS runner) +- **lint-and-test**: Type checking and testing + +**Features:** +- Generates lexicons before building +- Platform-specific builds +- Artifact uploads for build assets + +### 🛠️ Services (`services.yml`) + +**Triggers:** Push/PR to `main` with changes to `services/**` + +**Purpose:** Dynamically detects and builds all services with Dockerfiles. + +**Jobs:** +- **detect-services**: Scans for services with Dockerfiles +- **build-service**: Matrix build for each detected service +- **test-services**: Runs tests for all services + +**Features:** +- Dynamic service detection +- Skips special directories (target, migrations, types, .sqlx) +- Per-service Docker caching +- Multi-platform builds + +### 🎉 Release (`release.yml`) + +**Triggers:** +- Push to tags matching `v*` +- Manual workflow dispatch + +**Purpose:** Creates GitHub releases and builds production Docker images. + +**Jobs:** +- **create-release**: Creates GitHub release with changelog +- **build-and-release-aqua**: Builds and tags Aqua for release +- **build-and-release-cadet**: Builds and tags Cadet for release +- **release-other-services**: Builds other services (rocketman, satellite) +- **build-and-release-amethyst**: Builds Amethyst and uploads to release + +**Features:** +- Automatic changelog extraction +- Production Docker tags (latest + version) +- Release artifact uploads +- Support for pre-releases (tags with `-`) + +### 🔒 Security (`security.yml`) + +**Triggers:** +- Push/PR to `main` or `develop` +- Daily at 2 AM UTC +- Manual dispatch + +**Purpose:** Comprehensive security scanning and vulnerability detection. + +**Jobs:** +- **rust-security-audit**: Uses `cargo audit` for Rust dependencies +- **node-security-audit**: Uses `pnpm audit` for Node.js dependencies +- **codeql-analysis**: GitHub's semantic code analysis +- **docker-security-scan**: Trivy vulnerability scanning for Docker images +- **secrets-scan**: TruffleHog for secrets detection + +**Features:** +- Fails on high/critical vulnerabilities +- SARIF upload for security tab integration +- Historical scanning with git history + +## Configuration Files + +### Dependabot (`dependabot.yml`) + +Automated dependency updates for: +- **npm**: Weekly updates for Node.js dependencies +- **cargo**: Weekly updates for Rust dependencies (services + apps) +- **github-actions**: Weekly updates for workflow actions +- **docker**: Weekly updates for Docker base images + +**Schedule:** Monday-Tuesday mornings, staggered to avoid conflicts + +## Container Registry + +All Docker images are pushed to GitHub Container Registry: +- `ghcr.io/[owner]/[repo]/aqua` +- `ghcr.io/[owner]/[repo]/cadet` +- `ghcr.io/[owner]/[repo]/[service-name]` + +**Tags:** +- `latest`: Latest build from main branch +- `sha-[commit]`: Specific commit builds +- `v[version]`: Release builds +- `pr-[number]`: Pull request builds (for testing) + +## Secrets and Permissions + +**Required secrets:** +- `GITHUB_TOKEN`: Automatically provided (for registry access and releases) + +**Permissions used:** +- `contents: read`: Read repository contents +- `packages: write`: Push to GitHub Container Registry +- `security-events: write`: Upload security scan results +- `actions: read`: Access workflow information + +## Best Practices + +1. **Path-based triggers**: Workflows only run when relevant files change +2. **Caching**: Aggressive caching for Rust, Node.js, and Docker layers +3. **Multi-platform**: Docker images built for amd64 and arm64 +4. **Security-first**: Regular vulnerability scanning and secrets detection +5. **Fail-fast**: Early termination on critical issues +6. **Artifact preservation**: Build outputs stored for debugging/deployment + +## Usage Examples + +### Manual Release +```bash +# Tag and push for automatic release +git tag v1.0.0 +git push origin v1.0.0 + +# Or use workflow dispatch in GitHub UI +``` + +### Local Development +```bash +# Run the same checks locally +pnpm rust:fmt +pnpm rust:clippy +pnpm typecheck +pnpm test +``` + +### Debugging Failed Builds +1. Check the Actions tab for detailed logs +2. Download artifacts from successful builds +3. Use the same commands locally with cached dependencies + +## Maintenance + +- **Weekly**: Review Dependabot PRs +- **Monthly**: Update action versions if not auto-updated +- **Quarterly**: Review and update security scanning tools +- **As needed**: Add new services to release workflow matrix \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..83857bb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,96 @@ +version: 2 +updates: + # Enable version updates for npm (Node.js dependencies) + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 10 + assignees: + - "@me" + reviewers: + - "@me" + commit-message: + prefix: "deps" + include: "scope" + + # Enable version updates for Cargo (Rust dependencies) - services + - package-ecosystem: "cargo" + directory: "/services" + schedule: + interval: "weekly" + day: "monday" + time: "10:00" + open-pull-requests-limit: 5 + assignees: + - "@me" + reviewers: + - "@me" + commit-message: + prefix: "deps(rust)" + include: "scope" + + # Enable version updates for Cargo (Rust dependencies) - aqua app + - package-ecosystem: "cargo" + directory: "/apps/aqua" + schedule: + interval: "weekly" + day: "monday" + time: "10:30" + open-pull-requests-limit: 5 + assignees: + - "@me" + reviewers: + - "@me" + commit-message: + prefix: "deps(rust)" + include: "scope" + + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "11:00" + open-pull-requests-limit: 5 + assignees: + - "@me" + reviewers: + - "@me" + commit-message: + prefix: "deps(actions)" + include: "scope" + + # Enable version updates for Docker + - package-ecosystem: "docker" + directory: "/apps/aqua" + schedule: + interval: "weekly" + day: "tuesday" + time: "09:00" + open-pull-requests-limit: 3 + assignees: + - "@me" + reviewers: + - "@me" + commit-message: + prefix: "deps(docker)" + include: "scope" + + - package-ecosystem: "docker" + directory: "/services/cadet" + schedule: + interval: "weekly" + day: "tuesday" + time: "09:30" + open-pull-requests-limit: 3 + assignees: + - "@me" + reviewers: + - "@me" + commit-message: + prefix: "deps(docker)" + include: "scope" diff --git a/.github/workflows/amethyst.yml b/.github/workflows/amethyst.yml new file mode 100644 index 0000000..333c4a6 --- /dev/null +++ b/.github/workflows/amethyst.yml @@ -0,0 +1,128 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Build Amethyst + +on: + push: + branches: [main] + paths: + - "apps/amethyst/**" + - "packages/**" + - "lexicons/**" + - "package.json" + - "pnpm-lock.yaml" + - ".github/workflows/amethyst.yml" + pull_request: + branches: [main] + paths: + - "apps/amethyst/**" + - "packages/**" + - "lexicons/**" + - "package.json" + - "pnpm-lock.yaml" + - ".github/workflows/amethyst.yml" + +jobs: + build-web: + name: Build Web + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate lexicons + run: pnpm lex:gen-server + + - name: Build web + run: | + cd apps/amethyst + pnpm build:web + + - name: Upload web build artifacts + uses: actions/upload-artifact@v4 + with: + name: amethyst-web-build + path: apps/amethyst/build/ + retention-days: 7 + + build-ios: + name: Build iOS + runs-on: macos-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate lexicons + run: pnpm lex:gen-server + + - name: Setup Expo CLI + run: npm install -g @expo/cli + + - name: Build iOS + run: | + cd apps/amethyst + pnpm build:ios + + - name: Upload iOS build artifacts + uses: actions/upload-artifact@v4 + with: + name: amethyst-ios-build + path: apps/amethyst/build/ + retention-days: 7 + + lint-and-test: + name: Lint and Test + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate lexicons + run: pnpm lex:gen-server + + - name: Type check + run: | + cd apps/amethyst + npx tsc --noEmit + + - name: Run tests + run: | + cd apps/amethyst + pnpm test --watchAll=false diff --git a/.github/workflows/appview.yml b/.github/workflows/aqua.yml similarity index 91% rename from .github/workflows/appview.yml rename to .github/workflows/aqua.yml index 31c9c1b..4e1b6f9 100644 --- a/.github/workflows/appview.yml +++ b/.github/workflows/aqua.yml @@ -1,15 +1,19 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + name: Build and Push Aqua on: push: - branches: [main] + branches: + - main paths: - "apps/aqua/**" - "Cargo.toml" - "Cargo.lock" - ".github/workflows/aqua.yml" pull_request: - branches: [main] + branches: + - main paths: - "apps/aqua/**" - "Cargo.toml" @@ -17,7 +21,7 @@ on: - ".github/workflows/aqua.yml" env: - REGISTRY: ghcr.io + REGISTRY: ghcr.iosyste IMAGE_NAME: ${{ github.repository }}/aqua jobs: diff --git a/.github/workflows/cadet.yml b/.github/workflows/cadet.yml index 8366e7f..332adfd 100644 --- a/.github/workflows/cadet.yml +++ b/.github/workflows/cadet.yml @@ -1,20 +1,22 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + name: Build and Push Cadet on: push: - branches: [ main ] + branches: [main] paths: - - 'services/cadet/**' - - 'Cargo.toml' - - 'Cargo.lock' - - '.github/workflows/cadet.yml' + - "services/cadet/**" + - "Cargo.toml" + - "Cargo.lock" + - ".github/workflows/cadet.yml" pull_request: - branches: [ main ] + branches: [main] paths: - - 'services/cadet/**' - - 'Cargo.toml' - - 'Cargo.lock' - - '.github/workflows/cadet.yml' + - "services/cadet/**" + - "Cargo.toml" + - "Cargo.lock" + - ".github/workflows/cadet.yml" env: REGISTRY: ghcr.io @@ -28,39 +30,39 @@ jobs: packages: write steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - name: Log in to Container Registry - if: github.event_name != 'pull_request' - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + - name: Log in to Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=sha,prefix=sha- - type=raw,value=latest,enable={{is_default_branch}} + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix=sha- + type=raw,value=latest,enable={{is_default_branch}} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - - name: Build and push Docker image - uses: docker/build-push-action@v5 - with: - context: ./services/cadet - file: ./services/cadet/Dockerfile - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./services/cadet + file: ./services/cadet/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c116561 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,128 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +env: + CARGO_TERM_COLOR: always + SQLX_OFFLINE: true + +jobs: + rust-check: + name: Rust Check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + services + apps/aqua + + - name: Check Rust formatting (services) + run: | + cd services + cargo fmt --all -- --check + + - name: Check Rust formatting (apps) + run: | + for dir in apps/*/; do + if [ -f "$dir/Cargo.toml" ]; then + echo "Checking formatting for $dir" + cd "$dir" + cargo fmt --all -- --check + cd ../.. + fi + done + + - name: Run Clippy (services) + run: | + cd services + cargo clippy --all-targets --all-features -- -D warnings + + - name: Run Clippy (apps) + run: | + for dir in apps/*/; do + if [ -f "$dir/Cargo.toml" ]; then + echo "Running clippy for $dir" + cd "$dir" + cargo clippy --all-targets --all-features -- -D warnings + cd ../.. + fi + done + + - name: Run Rust tests (services) + run: | + cd services + cargo test --all-features + + node-check: + name: Node.js Check + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Type check + run: pnpm typecheck + + - name: Lint and format check + run: pnpm fix --check + + - name: Build packages + run: pnpm build + + - name: Run tests + run: pnpm test + + lexicon-check: + name: Lexicon Validation + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Validate lexicons + run: pnpm lex:validate + + - name: Check lexicon generation + run: | + pnpm lex:gen-server + git diff --exit-code || (echo "Lexicon files are out of sync. Run 'pnpm lex:gen-server' locally." && exit 1) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..98f42d9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,283 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + tag: + description: "Release tag" + required: true + type: string + +env: + REGISTRY: ghcr.io + CARGO_TERM_COLOR: always + SQLX_OFFLINE: true + +jobs: + create-release: + name: Create Release + runs-on: ubuntu-latest + outputs: + release_id: ${{ steps.create_release.outputs.id }} + upload_url: ${{ steps.create_release.outputs.upload_url }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get tag name + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT + else + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + fi + + - name: Generate changelog + id: changelog + run: | + if [ -f "CHANGELOG.md" ]; then + # Extract changelog for this version + awk '/^## \[${{ steps.tag.outputs.tag }}\]/{flag=1; next} /^## \[/{flag=0} flag' CHANGELOG.md > release_notes.md + else + echo "Release ${{ steps.tag.outputs.tag }}" > release_notes.md + fi + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.tag.outputs.tag }} + release_name: Release ${{ steps.tag.outputs.tag }} + body_path: release_notes.md + draft: false + prerelease: ${{ contains(steps.tag.outputs.tag, '-') }} + + build-and-release-aqua: + name: Release Aqua + runs-on: ubuntu-latest + needs: create-release + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get tag name + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT + else + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + fi + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository }}/aqua + tags: | + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.tag }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./apps/aqua + file: ./apps/aqua/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + build-and-release-cadet: + name: Release Cadet + runs-on: ubuntu-latest + needs: create-release + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get tag name + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT + else + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + fi + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository }}/cadet + tags: | + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.tag }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./services/cadet + file: ./services/cadet/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + release-other-services: + name: Release Other Services + runs-on: ubuntu-latest + needs: create-release + permissions: + contents: read + packages: write + strategy: + matrix: + service: [rocketman, satellite] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Check if service has Dockerfile + id: check + run: | + if [ -f "services/${{ matrix.service }}/Dockerfile" ]; then + echo "has_dockerfile=true" >> $GITHUB_OUTPUT + else + echo "has_dockerfile=false" >> $GITHUB_OUTPUT + fi + + - name: Log in to Container Registry + if: steps.check.outputs.has_dockerfile == 'true' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get tag name + if: steps.check.outputs.has_dockerfile == 'true' + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT + else + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + fi + + - name: Extract metadata + if: steps.check.outputs.has_dockerfile == 'true' + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.service }} + tags: | + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.tag }} + + - name: Set up Docker Buildx + if: steps.check.outputs.has_dockerfile == 'true' + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + if: steps.check.outputs.has_dockerfile == 'true' + uses: docker/build-push-action@v5 + with: + context: ./services/${{ matrix.service }} + file: ./services/${{ matrix.service }}/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=${{ matrix.service }} + cache-to: type=gha,mode=max,scope=${{ matrix.service }} + + build-and-release-amethyst: + name: Release Amethyst + runs-on: ubuntu-latest + needs: create-release + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate lexicons + run: pnpm lex:gen-server + + - name: Build for all platforms + run: | + cd apps/amethyst + pnpm build + + - name: Create build archive + run: | + cd apps/amethyst + tar -czf amethyst-build.tar.gz build/ + + - name: Get tag name + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT + else + echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + fi + + - name: Upload Amethyst build to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} + asset_path: ./apps/amethyst/amethyst-build.tar.gz + asset_name: amethyst-${{ steps.tag.outputs.tag }}.tar.gz + asset_content_type: application/gzip diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..8bdf63c --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,180 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Security + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + schedule: + # Run security checks daily at 2 AM sunday + - cron: "0 2 * * 0" + +jobs: + rust-security-audit: + name: Rust Security Audit + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Install cargo-audit + run: cargo install cargo-audit + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + services + apps/aqua + + - name: Run cargo audit (services) + run: | + cd services + cargo audit + + - name: Run cargo audit (apps) + run: | + for dir in apps/*/; do + if [ -f "$dir/Cargo.toml" ]; then + echo "Running security audit for $dir" + cd "$dir" + cargo audit + cd ../.. + fi + done + + node-security-audit: + name: Node.js Security Audit + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run npm audit + run: pnpm audit --prod + + - name: Check for known vulnerabilities + run: | + # Run audit and capture output + pnpm audit --json > audit-results.json || true + + # Check if there are any high or critical vulnerabilities + if jq '.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical")' audit-results.json | grep -q .; then + echo "High or critical vulnerabilities found!" + jq '.vulnerabilities | to_entries[] | select(.value.severity == "high" or .value.severity == "critical")' audit-results.json + exit 1 + else + echo "No high or critical vulnerabilities found." + fi + + codeql-analysis: + name: CodeQL Analysis + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: ["javascript", "typescript"] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-extended,security-and-quality + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" + + docker-security-scan: + name: Docker Security Scan + runs-on: ubuntu-latest + if: github.event_name == 'push' || github.event_name == 'schedule' + strategy: + matrix: + service: [aqua, cadet] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.service == 'aqua' && './apps/aqua' || './services/cadet' }} + file: ${{ matrix.service == 'aqua' && './apps/aqua/Dockerfile' || './services/cadet/Dockerfile' }} + load: true + tags: ${{ matrix.service }}:latest + cache-from: type=gha,scope=${{ matrix.service }} + cache-to: type=gha,mode=max,scope=${{ matrix.service }} + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: "${{ matrix.service }}:latest" + format: "sarif" + output: "trivy-results-${{ matrix.service }}.sarif" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: "trivy-results-${{ matrix.service }}.sarif" + + secrets-scan: + name: Secrets Scan + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run TruffleHog OSS + uses: trufflesecurity/trufflehog@main + with: + path: ./ + base: main + head: HEAD + extra_args: --debug --only-verified diff --git a/.github/workflows/services.yml b/.github/workflows/services.yml new file mode 100644 index 0000000..aa44ec5 --- /dev/null +++ b/.github/workflows/services.yml @@ -0,0 +1,124 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + +name: Build and Push Services + +on: + push: + branches: [main] + paths: + - "services/**" + - "Cargo.toml" + - "Cargo.lock" + - ".github/workflows/services.yml" + pull_request: + branches: [main] + paths: + - "services/**" + - "Cargo.toml" + - "Cargo.lock" + - ".github/workflows/services.yml" + +env: + REGISTRY: ghcr.io + CARGO_TERM_COLOR: always + SQLX_OFFLINE: true + +jobs: + detect-services: + name: Detect Services to Build + runs-on: ubuntu-latest + outputs: + services: ${{ steps.detect.outputs.services }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Detect services with Dockerfiles + id: detect + run: | + services=() + for service_dir in services/*/; do + service_name=$(basename "$service_dir") + # Skip special directories + if [[ "$service_name" == "target" || "$service_name" == "migrations" || "$service_name" == "types" || "$service_name" == ".sqlx" ]]; then + continue + fi + # Check if service has a Dockerfile + if [[ -f "$service_dir/Dockerfile" ]]; then + services+=("$service_name") + fi + done + + # Convert to JSON array + services_json=$(printf '%s\n' "${services[@]}" | jq -R . | jq -s .) + echo "services=$services_json" >> $GITHUB_OUTPUT + echo "Detected services: $services_json" + + build-service: + name: Build ${{ matrix.service }} + runs-on: ubuntu-latest + needs: detect-services + if: needs.detect-services.outputs.services != '[]' + strategy: + matrix: + service: ${{ fromJson(needs.detect-services.outputs.services) }} + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository }}/${{ matrix.service }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix=sha- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./services/${{ matrix.service }} + file: ./services/${{ matrix.service }}/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=${{ matrix.service }} + cache-to: type=gha,mode=max,scope=${{ matrix.service }} + + test-services: + name: Test Services + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: services + + - name: Run service tests + run: | + cd services + cargo test --all-features --workspace diff --git a/.sqlx/query-00b655145e9033d951628a8bc69521815b3af632d0433f87d78c5403dd22eb75.json b/.sqlx/query-00b655145e9033d951628a8bc69521815b3af632d0433f87d78c5403dd22eb75.json new file mode 100644 index 0000000..bcda051 --- /dev/null +++ b/.sqlx/query-00b655145e9033d951628a8bc69521815b3af632d0433f87d78c5403dd22eb75.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT DISTINCT\n ae1.name as synthetic_name,\n ae2.name as target_name,\n similarity(LOWER(TRIM(ae1.name)), LOWER(TRIM(ae2.name))) as similarity_score,\n COUNT(ptae1.play_uri) as synthetic_plays,\n COUNT(ptae2.play_uri) as target_plays\n FROM artists_extended ae1\n CROSS JOIN artists_extended ae2\n LEFT JOIN play_to_artists_extended ptae1 ON ae1.id = ptae1.artist_id\n LEFT JOIN play_to_artists_extended ptae2 ON ae2.id = ptae2.artist_id\n WHERE ae1.id != ae2.id\n AND ae1.mbid_type = 'synthetic'\n AND ae2.mbid_type = 'musicbrainz'\n AND similarity(LOWER(TRIM(ae1.name)), LOWER(TRIM(ae2.name))) >= $1\n GROUP BY ae1.id, ae1.name, ae2.id, ae2.name, similarity_score\n ORDER BY similarity_score DESC\n LIMIT 10\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "synthetic_name", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "target_name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "similarity_score", + "type_info": "Float4" + }, + { + "ordinal": 3, + "name": "synthetic_plays", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "target_plays", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Float4" + ] + }, + "nullable": [ + false, + false, + null, + null, + null + ] + }, + "hash": "00b655145e9033d951628a8bc69521815b3af632d0433f87d78c5403dd22eb75" +} diff --git a/.sqlx/query-0d7c3ef80c20dac6efd0fe3c430d7f41b1c90368ff99ce8a09f66bca63864d1e.json b/.sqlx/query-0d7c3ef80c20dac6efd0fe3c430d7f41b1c90368ff99ce8a09f66bca63864d1e.json new file mode 100644 index 0000000..baf82a1 --- /dev/null +++ b/.sqlx/query-0d7c3ef80c20dac6efd0fe3c430d7f41b1c90368ff99ce8a09f66bca63864d1e.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "REFRESH MATERIALIZED VIEW mv_release_play_counts;", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "0d7c3ef80c20dac6efd0fe3c430d7f41b1c90368ff99ce8a09f66bca63864d1e" +} diff --git a/.sqlx/query-0e053ba402c8b769b697f60d189675eceb89f1d14e52174bda67dc65cc68d273.json b/.sqlx/query-0e053ba402c8b769b697f60d189675eceb89f1d14e52174bda67dc65cc68d273.json new file mode 100644 index 0000000..8bda3ba --- /dev/null +++ b/.sqlx/query-0e053ba402c8b769b697f60d189675eceb89f1d14e52174bda67dc65cc68d273.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n pta.artist_mbid as mbid,\n pta.artist_name as name,\n COUNT(*) as play_count\n FROM plays p\n INNER JOIN play_to_artists pta ON p.uri = pta.play_uri\n WHERE p.did = $1\n AND pta.artist_mbid IS NOT NULL\n AND pta.artist_name IS NOT NULL\n GROUP BY pta.artist_mbid, pta.artist_name\n ORDER BY play_count DESC\n LIMIT $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mbid", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "play_count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + true, + null + ] + }, + "hash": "0e053ba402c8b769b697f60d189675eceb89f1d14e52174bda67dc65cc68d273" +} diff --git a/.sqlx/query-0f62d18dcac06b6da3fc90e2206af0fc21e46e42ce1402750f9cc4dd08b54cec.json b/.sqlx/query-0f62d18dcac06b6da3fc90e2206af0fc21e46e42ce1402750f9cc4dd08b54cec.json new file mode 100644 index 0000000..54d12e0 --- /dev/null +++ b/.sqlx/query-0f62d18dcac06b6da3fc90e2206af0fc21e46e42ce1402750f9cc4dd08b54cec.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM artists_extended WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [] + }, + "hash": "0f62d18dcac06b6da3fc90e2206af0fc21e46e42ce1402750f9cc4dd08b54cec" +} diff --git a/.sqlx/query-0ff59e15ce4faa50bb4b9996ae7877681060ed462a7905012f8097c9545f60b1.json b/.sqlx/query-0ff59e15ce4faa50bb4b9996ae7877681060ed462a7905012f8097c9545f60b1.json new file mode 100644 index 0000000..de2e1a2 --- /dev/null +++ b/.sqlx/query-0ff59e15ce4faa50bb4b9996ae7877681060ed462a7905012f8097c9545f60b1.json @@ -0,0 +1,112 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n uri, did, rkey, cid, isrc, duration, track_name, played_time, processed_time,\n release_mbid, release_name, recording_mbid, submission_client_agent,\n music_service_base_domain, origin_url,\n COALESCE(\n json_agg(\n json_build_object(\n 'artist_mbid', pta.artist_mbid,\n 'artist_name', pta.artist_name\n )\n ) FILTER (WHERE pta.artist_name IS NOT NULL),\n '[]'\n ) AS artists\n FROM plays\n LEFT JOIN play_to_artists as pta ON uri = pta.play_uri\n WHERE did = ANY($1)\n GROUP BY uri, did, rkey, cid, isrc, duration, track_name, played_time, processed_time,\n release_mbid, release_name, recording_mbid, submission_client_agent,\n music_service_base_domain, origin_url\n ORDER BY processed_time desc\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "uri", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "did", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "rkey", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "cid", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "isrc", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "duration", + "type_info": "Int4" + }, + { + "ordinal": 6, + "name": "track_name", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "played_time", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "processed_time", + "type_info": "Timestamptz" + }, + { + "ordinal": 9, + "name": "release_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 10, + "name": "release_name", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "recording_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 12, + "name": "submission_client_agent", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "music_service_base_domain", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "origin_url", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "artists", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "TextArray" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + true, + true, + true, + true, + true, + true, + true, + true, + null + ] + }, + "hash": "0ff59e15ce4faa50bb4b9996ae7877681060ed462a7905012f8097c9545f60b1" +} diff --git a/.sqlx/query-193ac753fc587fa24887d8be61eea86f74de6a1a8d4546304fb023532dfaefe7.json b/.sqlx/query-193ac753fc587fa24887d8be61eea86f74de6a1a8d4546304fb023532dfaefe7.json new file mode 100644 index 0000000..ea24896 --- /dev/null +++ b/.sqlx/query-193ac753fc587fa24887d8be61eea86f74de6a1a8d4546304fb023532dfaefe7.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT extract_discriminant($1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "extract_discriminant", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "193ac753fc587fa24887d8be61eea86f74de6a1a8d4546304fb023532dfaefe7" +} diff --git a/.sqlx/query-1d35c8cf83ad859a8c50986ef1f587fb7f9aef2067feccd8af89d3b03d88020c.json b/.sqlx/query-1d35c8cf83ad859a8c50986ef1f587fb7f9aef2067feccd8af89d3b03d88020c.json new file mode 100644 index 0000000..a88dee9 --- /dev/null +++ b/.sqlx/query-1d35c8cf83ad859a8c50986ef1f587fb7f9aef2067feccd8af89d3b03d88020c.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM releases WHERE mbid = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "1d35c8cf83ad859a8c50986ef1f587fb7f9aef2067feccd8af89d3b03d88020c" +} diff --git a/.sqlx/query-1e4e6b89ac28b1b6cb21c9fbab8f22348943b3f27e9ba9642785d33129f98363.json b/.sqlx/query-1e4e6b89ac28b1b6cb21c9fbab8f22348943b3f27e9ba9642785d33129f98363.json new file mode 100644 index 0000000..fcfeb74 --- /dev/null +++ b/.sqlx/query-1e4e6b89ac28b1b6cb21c9fbab8f22348943b3f27e9ba9642785d33129f98363.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM play_to_artists WHERE play_uri = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "1e4e6b89ac28b1b6cb21c9fbab8f22348943b3f27e9ba9642785d33129f98363" +} diff --git a/.sqlx/query-28b1d571a1d045115bcae785b2583f7bf6d02b0b19946b322192dd7f62748d4e.json b/.sqlx/query-28b1d571a1d045115bcae785b2583f7bf6d02b0b19946b322192dd7f62748d4e.json new file mode 100644 index 0000000..931d694 --- /dev/null +++ b/.sqlx/query-28b1d571a1d045115bcae785b2583f7bf6d02b0b19946b322192dd7f62748d4e.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT extract_edition_discriminant($1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "extract_edition_discriminant", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "28b1d571a1d045115bcae785b2583f7bf6d02b0b19946b322192dd7f62748d4e" +} diff --git a/.sqlx/query-2bdfb2ec8d91cffc761dc72be1a4f540e6cc918a9f7941bfdbefbea6f3dee149.json b/.sqlx/query-2bdfb2ec8d91cffc761dc72be1a4f540e6cc918a9f7941bfdbefbea6f3dee149.json new file mode 100644 index 0000000..d2f96bb --- /dev/null +++ b/.sqlx/query-2bdfb2ec8d91cffc761dc72be1a4f540e6cc918a9f7941bfdbefbea6f3dee149.json @@ -0,0 +1,52 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT DISTINCT\n r1.mbid as release1_mbid,\n r1.name as release1_name,\n r2.mbid as release2_mbid,\n r2.name as release2_name,\n similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) as similarity_score,\n COUNT(DISTINCT ptae1.artist_id) as shared_artists\n FROM releases r1\n CROSS JOIN releases r2\n INNER JOIN plays p1 ON p1.release_mbid = r1.mbid\n INNER JOIN plays p2 ON p2.release_mbid = r2.mbid\n INNER JOIN play_to_artists_extended ptae1 ON p1.uri = ptae1.play_uri\n INNER JOIN play_to_artists_extended ptae2 ON p2.uri = ptae2.play_uri\n WHERE r1.mbid != r2.mbid\n AND similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) >= $1\n AND ptae1.artist_id = ptae2.artist_id -- Same artist\n AND (\n (r1.discriminant IS NULL AND r2.discriminant IS NULL) OR\n (LOWER(TRIM(COALESCE(r1.discriminant, ''))) = LOWER(TRIM(COALESCE(r2.discriminant, ''))))\n ) -- Same or no discriminants\n GROUP BY r1.mbid, r1.name, r2.mbid, r2.name, similarity_score\n HAVING COUNT(DISTINCT ptae1.artist_id) > 0 -- At least one shared artist\n ORDER BY similarity_score DESC, shared_artists DESC\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "release1_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "release1_name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "release2_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 3, + "name": "release2_name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "similarity_score", + "type_info": "Float4" + }, + { + "ordinal": 5, + "name": "shared_artists", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Float4" + ] + }, + "nullable": [ + false, + false, + false, + false, + null, + null + ] + }, + "hash": "2bdfb2ec8d91cffc761dc72be1a4f540e6cc918a9f7941bfdbefbea6f3dee149" +} diff --git a/.sqlx/query-2c2f9db90b7465147a6a696a628e2542d51c42844162455230e702a87719588a.json b/.sqlx/query-2c2f9db90b7465147a6a696a628e2542d51c42844162455230e702a87719588a.json new file mode 100644 index 0000000..eedcdcd --- /dev/null +++ b/.sqlx/query-2c2f9db90b7465147a6a696a628e2542d51c42844162455230e702a87719588a.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM play_to_artists_extended WHERE artist_id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [] + }, + "hash": "2c2f9db90b7465147a6a696a628e2542d51c42844162455230e702a87719588a" +} diff --git a/.sqlx/query-3d84a9e1ed05846bc931eea9b90fd88cae8b636968af4bd2f9b1a9927d15379d.json b/.sqlx/query-3d84a9e1ed05846bc931eea9b90fd88cae8b636968af4bd2f9b1a9927d15379d.json new file mode 100644 index 0000000..988047e --- /dev/null +++ b/.sqlx/query-3d84a9e1ed05846bc931eea9b90fd88cae8b636968af4bd2f9b1a9927d15379d.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "REFRESH MATERIALIZED VIEW mv_global_play_count;", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "3d84a9e1ed05846bc931eea9b90fd88cae8b636968af4bd2f9b1a9927d15379d" +} diff --git a/.sqlx/query-413d8c111e295ddda68a47f38f6b9df88d4b45b149288caba54c339742a718a0.json b/.sqlx/query-413d8c111e295ddda68a47f38f6b9df88d4b45b149288caba54c339742a718a0.json new file mode 100644 index 0000000..6f26304 --- /dev/null +++ b/.sqlx/query-413d8c111e295ddda68a47f38f6b9df88d4b45b149288caba54c339742a718a0.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) FROM plays WHERE recording_mbid = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "413d8c111e295ddda68a47f38f6b9df88d4b45b149288caba54c339742a718a0" +} diff --git a/.sqlx/query-5095c5a6b62d018f95c5c1f58c274b9682f33d918ab02d4d78963fa9ca9c07d1.json b/.sqlx/query-5095c5a6b62d018f95c5c1f58c274b9682f33d918ab02d4d78963fa9ca9c07d1.json new file mode 100644 index 0000000..b6ffb6e --- /dev/null +++ b/.sqlx/query-5095c5a6b62d018f95c5c1f58c274b9682f33d918ab02d4d78963fa9ca9c07d1.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM profiles WHERE did = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "5095c5a6b62d018f95c5c1f58c274b9682f33d918ab02d4d78963fa9ca9c07d1" +} diff --git a/.sqlx/query-651c94b4edd5afa55c3679a5f8c1ef1cbe53f7dac01b050ec7ad9100950527c0.json b/.sqlx/query-651c94b4edd5afa55c3679a5f8c1ef1cbe53f7dac01b050ec7ad9100950527c0.json new file mode 100644 index 0000000..f0b773f --- /dev/null +++ b/.sqlx/query-651c94b4edd5afa55c3679a5f8c1ef1cbe53f7dac01b050ec7ad9100950527c0.json @@ -0,0 +1,112 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n uri, did, rkey, cid, isrc, duration, track_name, played_time, processed_time,\n release_mbid, release_name, recording_mbid, submission_client_agent,\n music_service_base_domain, origin_url,\n COALESCE(\n json_agg(\n json_build_object(\n 'artist_mbid', pta.artist_mbid,\n 'artist_name', pta.artist_name\n )\n ) FILTER (WHERE pta.artist_name IS NOT NULL),\n '[]'\n ) AS artists\n FROM plays\n LEFT JOIN play_to_artists as pta ON uri = pta.play_uri\n WHERE uri = $1\n GROUP BY uri, did, rkey, cid, isrc, duration, track_name, played_time, processed_time,\n release_mbid, release_name, recording_mbid, submission_client_agent,\n music_service_base_domain, origin_url\n ORDER BY processed_time desc\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "uri", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "did", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "rkey", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "cid", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "isrc", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "duration", + "type_info": "Int4" + }, + { + "ordinal": 6, + "name": "track_name", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "played_time", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "processed_time", + "type_info": "Timestamptz" + }, + { + "ordinal": 9, + "name": "release_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 10, + "name": "release_name", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "recording_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 12, + "name": "submission_client_agent", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "music_service_base_domain", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "origin_url", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "artists", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + true, + true, + true, + true, + true, + true, + true, + true, + null + ] + }, + "hash": "651c94b4edd5afa55c3679a5f8c1ef1cbe53f7dac01b050ec7ad9100950527c0" +} diff --git a/.sqlx/query-6b1a3660fc7e391293278d11020b1f37ddf7446cbc73931c8e30ee38c2f3ae48.json b/.sqlx/query-6b1a3660fc7e391293278d11020b1f37ddf7446cbc73931c8e30ee38c2f3ae48.json new file mode 100644 index 0000000..da5d1a4 --- /dev/null +++ b/.sqlx/query-6b1a3660fc7e391293278d11020b1f37ddf7446cbc73931c8e30ee38c2f3ae48.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE play_to_artists_extended\n SET artist_id = $1, artist_name = $2\n WHERE artist_id = $3\n AND NOT EXISTS (\n SELECT 1 FROM play_to_artists_extended existing\n WHERE existing.play_uri = play_to_artists_extended.play_uri\n AND existing.artist_id = $1\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Text", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "6b1a3660fc7e391293278d11020b1f37ddf7446cbc73931c8e30ee38c2f3ae48" +} diff --git a/.sqlx/query-6fec79345247c090a72f32d06cb53290156d41f49abba3a9280bc2bedc1c9c56.json b/.sqlx/query-6fec79345247c090a72f32d06cb53290156d41f49abba3a9280bc2bedc1c9c56.json new file mode 100644 index 0000000..d16fffe --- /dev/null +++ b/.sqlx/query-6fec79345247c090a72f32d06cb53290156d41f49abba3a9280bc2bedc1c9c56.json @@ -0,0 +1,52 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT DISTINCT\n ae1.id as synthetic_id,\n ae1.name as synthetic_name,\n ae2.id as target_id,\n ae2.name as target_name,\n ae2.mbid as target_mbid,\n similarity(LOWER(TRIM(ae1.name)), LOWER(TRIM(ae2.name))) as similarity_score\n FROM artists_extended ae1\n CROSS JOIN artists_extended ae2\n WHERE ae1.id != ae2.id\n AND ae1.mbid_type = 'synthetic'\n AND ae2.mbid_type = 'musicbrainz'\n AND similarity(LOWER(TRIM(ae1.name)), LOWER(TRIM(ae2.name))) >= $1\n ORDER BY similarity_score DESC\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "synthetic_id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "synthetic_name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "target_id", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "target_name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "target_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 5, + "name": "similarity_score", + "type_info": "Float4" + } + ], + "parameters": { + "Left": [ + "Float4" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + null + ] + }, + "hash": "6fec79345247c090a72f32d06cb53290156d41f49abba3a9280bc2bedc1c9c56" +} diff --git a/.sqlx/query-76c4d9600293bb80c2a6009b2b823ba85b02f77442ce3a783643e89676fef9a0.json b/.sqlx/query-76c4d9600293bb80c2a6009b2b823ba85b02f77442ce3a783643e89676fef9a0.json new file mode 100644 index 0000000..aeec34e --- /dev/null +++ b/.sqlx/query-76c4d9600293bb80c2a6009b2b823ba85b02f77442ce3a783643e89676fef9a0.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO artists_extended (mbid, name, mbid_type) VALUES ($1, $2, 'musicbrainz')\n ON CONFLICT (mbid) DO UPDATE SET\n name = EXCLUDED.name,\n updated_at = NOW()\n RETURNING id;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "76c4d9600293bb80c2a6009b2b823ba85b02f77442ce3a783643e89676fef9a0" +} diff --git a/.sqlx/query-7cdcd5e8ecada65d351a38c38cfda64ad3d9f04982181dbb32bde93ebd5adc85.json b/.sqlx/query-7cdcd5e8ecada65d351a38c38cfda64ad3d9f04982181dbb32bde93ebd5adc85.json new file mode 100644 index 0000000..9e9e687 --- /dev/null +++ b/.sqlx/query-7cdcd5e8ecada65d351a38c38cfda64ad3d9f04982181dbb32bde93ebd5adc85.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO plays (\n uri, cid, did, rkey, isrc, duration, track_name, played_time,\n processed_time, release_mbid, release_name, recording_mbid,\n submission_client_agent, music_service_base_domain, artist_names_raw,\n track_discriminant, release_discriminant\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8,\n NOW(), $9, $10, $11, $12, $13, $14, $15, $16\n ) ON CONFLICT(uri) DO UPDATE SET\n isrc = EXCLUDED.isrc,\n duration = EXCLUDED.duration,\n track_name = EXCLUDED.track_name,\n played_time = EXCLUDED.played_time,\n processed_time = EXCLUDED.processed_time,\n release_mbid = EXCLUDED.release_mbid,\n release_name = EXCLUDED.release_name,\n recording_mbid = EXCLUDED.recording_mbid,\n submission_client_agent = EXCLUDED.submission_client_agent,\n music_service_base_domain = EXCLUDED.music_service_base_domain,\n artist_names_raw = EXCLUDED.artist_names_raw,\n track_discriminant = EXCLUDED.track_discriminant,\n release_discriminant = EXCLUDED.release_discriminant;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text", + "Text", + "Int4", + "Text", + "Timestamptz", + "Uuid", + "Text", + "Uuid", + "Text", + "Text", + "Jsonb", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "7cdcd5e8ecada65d351a38c38cfda64ad3d9f04982181dbb32bde93ebd5adc85" +} diff --git a/.sqlx/query-7cfece6879feb2653c647d1248913c9cf54bd02a20e9694c7f6d7e92f28f8d10.json b/.sqlx/query-7cfece6879feb2653c647d1248913c9cf54bd02a20e9694c7f6d7e92f28f8d10.json new file mode 100644 index 0000000..91e717c --- /dev/null +++ b/.sqlx/query-7cfece6879feb2653c647d1248913c9cf54bd02a20e9694c7f6d7e92f28f8d10.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE plays SET release_mbid = $1, release_name = $2 WHERE release_mbid = $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "7cfece6879feb2653c647d1248913c9cf54bd02a20e9694c7f6d7e92f28f8d10" +} diff --git a/.sqlx/query-8758f5bb57feedca6cd65831f36aabc811e8b7072dc6bdbfd4a49242e5d7c946.json b/.sqlx/query-8758f5bb57feedca6cd65831f36aabc811e8b7072dc6bdbfd4a49242e5d7c946.json new file mode 100644 index 0000000..4918337 --- /dev/null +++ b/.sqlx/query-8758f5bb57feedca6cd65831f36aabc811e8b7072dc6bdbfd4a49242e5d7c946.json @@ -0,0 +1,18 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO statii (uri, did, rkey, cid, record)\n VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (uri) DO UPDATE SET\n cid = EXCLUDED.cid,\n record = EXCLUDED.record,\n indexed_at = NOW();\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "8758f5bb57feedca6cd65831f36aabc811e8b7072dc6bdbfd4a49242e5d7c946" +} diff --git a/.sqlx/query-97e98ede9b32adab5e1ad9808ae827387eba7ad376fba8e41217862a76179f59.json b/.sqlx/query-97e98ede9b32adab5e1ad9808ae827387eba7ad376fba8e41217862a76179f59.json new file mode 100644 index 0000000..13f5253 --- /dev/null +++ b/.sqlx/query-97e98ede9b32adab5e1ad9808ae827387eba7ad376fba8e41217862a76179f59.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n p.release_mbid as mbid,\n p.release_name as name,\n COUNT(*) as play_count\n FROM plays p\n WHERE p.release_mbid IS NOT NULL\n AND p.release_name IS NOT NULL\n GROUP BY p.release_mbid, p.release_name\n ORDER BY play_count DESC\n LIMIT $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mbid", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "play_count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + true, + true, + null + ] + }, + "hash": "97e98ede9b32adab5e1ad9808ae827387eba7ad376fba8e41217862a76179f59" +} diff --git a/.sqlx/query-9af33e4329198dee7814519573b63858eaf69f08ad2959d96ffee5c8387af0ba.json b/.sqlx/query-9af33e4329198dee7814519573b63858eaf69f08ad2959d96ffee5c8387af0ba.json new file mode 100644 index 0000000..2a6442c --- /dev/null +++ b/.sqlx/query-9af33e4329198dee7814519573b63858eaf69f08ad2959d96ffee5c8387af0ba.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "REFRESH MATERIALIZED VIEW mv_artist_play_counts;", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "9af33e4329198dee7814519573b63858eaf69f08ad2959d96ffee5c8387af0ba" +} diff --git a/.sqlx/query-9bac472357fa38a6e3bb38d02ebb56a6e11c85d4aff91096f8ea68f1196e8bd3.json b/.sqlx/query-9bac472357fa38a6e3bb38d02ebb56a6e11c85d4aff91096f8ea68f1196e8bd3.json new file mode 100644 index 0000000..c95bb16 --- /dev/null +++ b/.sqlx/query-9bac472357fa38a6e3bb38d02ebb56a6e11c85d4aff91096f8ea68f1196e8bd3.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO play_to_artists_extended (play_uri, artist_id, artist_name) VALUES\n ($1, $2, $3)\n ON CONFLICT (play_uri, artist_id) DO NOTHING;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Int4", + "Text" + ] + }, + "nullable": [] + }, + "hash": "9bac472357fa38a6e3bb38d02ebb56a6e11c85d4aff91096f8ea68f1196e8bd3" +} diff --git a/.sqlx/query-9c08de3ad1dd8e005e6cf15694ad1878203772969a3b280c3db4193631a98f81.json b/.sqlx/query-9c08de3ad1dd8e005e6cf15694ad1878203772969a3b280c3db4193631a98f81.json new file mode 100644 index 0000000..225ff81 --- /dev/null +++ b/.sqlx/query-9c08de3ad1dd8e005e6cf15694ad1878203772969a3b280c3db4193631a98f81.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO recordings (mbid, name, discriminant) VALUES ($1, $2, $3)\n ON CONFLICT (mbid) DO UPDATE SET\n name = EXCLUDED.name,\n discriminant = COALESCE(EXCLUDED.discriminant, recordings.discriminant)\n RETURNING mbid;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mbid", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "9c08de3ad1dd8e005e6cf15694ad1878203772969a3b280c3db4193631a98f81" +} diff --git a/.sqlx/query-9d4e872755f90087f64f116d8fee340218e09b40ab8f94b5d9d17b9c39bf3d4f.json b/.sqlx/query-9d4e872755f90087f64f116d8fee340218e09b40ab8f94b5d9d17b9c39bf3d4f.json new file mode 100644 index 0000000..52ab503 --- /dev/null +++ b/.sqlx/query-9d4e872755f90087f64f116d8fee340218e09b40ab8f94b5d9d17b9c39bf3d4f.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM plays WHERE uri = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "9d4e872755f90087f64f116d8fee340218e09b40ab8f94b5d9d17b9c39bf3d4f" +} diff --git a/.sqlx/query-ad02971766fb37f49f4a75a6414807606be0562574826f8fe88827c645c01acd.json b/.sqlx/query-ad02971766fb37f49f4a75a6414807606be0562574826f8fe88827c645c01acd.json new file mode 100644 index 0000000..1d1b065 --- /dev/null +++ b/.sqlx/query-ad02971766fb37f49f4a75a6414807606be0562574826f8fe88827c645c01acd.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT generate_synthetic_mbid($1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "generate_synthetic_mbid", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "ad02971766fb37f49f4a75a6414807606be0562574826f8fe88827c645c01acd" +} diff --git a/.sqlx/query-af5c1fdabaee1cbc49f89d1df92e13cbb4a0837e3c644de9c7cf8e33e170d2e3.json b/.sqlx/query-af5c1fdabaee1cbc49f89d1df92e13cbb4a0837e3c644de9c7cf8e33e170d2e3.json new file mode 100644 index 0000000..9c18178 --- /dev/null +++ b/.sqlx/query-af5c1fdabaee1cbc49f89d1df92e13cbb4a0837e3c644de9c7cf8e33e170d2e3.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n p.release_mbid as mbid,\n p.release_name as name,\n COUNT(*) as play_count\n FROM plays p\n WHERE p.did = $1\n AND p.release_mbid IS NOT NULL\n AND p.release_name IS NOT NULL\n GROUP BY p.release_mbid, p.release_name\n ORDER BY play_count DESC\n LIMIT $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mbid", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "play_count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + true, + true, + null + ] + }, + "hash": "af5c1fdabaee1cbc49f89d1df92e13cbb4a0837e3c644de9c7cf8e33e170d2e3" +} diff --git a/.sqlx/query-b0036bbbb21b71900394c33f4c1db6f8281159b68ca492f6977dc153c60ab453.json b/.sqlx/query-b0036bbbb21b71900394c33f4c1db6f8281159b68ca492f6977dc153c60ab453.json new file mode 100644 index 0000000..303f7b9 --- /dev/null +++ b/.sqlx/query-b0036bbbb21b71900394c33f4c1db6f8281159b68ca492f6977dc153c60ab453.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT DISTINCT\n r1.name as recording1_name,\n r2.name as recording2_name,\n similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) as similarity_score,\n COUNT(DISTINCT ptae1.artist_id) as shared_artists,\n STRING_AGG(DISTINCT ae.name, ', ') as artist_names\n FROM recordings r1\n CROSS JOIN recordings r2\n INNER JOIN plays p1 ON p1.recording_mbid = r1.mbid\n INNER JOIN plays p2 ON p2.recording_mbid = r2.mbid\n INNER JOIN play_to_artists_extended ptae1 ON p1.uri = ptae1.play_uri\n INNER JOIN play_to_artists_extended ptae2 ON p2.uri = ptae2.play_uri\n INNER JOIN artists_extended ae ON ptae1.artist_id = ae.id\n WHERE r1.mbid != r2.mbid\n AND similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) >= $1\n AND ptae1.artist_id = ptae2.artist_id\n GROUP BY r1.mbid, r1.name, r2.mbid, r2.name, similarity_score\n HAVING COUNT(DISTINCT ptae1.artist_id) > 0\n ORDER BY similarity_score DESC\n LIMIT 5\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "recording1_name", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "recording2_name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "similarity_score", + "type_info": "Float4" + }, + { + "ordinal": 3, + "name": "shared_artists", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "artist_names", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Float4" + ] + }, + "nullable": [ + false, + false, + null, + null, + null + ] + }, + "hash": "b0036bbbb21b71900394c33f4c1db6f8281159b68ca492f6977dc153c60ab453" +} diff --git a/.sqlx/query-b23dc54aec3e2bee85f1e5874df7ad4cbaeb15ca043b244bbce224dfc26d8b56.json b/.sqlx/query-b23dc54aec3e2bee85f1e5874df7ad4cbaeb15ca043b244bbce224dfc26d8b56.json new file mode 100644 index 0000000..ab5056f --- /dev/null +++ b/.sqlx/query-b23dc54aec3e2bee85f1e5874df7ad4cbaeb15ca043b244bbce224dfc26d8b56.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE artists_extended SET name = $1, updated_at = NOW() WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "b23dc54aec3e2bee85f1e5874df7ad4cbaeb15ca043b244bbce224dfc26d8b56" +} diff --git a/.sqlx/query-b4e829c20bb78b9db20eccd9827e0d2f7bdbeedbaa39f6b40d1ae8a1045d6837.json b/.sqlx/query-b4e829c20bb78b9db20eccd9827e0d2f7bdbeedbaa39f6b40d1ae8a1045d6837.json new file mode 100644 index 0000000..9b28718 --- /dev/null +++ b/.sqlx/query-b4e829c20bb78b9db20eccd9827e0d2f7bdbeedbaa39f6b40d1ae8a1045d6837.json @@ -0,0 +1,65 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n p.avatar,\n p.banner,\n p.created_at,\n p.description,\n p.description_facets,\n p.did,\n p.display_name,\n s.record as status\n FROM profiles p\n LEFT JOIN statii s ON p.did = s.did AND s.rkey = 'self'\n WHERE (p.did = ANY($1))\n OR (p.handle = ANY($2))", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "avatar", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "banner", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 3, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "description_facets", + "type_info": "Jsonb" + }, + { + "ordinal": 5, + "name": "did", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "display_name", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "status", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "TextArray", + "TextArray" + ] + }, + "nullable": [ + true, + true, + true, + true, + true, + false, + true, + true + ] + }, + "hash": "b4e829c20bb78b9db20eccd9827e0d2f7bdbeedbaa39f6b40d1ae8a1045d6837" +} diff --git a/.sqlx/query-b8bf07c21c04acf3b4d908b2db93643e497db9a1f01d4d51b99dfdbddd2d4c0e.json b/.sqlx/query-b8bf07c21c04acf3b4d908b2db93643e497db9a1f01d4d51b99dfdbddd2d4c0e.json new file mode 100644 index 0000000..7d29a7f --- /dev/null +++ b/.sqlx/query-b8bf07c21c04acf3b4d908b2db93643e497db9a1f01d4d51b99dfdbddd2d4c0e.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n pta.artist_mbid as mbid,\n pta.artist_name as name,\n COUNT(*) as play_count\n FROM plays p\n INNER JOIN play_to_artists pta ON p.uri = pta.play_uri\n WHERE pta.artist_mbid IS NOT NULL\n AND pta.artist_name IS NOT NULL\n GROUP BY pta.artist_mbid, pta.artist_name\n ORDER BY play_count DESC\n LIMIT $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mbid", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "play_count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + true, + null + ] + }, + "hash": "b8bf07c21c04acf3b4d908b2db93643e497db9a1f01d4d51b99dfdbddd2d4c0e" +} diff --git a/.sqlx/query-b9ca1a73cba5a29665e5f996fd33410054936bbd74cfd611767bf6a7893ebded.json b/.sqlx/query-b9ca1a73cba5a29665e5f996fd33410054936bbd74cfd611767bf6a7893ebded.json new file mode 100644 index 0000000..6bc4651 --- /dev/null +++ b/.sqlx/query-b9ca1a73cba5a29665e5f996fd33410054936bbd74cfd611767bf6a7893ebded.json @@ -0,0 +1,21 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO profiles (did, handle, display_name, description, description_facets, avatar, banner, created_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ON CONFLICT (did) DO UPDATE SET\n display_name = EXCLUDED.display_name,\n description = EXCLUDED.description,\n description_facets = EXCLUDED.description_facets,\n avatar = EXCLUDED.avatar,\n banner = EXCLUDED.banner,\n created_at = EXCLUDED.created_at;\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text", + "Jsonb", + "Text", + "Text", + "Timestamptz" + ] + }, + "nullable": [] + }, + "hash": "b9ca1a73cba5a29665e5f996fd33410054936bbd74cfd611767bf6a7893ebded" +} diff --git a/.sqlx/query-bbedc0ebf2ae8ecd086c089546f700e4c027150db583ae78ebba24da334c7224.json b/.sqlx/query-bbedc0ebf2ae8ecd086c089546f700e4c027150db583ae78ebba24da334c7224.json new file mode 100644 index 0000000..6c57fd5 --- /dev/null +++ b/.sqlx/query-bbedc0ebf2ae8ecd086c089546f700e4c027150db583ae78ebba24da334c7224.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) FROM plays WHERE release_mbid = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + null + ] + }, + "hash": "bbedc0ebf2ae8ecd086c089546f700e4c027150db583ae78ebba24da334c7224" +} diff --git a/.sqlx/query-bf9c6d3bf0f9594ae1c02dc85c9887b747aaa5f0c3e67d9381c3867c4f67ae6d.json b/.sqlx/query-bf9c6d3bf0f9594ae1c02dc85c9887b747aaa5f0c3e67d9381c3867c4f67ae6d.json new file mode 100644 index 0000000..d7a16b8 --- /dev/null +++ b/.sqlx/query-bf9c6d3bf0f9594ae1c02dc85c9887b747aaa5f0c3e67d9381c3867c4f67ae6d.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "REFRESH MATERIALIZED VIEW mv_recording_play_counts;", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "bf9c6d3bf0f9594ae1c02dc85c9887b747aaa5f0c3e67d9381c3867c4f67ae6d" +} diff --git a/.sqlx/query-cbc1d1c3cfe95d3d223ab4bb125e301436c9d6bbf09376215aa43e7abc98d87c.json b/.sqlx/query-cbc1d1c3cfe95d3d223ab4bb125e301436c9d6bbf09376215aa43e7abc98d87c.json new file mode 100644 index 0000000..0568db9 --- /dev/null +++ b/.sqlx/query-cbc1d1c3cfe95d3d223ab4bb125e301436c9d6bbf09376215aa43e7abc98d87c.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT DISTINCT\n r1.name as release1_name,\n r2.name as release2_name,\n similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) as similarity_score,\n COUNT(DISTINCT ptae1.artist_id) as shared_artists,\n STRING_AGG(DISTINCT ae.name, ', ') as artist_names\n FROM releases r1\n CROSS JOIN releases r2\n INNER JOIN plays p1 ON p1.release_mbid = r1.mbid\n INNER JOIN plays p2 ON p2.release_mbid = r2.mbid\n INNER JOIN play_to_artists_extended ptae1 ON p1.uri = ptae1.play_uri\n INNER JOIN play_to_artists_extended ptae2 ON p2.uri = ptae2.play_uri\n INNER JOIN artists_extended ae ON ptae1.artist_id = ae.id\n WHERE r1.mbid != r2.mbid\n AND similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) >= $1\n AND ptae1.artist_id = ptae2.artist_id\n GROUP BY r1.mbid, r1.name, r2.mbid, r2.name, similarity_score\n HAVING COUNT(DISTINCT ptae1.artist_id) > 0\n ORDER BY similarity_score DESC\n LIMIT 5\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "release1_name", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "release2_name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "similarity_score", + "type_info": "Float4" + }, + { + "ordinal": 3, + "name": "shared_artists", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "artist_names", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Float4" + ] + }, + "nullable": [ + false, + false, + null, + null, + null + ] + }, + "hash": "cbc1d1c3cfe95d3d223ab4bb125e301436c9d6bbf09376215aa43e7abc98d87c" +} diff --git a/.sqlx/query-cdd7488f49e0b81ab138afaf173030ef4c37d195aee42cc6e5e2c6638cb6f3b2.json b/.sqlx/query-cdd7488f49e0b81ab138afaf173030ef4c37d195aee42cc6e5e2c6638cb6f3b2.json new file mode 100644 index 0000000..77c5a71 --- /dev/null +++ b/.sqlx/query-cdd7488f49e0b81ab138afaf173030ef4c37d195aee42cc6e5e2c6638cb6f3b2.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE plays SET recording_mbid = $1 WHERE recording_mbid = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "cdd7488f49e0b81ab138afaf173030ef4c37d195aee42cc6e5e2c6638cb6f3b2" +} diff --git a/.sqlx/query-d5414741e228591937d2d3e743d0ed343ee2434cc86a8b726806959f024b7b45.json b/.sqlx/query-d5414741e228591937d2d3e743d0ed343ee2434cc86a8b726806959f024b7b45.json new file mode 100644 index 0000000..a36dc59 --- /dev/null +++ b/.sqlx/query-d5414741e228591937d2d3e743d0ed343ee2434cc86a8b726806959f024b7b45.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM recordings WHERE mbid = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "d5414741e228591937d2d3e743d0ed343ee2434cc86a8b726806959f024b7b45" +} diff --git a/.sqlx/query-d80a24e6b32f04c26d28823db4601960a926801000b5f37583c98ae168c7e961.json b/.sqlx/query-d80a24e6b32f04c26d28823db4601960a926801000b5f37583c98ae168c7e961.json new file mode 100644 index 0000000..9cfd7da --- /dev/null +++ b/.sqlx/query-d80a24e6b32f04c26d28823db4601960a926801000b5f37583c98ae168c7e961.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM statii WHERE uri = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "d80a24e6b32f04c26d28823db4601960a926801000b5f37583c98ae168c7e961" +} diff --git a/.sqlx/query-f224b252a34a67a71266caca5affc5022e74dc42496aef9e61cec0e86d80f9d0.json b/.sqlx/query-f224b252a34a67a71266caca5affc5022e74dc42496aef9e61cec0e86d80f9d0.json new file mode 100644 index 0000000..0dfa066 --- /dev/null +++ b/.sqlx/query-f224b252a34a67a71266caca5affc5022e74dc42496aef9e61cec0e86d80f9d0.json @@ -0,0 +1,112 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n uri, did, rkey, cid, isrc, duration, track_name, played_time, processed_time,\n release_mbid, release_name, recording_mbid, submission_client_agent,\n music_service_base_domain, origin_url,\n COALESCE(\n json_agg(\n json_build_object(\n 'artist_mbid', pta.artist_mbid,\n 'artist_name', pta.artist_name\n )\n ) FILTER (WHERE pta.artist_name IS NOT NULL),\n '[]'\n ) AS artists\n FROM plays p\n LEFT JOIN play_to_artists as pta ON p.uri = pta.play_uri\n GROUP BY uri, did, rkey, cid, isrc, duration, track_name, played_time, processed_time,\n release_mbid, release_name, recording_mbid, submission_client_agent,\n music_service_base_domain, origin_url\n ORDER BY processed_time DESC\n LIMIT $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "uri", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "did", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "rkey", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "cid", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "isrc", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "duration", + "type_info": "Int4" + }, + { + "ordinal": 6, + "name": "track_name", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "played_time", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "processed_time", + "type_info": "Timestamptz" + }, + { + "ordinal": 9, + "name": "release_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 10, + "name": "release_name", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "recording_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 12, + "name": "submission_client_agent", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "music_service_base_domain", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "origin_url", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "artists", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + true, + true, + true, + true, + true, + true, + true, + true, + null + ] + }, + "hash": "f224b252a34a67a71266caca5affc5022e74dc42496aef9e61cec0e86d80f9d0" +} diff --git a/.sqlx/query-f604394b9517a78f2dd81723bed6435b9c3a03922a50d86daa21bfb6d09ac734.json b/.sqlx/query-f604394b9517a78f2dd81723bed6435b9c3a03922a50d86daa21bfb6d09ac734.json new file mode 100644 index 0000000..bdda187 --- /dev/null +++ b/.sqlx/query-f604394b9517a78f2dd81723bed6435b9c3a03922a50d86daa21bfb6d09ac734.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO artists_extended (mbid, name, mbid_type) VALUES ($1, $2, 'synthetic')\n ON CONFLICT (mbid) DO UPDATE SET\n name = EXCLUDED.name,\n updated_at = NOW()\n RETURNING id;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "f604394b9517a78f2dd81723bed6435b9c3a03922a50d86daa21bfb6d09ac734" +} diff --git a/.sqlx/query-f8caa11009d6220e139157dff83a0d3ffb37fcd8590527a5d7d3fc6e2e8f3672.json b/.sqlx/query-f8caa11009d6220e139157dff83a0d3ffb37fcd8590527a5d7d3fc6e2e8f3672.json new file mode 100644 index 0000000..6bb7062 --- /dev/null +++ b/.sqlx/query-f8caa11009d6220e139157dff83a0d3ffb37fcd8590527a5d7d3fc6e2e8f3672.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO releases (mbid, name, discriminant) VALUES ($1, $2, $3)\n ON CONFLICT (mbid) DO UPDATE SET\n name = EXCLUDED.name,\n discriminant = COALESCE(EXCLUDED.discriminant, releases.discriminant)\n RETURNING mbid;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mbid", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "f8caa11009d6220e139157dff83a0d3ffb37fcd8590527a5d7d3fc6e2e8f3672" +} diff --git a/.sqlx/query-fd5f376dac5f38005efa3217c9614e377703c681e1510fc0c6539b1edee289b7.json b/.sqlx/query-fd5f376dac5f38005efa3217c9614e377703c681e1510fc0c6539b1edee289b7.json new file mode 100644 index 0000000..f9aba1b --- /dev/null +++ b/.sqlx/query-fd5f376dac5f38005efa3217c9614e377703c681e1510fc0c6539b1edee289b7.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n ae.id,\n ae.name\n FROM artists_extended ae\n WHERE ae.mbid_type = 'musicbrainz'\n AND (\n LOWER(TRIM(ae.name)) = $1\n OR LOWER(TRIM(ae.name)) LIKE '%' || $1 || '%'\n OR $1 LIKE '%' || LOWER(TRIM(ae.name)) || '%'\n OR similarity(LOWER(TRIM(ae.name)), $1) > 0.6\n )\n ORDER BY similarity(LOWER(TRIM(ae.name)), $1) DESC\n LIMIT 10\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "fd5f376dac5f38005efa3217c9614e377703c681e1510fc0c6539b1edee289b7" +} diff --git a/.sqlx/query-ffa27ada5f1ef0d5c699277b88ad33aa6576f6d14a12ad61974e77d52b42eea0.json b/.sqlx/query-ffa27ada5f1ef0d5c699277b88ad33aa6576f6d14a12ad61974e77d52b42eea0.json new file mode 100644 index 0000000..074bfdd --- /dev/null +++ b/.sqlx/query-ffa27ada5f1ef0d5c699277b88ad33aa6576f6d14a12ad61974e77d52b42eea0.json @@ -0,0 +1,52 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT DISTINCT\n r1.mbid as recording1_mbid,\n r1.name as recording1_name,\n r2.mbid as recording2_mbid,\n r2.name as recording2_name,\n similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) as similarity_score,\n COUNT(DISTINCT ptae1.artist_id) as shared_artists\n FROM recordings r1\n CROSS JOIN recordings r2\n INNER JOIN plays p1 ON p1.recording_mbid = r1.mbid\n INNER JOIN plays p2 ON p2.recording_mbid = r2.mbid\n INNER JOIN play_to_artists_extended ptae1 ON p1.uri = ptae1.play_uri\n INNER JOIN play_to_artists_extended ptae2 ON p2.uri = ptae2.play_uri\n WHERE r1.mbid != r2.mbid\n AND similarity(LOWER(TRIM(r1.name)), LOWER(TRIM(r2.name))) >= $1\n AND ptae1.artist_id = ptae2.artist_id -- Same artist\n AND (\n (r1.discriminant IS NULL AND r2.discriminant IS NULL) OR\n (LOWER(TRIM(COALESCE(r1.discriminant, ''))) = LOWER(TRIM(COALESCE(r2.discriminant, ''))))\n ) -- Same or no discriminants\n GROUP BY r1.mbid, r1.name, r2.mbid, r2.name, similarity_score\n HAVING COUNT(DISTINCT ptae1.artist_id) > 0 -- At least one shared artist\n ORDER BY similarity_score DESC, shared_artists DESC\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "recording1_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "recording1_name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "recording2_mbid", + "type_info": "Uuid" + }, + { + "ordinal": 3, + "name": "recording2_name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "similarity_score", + "type_info": "Float4" + }, + { + "ordinal": 5, + "name": "shared_artists", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Float4" + ] + }, + "nullable": [ + false, + false, + false, + false, + null, + null + ] + }, + "hash": "ffa27ada5f1ef0d5c699277b88ad33aa6576f6d14a12ad61974e77d52b42eea0" +} diff --git a/services/.sqlx/.sqlxrc b/services/.sqlx/.sqlxrc deleted file mode 100644 index f80edc4..0000000 --- a/services/.sqlx/.sqlxrc +++ /dev/null @@ -1,6 +0,0 @@ -[database] -url = "postgres://localhost/teal" -migrations = "./migrations" - -[compile_time_verification] -offline = false